跑完 LoHoSearch 后只知道一条任务没通过,问题通常不在“总分低”,而在于缺少对首个失分步骤的判断。LoHoSearch 这类多步智能体链路通常由解析任务、检索、筛选材料、组织答案、判定几段组成,最终判定只给出一个通过或不通过。处理顺序建议是:先确认结果文件里有没有步骤级记录,再从前往后找第一条被判定为不通过的步骤,把失败任务按该步骤归类,最后只改该步骤的一项配置回跑同一批任务,看首个失分步骤是否变化。若结果文件只存最终答案,则需要先调整运行配置,把步骤级输出打开再重跑,不适合拿最终判定反推中间过程。
处理 LoHoSearch 多步任务失败时,判断依据应放在首个失分步骤而非总分:先确认结果文件是否含步骤序号、该步输出与判定字段,缺失时打开步骤级输出开关重跑;再按首个失分步骤归类失败任务,区分起步检索就错与后段整理崩掉;随后在同一失分步骤上对照通过与失败样本,比较该步输入、检索材料与组织出的答案;最后只改一项配置回跑同批任务验证。字段名与开关名需按本环境实际配置核对,不要凭本文示例直接套用。
在结果文件里定位每条任务的分步记录
第一步不是分析,而是确认数据够不够用。打开本次任务的输出目录,找结果文件(常见是 jsonl、json 或带 trace 的目录),检查是否存在步骤级字段。至少要能取到四类信息:任务标识、步骤序号、该步输出内容、该步判定结果。判定可以是布尔值、分数或判定理由文本,只要能区分通过与否即可。
# 通用字段骨架,字段名请按本环境结果文件实际结构替换
{
"task_id": "t-0007",
"question": "...",
"steps": [
{"step_index": 1, "step_name": "query_parse", "step_input": "...", "step_output": "...", "step_verdict": "pass"},
{"step_index": 2, "step_name": "retrieve", "step_input": "...", "step_output": "...", "step_verdict": "fail", "judge_reason": "..."},
{"step_index": 3, "step_name": "compose", "step_input": "...", "step_output": "...", "step_verdict": "pass"}
],
"final_verdict": "fail"
}
如果结果文件只有最终答案和最终判定,属于退化情况,不建议硬推。做法是重跑同一批任务并打开步骤级输出开关,常见配置形态包括 trace 级别、保存中间产物、dump 步骤日志一类的选项,具体开关键名以本环境配置说明为准。重跑前先跑一条任务确认输出结构变了,再放全量,避免跑完一批才发现开关没生效。
# 查看单条任务的步骤序列(字段名按实际结构替换)
jq 'select(.task_id=="t-0007") | .steps[] | {step_index, step_name, step_verdict}' result.jsonl
# 统计每条任务的首个失败步骤(用于下一步归类)
jq -r '[.task_id, ([.steps[] | select(.step_verdict=="fail")][0].step_name // "all_pass")] | @tsv' result.jsonl
把失败任务按首个失分步骤归类
归类规则要从前往后定死:从第 1 步开始顺序检查,第一条判定为不通过的步骤记为该任务的首个失分步骤,后面的步骤不再参与归类。这样做的原因是后段答案质量差经常是前段材料缺失的连锁结果,按最后一步归类会把问题记到错误的环节。全部步骤都通过但最终判定不通过的任务单独列一类,这类通常是判定口径或答案格式的问题,与链路步骤无关。
归类后能看出两种形态:一开始就错的,首个失分步骤集中在解析或首轮检索;后段崩掉的,前几步通过,问题落在材料筛选或答案组织。两种形态的排查方向完全不同,混在一起看只会得到“整体效果不好”这种无法行动的结论。
| 首个失分步骤 | 形态 | 该步典型表现 | 先查什么 |
|---|---|---|---|
| 任务解析 | 一开始就错 | 检索词与问题意图不符,抽错关键实体 | 解析提示词、字段抽取规则 |
| 首轮检索 | 一开始就错 | 返回材料为空或与问题无关 | 检索词构造、过滤条件、知识源范围 |
| 材料筛选 | 后段崩掉 | 有用材料被丢弃,保留了大量无关片段 | 筛选阈值、截断长度、排序依据 |
| 答案组织 | 后段崩掉 | 答案与已检索材料不一致,缺关键结论 | 组织提示词、上下文拼接方式 |
| 无失败步骤 | 判定层 | 各步通过但最终判定不通过 | 判定脚本、答案格式要求 |
表里每一行只统计本批任务中首个失分步骤落在该处的条数,不要叠加多个步骤的失分次数,否则分布会失真。
在同一失分步骤上对照通过与失败的样本
归到同一类后,不要只看失败样本。挑两三条首个失分步骤相同的失败任务,再挑两三条在该步骤判为通过、且后续步骤也顺利的任务,逐条比较三处差异:该步的输入内容(检索词或上下文是否一致)、该步检索到的材料(数量与相关度)、该步组织出的答案(结论是否落到材料上)。差异往往集中在其中一处,两处以上同时不同则说明该步与其他配置耦合,需要分开验证。
# 对照记录表字段建议(按行逐条填写,不写主观评价)
task_id | 分组(通过/失败) | 首个失分步骤 | 该步输入摘要 | 检索材料条数 | 材料是否覆盖答案要点 | 该步输出摘要 | 差异点
比较时保持观察口径一致:同一个判定脚本、同一份材料来源、同一套提示词版本。如果失败样本和通过样本用的配置版本本来就不同,对照结论不成立,需要先把配置拉齐再比。
只改一个步骤的配置再回跑同一批任务
前面的定位只是推断,需要回跑验证。原则是单变量:只改该步骤的一项配置,其余提示词、检索源、判定脚本全部不动,回跑同一批任务(同一份任务清单,不换样本)。改动项和回跑结果各自记录,避免事后无法复现。
改哪一项:材料筛选阈值 / 检索 top_k / 组织提示词(三选一,只动一个)
其余不变:任务清单、知识源、判定脚本、模型与参数
回跑范围:与首次运行完全相同的任务清单
对比口径:同一判定脚本输出,逐条比较首个失分步骤
判定标准:该类失败任务的占比是否下降;首个失分步骤是否后移或消失;
通过样本是否出现回归(原本通过的步骤变为失败)
风险边界:单次回跑只能说明本条改动与该类失败相关,不能证明是唯一原因
判定时优先看两个信号:该类失败任务的首个失分步骤是否从原来的位置往后移,或直接变为全部通过;原本通过的样本有没有被这次改动带坏。只看到总数变好、首个失分步骤分布没变,通常说明改动没有作用在定位到的环节。若回跑后失败分布整体迁移到另一步骤,说明两步存在依赖,需要回到第二步重新归类,而不是继续叠加改动。每轮只保留一条改动结论,改动项记不住的对比结果不能作为后续依据。