同样是多证据问答,一套 naive RAG 在单文档多证据题上题级全覆盖率是 95.0%,到跨文档题上只有 63.4%。
把指标拆到「(题, 文档)」这一层后,规律很清楚:一道题跨几份文档,就要把单份文档的成功率连乘几次。
跨 2 份文档时,
0.807^2 = 65.1%,实测 65.7%;跨 3 份文档时,0.807^3 = 52.5%,实测 50.0%。
RAG 评测里有一类题很容易被低估:答案不在一个知识块里,也不在一份文档里,而是要从多份文档各取一部分证据。
这类题看上去只是「多找几条证据」。但对检索系统来说,它更像连续过关。
只要其中一份文档的关键证据没有进上下文,题级答案就会残缺。缺一条证据,和缺三条证据,在 full_coverage 指标下都是失败。
这次实验要回答的问题是:
跨文档问答为什么会让 RAG 的 full coverage 掉得这么快?
评测对象
系统是一个标准两段式 naive RAG:
向量检索和 jieba FTS 混合召回 top-100。 用 jina-reranker 精排。 取 top-k chunk 塞进最终上下文。
语料来自 26 篇中文工业技术文档,主要是设备调试和操作手册类 Markdown。评测集里的每道 QA 都人工标注了答案证据,答案是否被检索到可以直接在检索层判断,不需要再让 LLM 做裁判。
这里的核心指标是 full_coverage:一道题标注的全部证据都进入最终上下文,才算成功。
两份评测集的边界如下:
medium_qa_60 | |||
cross_doc_qa |
在 RERANK_TOPK=16 下,结果差距很大:
full_coverage | ||
|---|---|---|
medium_qa_60 | ||
cross_doc_qa |
两份评测的检索链路、chunk 口径、top-k 设置一致。主要区别是:证据是否分散在多份文档里。
这里还有一个边界要先写清楚:cross_doc_qa 每题证据是 27 条,4 条。证据条数本身也会增加难度。后文拆出的 medium_qa_60 是 2p,包含了「跨文档」和「每份文档内更难挑全」这两部分影响。
失败样例
先看一类典型失败。
某道跨文档题同时问两代设备:旧型号比新型号多了哪些前置操作,以及两代设备进入调试模式的方式是否相同。下文的问题和文档名都已脱敏,只保留证据结构。
标准证据分布在两份文档里。k=16 的检索结果是:
新型号这半边齐了,旧型号这半边缺一条。这道题就失败。
这里的关键不是「旧型号文档完全没召回」。它已经进入召回池,也进入最终上下文,只是最终只拿到 1 个槽位,而这个槽位里的段落没有覆盖缺失证据。
失败发生在更细的位置:同一份来源文档里,reranker 没有把该给的段落全部挑出来。
文档粒度成功率
题级 full_coverage 太粗。它只能告诉我们这题过了还是没过,看不出是哪一份文档没过。
把指标拆到「(题, 文档)」粒度后,每一道跨文档题会变成 2~3 个小判定:
旧型号文档的证据是否全到手。 新型号文档的证据是否全到手。 如果还有第三份文档,再检查第三份。
定义一个新指标 p:对某个「(题, 文档)」,这份来源文档该给的证据是否全部进入最终上下文。
统计结果如下:
p | |||
|---|---|---|---|
medium_qa_60 | |||
cross_doc_qa |
到这一步,跨文档题的掉分机制就可以写成一个近似模型:
题级 full_coverage ≈ p^nn = 这道题跨几份文档拿 cross_doc_qa 里的 p=80.7% 去预测:
full_coverage | |||
|---|---|---|---|
0.807^2 = 65.1% | |||
0.807^3 = 52.5% |
误差都在 2.5 个百分点以内。
这个数字不能读成精确拟合。跨 3 份文档的子集只有 6 题,50.0% 对应的是 3/6。这里更有价值的是近似关系:题目跨越的文档数增加后,题级成功率会按文档粒度成功率连乘下降。
这个结果说明,跨文档难不只是证据更多。它把一次题级成功,拆成了多次文档级成功;每多跨一份文档,成功率就多乘一次。
两个因素
从 medium_qa_60 到 cross_doc_qa,有两个变化叠在一起。
第一,关卡变多。medium_qa_60 里 59/60 题只来自一份文档,基本只需要过一关;cross_doc_qa 要跨 2~3 份文档。
第二,每一关更难。单份来源文档的成功率从 95.1% 掉到 80.7%。
把这两个因素分开看:
p=95.1% | |
p=80.7% |
任何一个因素单独看,都解释不了 63.4%。跨文档问答真正麻烦的地方,是「每关更难」和「关数更多」相乘。
槽位浪费的误判
看到失败样例时,一个直觉诊断很自然:最终上下文里有很多槽位给了不相关文档,所以证据装不下。
在跨文档集上,这个数字确实不低。cross_doc_qa 的 656 个最终上下文槽位里,251 个落在非来源文档,占 38.3%。
但对照组推翻了这个解释。
p | |||
|---|---|---|---|
medium_qa_60 | |||
cross_doc_qa |
medium_qa_60 浪费的槽位更多,每条证据分到的来源文档槽位更少,但文档粒度成功率更高。
所以主要瓶颈不是上下文槽位总量,也不是非来源文档占比。
一个更合理的假说是:跨文档题的 query 往往包含多个子问题。一个 chunk 只回答子问题 A 时,拿整条复合 query 去打分,会显得只相关了一部分。reranker 的信号被稀释后,同一份来源文档里的正确段落更容易被排掉。
这还不是直接测量出来的因果结论。要坐实它,需要补跑单文档内的 reranker 打分对比,观察复合 query 和拆分 query 对同一批证据 chunk 的排序差异。
单文档多证据题没有这么强的稀释效应。query 通常指向同一个主题范围,相关 chunk 之间更容易一起被排上来。
优化路径
用 p^n 这把尺子看修法,方向只有两类:
提高每一关的成功率,也就是抬高 p。减少必须连过的关数,也就是降低 n。
几种常见修法可以这样读:
p | n | ||
|---|---|---|---|
full_coverage | |||
p 回到 95% 估算,约 89%(推算,未实测) |
邻居扩展的问题在于,它只是把命中 chunk 的相邻块也带上。这个动作没有让 reranker 更准确地识别单份文档里的关键段落,也没有减少题目跨越的文档数。在这组实验里,它既不抬 p,也不降 n,所以无效不是只靠调参数就能解决。
增大 top-k 能改善结果。k=16 到 k=40 后,题级 full_coverage 从 63.4% 到 82.9%。但它付出的代价也直接:最终上下文扩大到原来的 2.5 倍。它是在用更多上下文买更高的 p。
如果信号稀释假说成立,查询分解会更贴近这个失败机制。把一个复合问题拆成多个子查询后,每个子查询都只对准一个子问题,reranker 不再用一条混合 query 去给所有证据打分。
它不能减少最终答案需要覆盖的文档数。旧型号和新型号的证据仍然都要有。但它能让每一关获得更干净的检索信号和独立预算。
按 p 回到 95% 粗略估算,cross_doc_qa 的题级 full_coverage 可以到:
(35 * 0.95^2 + 6 * 0.95^3) / 41 ≈ 89%这个 89% 只是模型推算,不是实测结果。原实验里,p≈91.5% 也是从 k=40 的题级 full_coverage 反解得到,不是逐题命中日志直接统计。要把这个结论坐实,需要补跑并保存「(题, 文档)」粒度的命中记录。
评测边界
这组实验的结论有几个边界。
第一,它来自 26 篇中文工业技术文档,语料类型是设备调试和操作手册。文档结构、参数密度、段落重复度都会影响 reranker 表现。
第二,检索链路是 naive RAG:混合召回 top-100,再用 jina-reranker 精排取 top-k。换成 query decomposition、multi-vector、late interaction、文档级路由或结构化检索后,p 和 n 的具体数值会变。
第三,full_coverage 是严格指标。它适合多证据题,因为答案缺一块就可能不完整;但它不等同于最终生成答案的用户满意度。
第四,这里评估的是检索层,证据命中由人工标注的 evidence span 决定,没有把 LLM 生成阶段的表达、推理和幻觉风险算进去。
小结
跨文档问答的难点,可以压缩成一个公式:
题级全覆盖 ≈ 单份文档证据全覆盖率 ^ 跨越文档数单文档多证据题上,系统只需要在一份文档里挑全证据,p=95.1% 时题级结果就是 95.0% 左右。
跨文档题上,每道题要连续通过 2~3 份文档。即使单份文档成功率还有 80.7%,连乘之后也会落到 63.4%。
这个拆法还纠正了一个常见误判:看到上下文里有 38.3% 的槽位给了非来源文档,并不能直接说明瓶颈是槽位浪费。对照组里,单文档题浪费了 60.8% 的槽位,却仍然做到 95.0% 的题级全覆盖。
下一步最该验证的是复合 query 对 reranker 信号的稀释。加 k 可以缓解,但代价是上下文膨胀;查询分解更接近这个假说,因为它让每个子问题单独检索、单独排序、单独分配预算。
评测集如果只覆盖单文档多证据题,会高估 RAG 在真实综合问答里的能力。多证据题不只要标证据条数,还要标清楚证据跨了几份文档。这个 n,会被完整地写进最终成功率里。
夜雨聆风