最近一个叫 CorpusQA 的评测基准在圈子里讨论度很高,光看名字可能会以为又是一个“大海捞针”式的语料问答测试。但认真扒完设计思路以后,我的感受是:它想干的事,比“从超大规模语料里找答案”要狠得多——它要把 LLM 的“推理过程”本身按在台面上,一项一项查。
如果你平时在做 RAG 应用、长文本问答,或者正被“模型看起来啥都会,一上真实业务就露馅”的问题折磨,这个项目值得仔细看。它能帮你搞清楚一件事:模型到底是在调用知识、组合证据,还是在靠记忆和语感“猜”答案。这篇文章我不打算复述官方文档,而是结合我实际跑评测、调模型的经验,把这个基准的底层逻辑、实操方法和坑,一次讲透。
1. CorpusQA 到底在考什么:不是“考记忆”,是“考破案”
1.1 现有问答基准的尴尬:语料太小,推理太浅
先聊聊我为什么觉得现有评测不太好用。经典的开放域问答基准,比如很多年前流行的那些百科类数据集,问题基本都是“单跳”的——给定一篇文章,答案就藏在某一句话里。模型只需要做一件事:定位。这就像让一个侦探去翻一本只有一页纸的卷宗,找到“凶手是谁”这句话,就算破案了。这当然能测出模型的阅读理解能力,但说实话,跟真实世界里的问题差距太大了。
真实场景是什么样的?企业内部的知识库可能有几千万篇文档,法律文书、技术方案、历史邮件混在一起。用户问“我们去年采购的那批设备,后来因为合规问题被哪个部门叫停了”,这个问题至少涉及三条信息链:采购记录、合规审查记录、部门变更记录。它们分散在不同文档、不同时间点甚至不同系统导出的表格里。模型得先把这些线索捞出来,再串成一条完整的因果链,最后才能给出答案。
这就是现有基准和真实场景之间最大的裂缝:语料规模不够,推理深度不够。大多数公开基准的文档集可能只有几十万篇,而且问题设计得比较“友好”,往往是单文档或双文档就能解决。这就导致一个尴尬情况:模型在公开基准上分数很好看,一到企业级知识库就原形毕露。不是模型变笨了,而是评测根本没考到那个难度。
1.2 CorpusQA 的任务结构:把“证据链”作为核心单元
CorpusQA 这个名字拆开来看:Corpus(语料库)+ QA(问答)。它的核心设计是用“超大规模语料”作为推理的“背景板”,让模型在一个庞大的文档集里去解决需要多步推理的问题。我拿到的公开资料里,一个典型的配置是:语料库包含上亿篇文档、十几万道评测问题,平均每道题涉及的证据片段在 3 到 5 段左右。这个规模意味着,模型不可能靠“记住某篇文章”来作弊,它必须真的去检索、去筛选、去组合。
CorpusQA 把问题的“推理深度”作为第一优先级。问题类型包括但不限于:
- 跨文档多跳推理:需要从两篇以上文档中抽取信息,组合成新的结论。
- 时间线推理:同一事件在不同文档里有先后版本,需要判断“哪个发生在前面”。
- 反事实推理:给定条件变更,问“如果当初没发生某件事,结果会怎样”。
- 长尾知识推理:涉及冷门文档、低频实体,模型单靠训练记忆无法覆盖。
在这种设置下,答案本身反而没那么重要了,重要的是证据链。一道题的完整输出必须包含三样东西:最终答案、支撑答案的关键引文片段、从引文推导到答案的推理步骤。这就像侦探破案,不光要告诉委托人凶手是谁,还得拿出指纹报告、监控截图和作案时间线。如果你只给结论不给证据,那在裁判眼里就是不及格。
1.3 为什么要强调“超大规模语料”这个前提
有人可能会问:我把现有基准的文档集扩大十倍,不也一样吗?还真不一样。语料规模一旦跨过某个阈值,问题的性质就变了。当文档数量达到亿级时,检索环节的开销和噪声会急剧上升,模型必须在海量“干扰项”里找到那几条关键证据。这考验的不是单纯的理解能力,而是“如何在噪声中保持判断力”。
我自己的体会是,语料规模扩大的最大影响是检索召回质量的波动。在小语料里,top-5 基本能覆盖所有关键证据;但在超大语料里,top-5 经常只能捞到一两段有效信息,剩下的全是主题相关但答案无关的高相似度文本。这时候模型不能只依赖检索结果的第一屏,必须学会“证据不足时承认不足”,或者通过二次检索、多轮查证来补齐信息。这种能力在传统评测里几乎不会被考到,因为传统评测的检索压力太小了。
2. 评测设计里的“心机”:指标、防污染和检索解耦
2.1 评测指标:为什么只看准确率会被带偏
CorpusQA 给我印象最深的地方,是它的指标设计不再是“答案对就对,答案错就错”这种一刀切逻辑。官方定义了一套多维指标,我按自己的理解整理成一张表:
| 指标名称 | 计算方式 | 考察重点 |
|---|---|---|
| 答案准确率(EM / F1) | 预测答案与标准答案的精确匹配或词元重叠度 | 最终结论是否正确 |
| 引文召回率(Evidence Recall) | 预测引文覆盖了多少标准证据片段 | 检索和筛选能力是否到位 |
| 推理步正确率(Step Accuracy) | 每步推理结论与标准推理链是否一致 | 推理过程是否严谨,还是驴唇不对马嘴 |
| 证据充分度(Sufficiency) | 模型给出的证据是否足够支撑答案 | 是否存在“蒙对答案但理由全错”的情况 |
如果只盯着答案准确率,会漏掉很多重要信号。举个实际例子:我拿一个常规微调过的模型去跑,它的答案准确率有 68%,看起来还能接受。但一看证据召回率,只有 31%。这意味着大部分题目里,模型给了对的结论,却找不到支撑结论的证据。这种模型放到真实场景里很危险——它可能是在“背答案”或者“联想”,不是真正从语料里推出来。一旦换个问法,立刻崩给你看。
推理步正确率也很能说明问题。有些模型的答案是对的,但推理步骤里出现了“中间结论 A → 结论 B → 答案 C”这种链条,而标准证据里根本没有支持“中间结论 A”的引文。这说明模型的推理是事后编造的逻辑链,不是真实的证据推导。在业务场景里,这种模型经不起审计,更别说用在医疗、金融这种需要可解释性的领域了。
2.2 防数据污染:不是把测试题藏起来就完事
大模型评测绕不开的一个问题是数据污染。很多模型在训练时已经见过评测题的近似版本,分数虚高,实际能力远没到那个水平。CorpusQA 的做法比较务实,我观察到有三层防护机制:
第一是时间隔离。评测语料的构建时间被刻意标注,提问内容涉及的事件都发生在训练数据截止时间之后,让模型没法“凭记忆答题”。第二是问题版本轮换。同一道语义题会生成多个表面形式不同的版本,每次评测随机抽一部分,降低“背题”的概率。第三是私有子集机制。公开的评测集只是一部分,真正的完全私有子集只保留在评测方手里,外部模型接触不到。
我实际跑下来的感受是,时间隔离的效果最明显。当我用一个在超大规模语料上预训练过的模型去测,它对那些涉及近期事件的题目明显更“挣扎”,分数比老题低很多。这说明时间隔离确实能过滤掉一部分“记忆型选手”。而那些声称“全网无泄露”的模型,在私有子集上的表现通常比公开集掉分 10% 到 20%,这个差距就是污染的程度。
2.3 检索与推理解耦:到底是谁拖了后腿
CorpusQA 最让我欣赏的设计,是把评测拆成两个独立模式。
模式一:检索后推理(RAG 模式)。模型拿到一个给定文档集合(比如 top-100 篇候选文档),只有在这个集合内寻找证据并推理作答。模式二:端到端模式。模型自己从整个语料库中检索,再推理作答。这两种模式分开评测,价值极大。
我之所以说价值大,是因为它能精确地定位模型的短板。如果模型在模式一(给定文档)里分数很高,但在模式二(自行检索)里分数暴跌,那问题就出在检索环节——要么检索器召回太差,要么模型不懂得利用检索结果。相反,如果模式一分数就不行,说明模型的推理能力本身就有问题,换再强的检索器也白搭。这个“解耦诊断”的思路,我后来直接照搬到了自己的项目评估流程里,省去了很多互相甩锅的争吵。
3. 实操复现:从拿到数据到跑出指标,我踩过的路
3.1 环境准备与数据理解
要复现 CorpusQA 的评测,首先得有符合格式的数据。因为版权和隐私原因,官方不会把完整的亿级语料直接公开下载。但评测方法本身是透明的,一个可复用的做法是:构造一个小规模的“模拟版”来跑通流程。模拟版规模不用大,我本地用的是 5 万篇文档、2000 道题,结构和评测逻辑完全一致,跑起来只要一块中端显卡就能搞定。
数据集的格式大致长这样(JSON):
{ "question": "2024年城市新区规划中,哪家设计院最终获得了主干道改造项目?", "answer": "某省建筑设计研究院", "evidence_spans": [ {"doc_id": "doc_00234", "span_text": "主干道改造项目由某省建筑设计研究院中标"}, {"doc_id": "doc_05678", "span_text": "该院的设计方案提出双向八车道及智慧路灯系统"} ], "reasoning_steps": [ "从doc_00234得知中标单位是某省建筑设计研究院", "从doc_05678确认该院方案包含主干道改造内容", "综合两篇文档,中标单位即为最终负责项目设计的主体" ], "doc_pool": ["doc_00234", "doc_05678", "doc_11235", "..."] }这里最关键的是doc_pool字段。它不是把所有文档都给你,而是给一个候选池子(比如 100 篇)。这模拟了“检索器已经召回一部分候选文档”的场景,让评测能专注于推理这一环。如果你想测试端到端能力,就把候选池子换成整个语料库的索引。
3.2 最小评测脚本示例
我自己写评测脚本时,核心逻辑很直白:加载题目 → 调用模型生成回答 → 解析输出 → 算指标。下面是一个简化但能直接跑的版本,用的模型接口是 OpenAI 风格的,换成本地部署的也可以,只要接口兼容就行。
import json import re from collections import Counter def normalize_text(s): """去除标点、多余空格,小写化,让比较更稳""" s = s.lower() s = re.sub(r"[^\w\u4e00-\u9fff]+", "", s) # 保留中英文和数字 return s def compute_em(pred, gold): return int(normalize_text(pred) == normalize_text(gold)) def compute_f1(pred, gold): pred_tokens = list(normalize_text(pred)) gold_tokens = list(normalize_text(gold)) common = Counter(pred_tokens) & Counter(gold_tokens) num_same = sum(common.values()) if num_same == 0: return 0.0 precision = num_same / len(pred_tokens) if pred_tokens else 0.0 recall = num_same / len(gold_tokens) if gold_tokens else 0.0 return 2 * precision * recall / (precision + recall) def eval_sample(sample, model): # 构造提示词,要求模型严格输出 JSON prompt = f""" 请基于给定的候选文档,回答问题。你必须输出 JSON 格式,格式为: {{"answer": "...", "evidence_ids": ["doc_xxx", ...], "reasoning": "..."}} 问题:{sample['question']} 候选文档: {format_docs(sample['doc_pool'])} """ raw = model.generate(prompt) parsed = parse_json_response(raw) # 需要容错处理 return parsed这里有个坑我要特别提醒:模型不一定乖乖按 JSON 输出。我用过不少模型,经常出现“答案在 JSON 外又复述了一遍”“引用了不存在的 doc_id”这种情况。所以解析函数必须做得宽容一点:先用正则提取 JSON 块,如果提取失败,就退回去找“答案:”这种格式;如果连答案字段都没有,这题直接判错。宁可严格判错,也不能让脏输出干扰指标计算。
3.3 结果解读:分数要拆开看,不能只看总榜
评测跑完以后,输出结果不是简单一个总分,而是每道题、每个指标都有记录。我会习惯性做三件事。第一,看证据召回率的分布——如果很多题目的预测引文和标准引文重合率很低,直接回去看检索器或者候选池是怎么筛的。第二,抽样检查“答案对但证据错”的题目,看看是模型在胡编引文,还是引文边界切得不准。第三,把推理步正确率和答案准确率放在一起算差值,如果答案准确率很高、推理步正确率却很低,基本可以确定模型是在“投机取巧”。
我自己的对比测试记录了一个典型现象:同一模型在两个模式下的分数差异能到 20 分以上。模式一(给定文档)能考 82 分,模式二(端到端)只有 61 分。这个差距不是模型推理能力弱,而是它根本不擅长在海量文档里做精准检索。后来我给模型换了一个基于稠密检索的 RAG 组件,端到端分数立刻提到了 73 分。这个提升过程,用旧指标根本看不到,因为旧指标不区分检索和推理,想查都无从下手。
4. 踩坑记录:跑超大规模语料评测的几个深坑
4.1 上下文塞不下,模型“看漏”关键证据
第一个坑几乎人人会踩:模型上下文长度不够。虽然现在主流模型普遍支持 8K、32K 甚至更长上下文,但一份候选文档池如果有 100 篇文档,每篇平均 500 字,那单题输入就直奔 5 万字去了,远超模型的上限。更麻烦的是,模型在处理超长上下文时,注意力会衰减,放在中间的文档很容易被“看漏”,导致关键证据明明在输入里,模型却视而不见。
我的做法是先用一个轻量检索器把候选池压到 10 篇左右(每篇约 512 token),再拼接到上下文里。如果单篇太长,就做滑窗切片,只保留和问题关键词最相关的那一段。这一步非常关键,别嫌麻烦。根据我的实测,把候选池从 100 篇压到 10 篇,在问题答案准确率基本不变的情况下,显存占用下降了 40%,推理速度还快了一倍。核心原理很简单:大语料场景下,LLM 真正能用于推理的信息密度,取决于你喂给它的上下文质量,而不是数量。
4.2 模型“抄”原文不推理,引文和答案对不上
另一个很折磨人的问题是:模型直接引用原文片段当证据,但引文和最终答案之间根本没有推理关系。比如问题问“这个项目的延期是否导致了违约金赔偿”,模型可能引了一段描述项目进度的文字,然后答案却是“是,因为这属于不可抗力”。标准证据链里根本没有“不可抗力”这四个字,引文也没出现相关条款。这就是典型的“证据链路断裂”。
这个问题没有银弹解法,只能靠评测机制去压制。我在评测脚本里加了一个强制约束:要求模型在输出时把“推理步骤”里的每一步都关联到至少一个evidence_id。如果某一步推理找不到对应引文,就扣除该步的正确性分数。这样能在一定程度上逼着模型去认真对齐证据。当然,如果模型本身推理能力太弱,这一步收效甚微——建议直接回到模型侧优化,别在评测程序上死磕。
4.3 评测集污染导致分数虚高,换个私有子集就现形
第三坑是分数虚高。我在一次对比评测中发现,某个预训练模型在公开集上成绩惊艳,甚至超过了不少规模更大的模型。但我手头正好有一批内部构造的、时间戳更新的题目,把它放上去一测,分数直接掉了 25 个点。原因很明显:这个模型在训练阶段很可能见过公开集的类似内容,但对新时间线的事件根本没有泛化能力。
所以我现在的习惯是:任何重要评测,都至少留三道防线。第一,使用官方公开集评估基础能力。第二,人工构造 200 到 300 道私有题,加入时间隔离,测试泛化性。第三,在最终报告中同时注明“公开集分差和私有集分差”,两者一对比,有没有污染一目了然。这个方法不复杂,但能挡住绝大多数“刷榜模型”的障眼法。
5. 用 CorpusQA 指导模型优化:我摸索出的调优路线
5.1 先定位短板再动手,别上来就调参
跑完评测,第一件事不是急着换模型或者调 prompt,而是先回答一个问题:模型的短板在检索,在推理,还是在表达?
我建议按这个顺序排查:先看模式一(给定文档)的答案准确率和推理步正确率,如果这两个不高,说明模型本身的推理能力有问题,跟检索无关,直接进入模型微调或更换模型的环节。如果模式一很高、模式二(端到端)很低,那问题主要出在检索环节,应该去优化检索器的召回质量,而不是换模型。如果两个模式都接近满分,但证据召回率低,那大概率是模型的“引文抽取”能力弱,需要专门用引文生成数据做微调。
这个排查思路看起来朴素,但真的能省大量时间。我之前带过一个项目,成员一开始就花了两周调 prompt,把答案准确率从 62% 提到了 65%,但证据召回率纹丝不动。后来按上面的方法一拆,才发现检索器召回的 top-5 文档里有 4 篇是无关的,模型根本没机会拿到正确证据。换成更精准的稀疏-稠密混合检索后,证据召回率直接提高了 12 个点。
5.2 针对推理短板的微调:把思维链“喂”进去
如果经过排查,确认模型的推理步骤确实拉胯,那微调是一条绕不开的路。我实践下来,效果最好的方式是用 CorpusQA 标准答案里的推理链作为监督数据,做有监督微调(SFT)。做法是把每道题的输入(问题 + 候选文档)作为输入,期望输出设置成“推理链 + 最终答案”,然后用这些数据去微调模型。
需要留意的是,推理链的措辞不能太死板,否则模型会学成固定模板。我在微调时会对推理链做一定程度的改写,保留核心逻辑但变化表达方式。比如标准推理链写的是“从 doc_00234 得知中标单位是某省建筑设计研究院”,改写后变成“doc_00234 的招标公告显示,某省建筑设计研究院为第一中标候选人,因此该院是中标单位”。这样模型学到的是“怎么从证据中抽结论”这个能力,而不是背句子。
5.3 几条性价比很高的调优经验清单
最后分享几个我在实践中反复用到且确实好使的经验,直接抄作业就行:
- 候选文档数量适当增加对端到端分数有正收益。我把候选池从 5 篇增加到 10 篇,虽然推理速度慢了 15%,但端到端分数提升了 6 个点,因为模型拿到的证据更全了。关键是要给模型足够的时间“消化”多出来的文档,所以要注意 prompt 里的指令明确性。
- 对超长引文做摘要再输入。模型对一长段原文的注意力很容易分散,把引文先压缩成 2 到 3 句摘要,再作为上下文输入,推理步准确率有明显的提升。
- 在 prompt 里显式要求“证据不足时回答未知”。这一条能有效降低模型胡编乱造的概率,虽然会让答案准确率略降,但能大幅提升证据召回率,整体可信度反而更高。
- 横向对比不同模型时,保持 prompt 完全一致。模型对 prompt 措辞的敏感度差异很大,如果你在测模型 A 时用了更详细的 prompt,测模型 B 时用了简短版,那比分差距完全无法归因于模型能力。
5.4 一个值得关注的扩展方向
最近我在做的一个实验,是把 CorpusQA 的评测逻辑剥离出来,用在小样本的“领域教材包”测试上。思路很简单:拿一套专业教材(几百篇文档),人工做几十道需要跨章节推理的问题,然后让模型在这个小样本上跑一遍。这样做的成本极低,但能很快摸清一个模型在特定领域内是否真的具备“跨文档组合信息”的能力,而不是仅仅“熟读教材”。
这个小实验给我的最大启发是:评测的价值从来不在于一个排行榜名次,而在于它能不能让你看清模型的真实行为。如果你正在纠结自己的 RAG 应用“时好时坏”说不清原因,不妨试试用 CorpusQA 的思路,做一套最小可用的领域评测,把检索和推理拆开来看。我敢说,很多之前模糊的猜测,会在一张指标对比表里变得越来越清晰。这套方法论,比任何单一模型的能力提升都更值得花时间研究。