Skip to content

我把检索召回从 16/32 修到 29/32,却只多通过 1 个问题

背景
新增 9 篇论文和 15 个测试问题后,目标证据进入候选列表的次数从 21/32 降到 16/32,新增问题的通过数从 5/15 降到 1/15。
问题
一次严格评分会同时受到论文数据、候选检索、原文读取、最终引用和评分规则影响,需要确认下降最早发生在哪一层。
做法
固定并重放检索查询,让测试论文与产品论文共用正式 Parser 和 Reading Model 构建流程,再分别统计找到、读取、引用和严格评分。
结果
目标证据进入候选列表恢复到 29/32,新增问题只从原来的 5/15 提高到 6/15;检索问题已经缩小,后续工作转向证据选择、引用覆盖和研究成本。

1. 背景、冲突、问题与结论

背景。 PaperLoom 新增了 9 篇 Agent Evaluation 论文和 15 个测试问题。扩展后,新增问题通过数从 5/15 降到 1/15,指定证据进入候选列表的次数从 21/32 降到 16/32

冲突。 严格评分位于回答链路末端。论文数据、候选检索、原文读取、最终引用和评分规则中的任何 一层出错,都会表现为同一个 Fail。只看最终分数,无法判断应该修改哪一层。

问题。 分数下降最早发生在哪里?候选召回恢复后,最终回答能否同步恢复?

结论。 召回下降来自测试论文的数据结构与产品论文不一致。统一 Parser 和 Reading Model 构建流程 后,候选召回恢复到 29/32。新增问题最终为 6/15,只比扩展前多通过 1 个。后续限制位于候选之后的 读取、证据选择、引用和成本。

2. 先固定五个概念

概念含义
Reading ModelParser 产出的论文结构,包含章节、页码、内容类型和阅读顺序
Golden Data固定问题、指定论文和指定证据位置组成的离线测试数据
Candidatefind_reading_locations 返回的候选位置
Read / Cited模型已经读取该位置 / 最终答案已经引用该位置
Hard Pass回答类型、指定位置和引用全部通过固定规则

Hard Pass 只表示答案满足当前固定规则。它不能直接代替自然语言回答的完整性或语义准确性。后续人工 审核也验证了这个边界。

3. 论证所依据的回答链路

Java 负责论文权限、会话和接口,Python Research Harness 负责 Agent 的研究过程:

text
用户问题
-> 搜索论文
-> 查找阅读位置
-> 读取准确原文
-> 组织引用
-> 提交答案
-> 离线严格评分

一次回答被拆成四个可独立观察的阶段:

text
找到:候选结果出现指定位置
读取:模型读取指定位置的原文
引用:最终答案引用指定位置
严格评分:回答类型、指定位置和引用全部通过

这个顺序存在明确依赖。候选中没有指定位置,模型无法读取它;没有读取,答案不能形成对应 Evidence; 没有引用,严格评分不会通过。

3.1 先只保留一条 Agent 运行路径

早期 Harness 把研究拆成 Intent、Retrieval Plan、Evidence Ledger、Claim Graph 和 Verification Pass。 连续的模型工具循环已经完成主要工作,后续阶段经常重复处理相同内容。

同一批 12 个固定问题得到下面的结果:

运行方式Hard Pass
多阶段版本3/12
多阶段版本加证据约束4/12
连续的 Agent 工具循环10/12

论点。 检索排查应建立在单一运行路径上。

论据。 连续工具循环通过数更高,多阶段版本还出现重复澄清、状态丢失和阶段切换错误。

论证。 同一问题同时经过多套控制流时,分数变化可能来自阶段切换,也可能来自检索。删除重复路径 可以减少干扰变量。

决定。 删除 4,000 多行自建控制流,把通用模型与工具循环交给 OpenAI Agents SDK。Harness 只保留 论文授权、位置公开、Evidence ID、引用检查和最终提交规则。

4. 决策一:先定位失败层,再修改算法

论点。 排查必须分别记录 Candidate、Read、Cited 和 Hard Pass,不能只比较最终分数。

论据。 检索返回正确位置后,模型仍可能不读取;读取后仍可能不引用;答案也可能因为 Outcome 或 格式规则失败。

论证。 如果 Candidate 已经命中,继续调 BM25 不会修复读取和引用。若 Candidate 没有命中,修改 提交检查也无法让模型看到目标位置。四层记录可以把修改指向最早失败的位置。

为了排除模型每次更换检索词的随机性,运行记录保存 find_reading_locations 的参数,再用完全相同的 参数离线重放 BM25。

决定。 检索变化使用固定查询重放;模型行为使用完整运行;最终评分在线下计算。三类结果分别保存, 不再用一个总分解释整条链路。

5. 决策二:先统一论文数据,再判断 BM25

论点。 这次召回下降应该修复测试论文的数据构建流程,不应该先增加 BM25 特殊规则。

论据。 原来的 5 篇测试论文来自正式 MinerU 流程。新增 9 篇论文使用了简化流程:

text
PDF
-> pdftotext
-> 按空行分段
-> 按句号切句
-> 全部标成普通段落
-> 不保存章节标题

简化流程生成了 8,225 个句子片段,没有有效章节、标题、表格、图片或公式。文件格式符合接口,内容 结构却与产品 Reading Model 不同。

论证。 BM25 根据词频排序。按句号切碎后,后文反复出现技术词的片段更容易排在首页摘要之前。 如果此时增加“第一页优先”,规则只会补偿测试数据缺失,无法证明产品检索需要同样修改。

测试论文随后改为共用产品构建流程:

text
PDF
-> MinerU
-> MinerUOutputMapper
-> PaperReadingModelBuilder
-> paperloom-reading-model-export/v1
-> Golden Harness

重建前后的结构差异如下:

指标简化数据正式数据
Reading Model 元素8,2253,278
带有效章节的元素02,626,约 80.1%
Heading0437
Table0108
Image + Chart0139
Formula016
可用物理页弱页码近似336

元素减少来自句子片段重新合并为段落、标题、表格和公式。正文没有丢失,章节、类型、阅读顺序和解析 来源恢复到产品使用的结构。

决定。 Golden Data 论文与产品论文共用正式 Parser 和 Reading Model 构建流程。没有增加“第一页 优先”或测试专用召回规则。

6. 论点:召回恢复不会自动提高最终通过数

论据。 统一构建流程后的结果为:

text
指定证据进入候选列表:21/32 -> 16/32 -> 29/32
新增 15 个问题:       5/15  -> 1/15  -> 6/15
全量 MiniMax:                   13/30 -> 18/30
指定位置审计:                            29/29

论证。 Candidate 从 16/32 恢复到 29/32,证明数据结构修复改善了检索。新增问题只从原来的 5/15 提高到 6/15,表明剩余失败大多发生在 Candidate 之后。

实际运行中出现了三类情况:模型没有继续读取候选;读取多篇论文后只引用其中一篇;第二次提交被截断, 答案只剩一半,旧检查仍然接受。

结论。 检索修复解决了上游召回问题,没有解决模型如何读取、选择和引用候选。检索指标与最终回答 指标需要继续分开报告。

7. 决策三:提交前检查论文级证据覆盖(已下线)

论点。 当答案讨论多篇论文时,每篇被讨论的论文都需要已有原文证据。

论据。 原来的提交检查只确认引用编号存在,并要求至少一条本轮 Evidence。它无法发现答案讨论四篇 论文,却只为一篇论文提供证据。

论证。 论文卡片、标题和摘要元数据只能帮助导航。方法、数值和实验结论需要来自 read_locations 读取的准确原文。检查论文级覆盖可以拦住“找到但没读”和“读了但没引”的答案。

当时在 submit_research_answer 前增加了论文级覆盖检查:

text
模型提交答案
-> 检查回答类型、格式和引用编号
-> 检查答案中提到的每篇论文是否已有原文证据
   -> 完整:结束运行
   -> 不完整:返回具体错误,允许同一轮继续处理

这层在线检查后来移除;现在覆盖链路由离线 Scorer/Judge 分析。

该检查只使用产品运行时已有信息,不读取测试问题编号、指定证据位置、预期事实或人工标签。

4 个引用问题最明显的测试复跑后得到:

问题结果变化
GAIA 与 WebArena 对比Fail两篇论文都有引用,但没引用 WebArena 指定位置
四类 Agent Benchmark 推荐Fail四篇论文都有引用,只命中一个指定位置
ReAct 到后续 Agent EvaluationPass三个指定位置都被找到、读取和引用
Topic Change 到 GitHub Issue AgentFail不再无引用回答,但没引用指定位置

严格评分从 0/4 变成 1/4。论文覆盖改善,指定证据位置选择仍未解决。

决定。 保留论文级证据覆盖检查,但不把它描述为检索质量提升,也不让它读取 Golden Data 答案。

8. 决策四:错误信息也属于 Agent 行为设计

论点。 提交拒绝信息需要明确告诉模型删除无关内容或补充必要证据。

论据。 Topic Change 第一次复跑时,错误信息先写“继续搜索”。模型随后搜索三篇无关论文,并把旧 话题的 GAIA 证据带入新答案。

错误信息改为优先删除与当前问题无关的内容,确有必要时再搜索。再次运行后,模型删除旁支比较,只保留 SWE-bench 和两条正文引用。

论证。 工具描述、拒绝原因和错误信息都会进入模型上下文。模型会把错误文字当作下一步行动条件, 开发日志式的措辞也会改变工具调用。

决定。 提交错误使用可执行、范围明确的反馈。先要求收窄无关内容,再要求新增搜索,避免拒绝规则 推动模型扩大问题范围。

9. 决策五:正确性检查必须同时计算成本

论点。 通过数增加不足以证明提交检查适合默认运行,还要比较模型调用、工具调用、Token 和耗时。

论据。 4 个重点问题合计使用:

text
模型调用:45
普通工具调用:48
供应商报告 Token:约 608,855
耗时:约 6.2 分钟

ReAct 相关问题经历 7 次提交拒绝,最终使用 19 次模型调用、26 次工具调用和约 36.4 万 Token。

论证。 提交检查可以推动模型补证,也会在答案范围过宽时触发更多搜索。严格评分只增加 1 个问题, 每个答案却可能多运行十几轮。成本变化必须与通过数一起评估。

决定。 Eval 记录同时保存模型调用、工具调用、拒绝次数、Token 和耗时。后续优化先减少无效搜索, 再讨论提高调用上限。

10. 工程结果

本轮保留四项做法:

  1. 测试论文与产品论文共用正式 Parser 和 Reading Model 构建流程;
  2. 检索排查使用固定查询离线重放;
  3. 运行时分别记录 Candidate、Read 和 Cited,Hard Pass 在线下计算;
  4. 提交检查只约束论文级证据覆盖,不读取测试答案。

同时停止三条路线:

  1. 不用“第一页优先”补偿上游数据缺失;
  2. 不继续增加自建 Agent 阶段;
  3. 没有人工审核时,不把指定位置失败直接归因于检索。

当前验证结果为:

text
Python tests:                  76/76
固定测试数据:                 30/30
指定位置审计:                 29/29
固定查询的候选召回:           29/32
全量 MiniMax 历史参考:        18/30
提交检查重点问题:             1/4,改造前为 0/4

向量检索没有进入本轮修复。多数剩余目标证据与查询已有明显词语重合,当前限制集中在模型如何选择、读取 和引用已有候选。

11. 保存的评估数据及用途

EvalRecorder 保存两类原始文件:

text
<run_id>/events.jsonl
<run_id>/result.json

事件包含模型响应、工具调用、候选位置、读取证据、最终引用、错误、耗时和 Token。候选召回、读取率、 引用率、成本和模型对比由离线脚本计算。运行时不保存在线奖励,也不用评分控制回答。

论点。 原始轨迹比单个总分更适合定位回归和积累训练数据。

论据。 同一条轨迹可以重新计算 Candidate、Read、Cited、Hard Pass、成本和错误分布,也能比较模型 版本与常见工具路径。

论证。 保存原始事件后,评分规则变化不需要重新调用模型。经过人工审核的高质量轨迹还可以整理成 监督数据,用于研究本地小模型训练或从强模型 API 蒸馏工具使用方式。

训练前仍需处理隐私、数据授权、错误轨迹和人工标签质量。当前记录提供材料,不直接等同于可用训练集。

12. 证据边界

能够支持的结论。 正式 Reading Model 修复了大部分候选召回;固定查询重放可以区分检索变化和模型 随机性;论文级覆盖检查能减少无引用或漏论文回答。

尚不能支持的结论。 29/32 不能证明最终回答已经解决;18/30 不能代表自然语言准确率;1/4 也不足以证明更严格的提交循环值得承担当前成本。

后续引入向量检索时,仍需分别报告 Candidate、Read、Cited 和最终评分,避免把排序变化解释成完整回答 能力变化。

13. 复现资料

bash
.venv-harness/bin/python -m unittest discover -s harness_py/tests

.venv-harness/bin/python -m harness_py \
  --manifest research/golden-data/manifest-expanded.yaml \
  validate

.venv-harness/bin/python -m harness_py agent-run \
  --product-corpus-map research/golden-data/product-corpus-map.local.yaml \
  --case-id transformer_adam_params_001

相关资料:

  • harness_py/ONBOARDING.md
  • research/golden-data/README.md
  • docs/adr/0011-use-evidence-first-golden-cases-for-harness-eval.md
  • docs/adr/0012-build-golden-schema-runtime-as-offline-eval-first.md
  • Best-of-2 Agent 编排实验

最后更新:

PaperLoom · Evidence-bounded Agentic RAG