一个"自动修复线上问题"的 Agent 跑了三个月,成功率 62%。日志显示三类失败各占三分之一:它看错了(把无关指标当成异常)、它想错了(选了不适用的动作)、它做错了(动作参数不对)。而团队修的时候,总在"提示里加一句话"——那是"决策"层的手段,治不了"观察"与"行动"的错。
Agent 的循环是三段:观察(读工具返回)、决策(选下一步)、行动(发工具调用)。三段的失败模式不同,修法也不同——这个"错误归因"有个结构原因:提示是"唯一能立刻改的东西"——而"工具返回、参数校验、终止判据"都要改代码,于是"能改的"替代了"该改的"。
而"都往提示里塞"是最常见的错误归因。
1. 三段的失败模式
| 段 | 失败模式 |
|---|---|
| 观察 | 读错字段、忽略关键行、被无关信息吸引 |
| 决策 | 选错工具、过早停止、重复无效动作 |
| 行动 | 参数错、格式错、顺序错 |
第一项最容易被忽略:**它"看起来在正常工作"**第一项的"识别"要"看中间产物":把它"读到的关键行"打印出来,与"你期望的关键行"对比——差异一眼可见,而"只看最终答案"永远看不出。
——工具返回了数据,Agent 也读了,只是"读到的不是关键那一段"。
2. "观察失败"的三种成因
| 成因 | 表现 |
|---|---|
| 返回太长(关键行被淹) | 读了几十条,漏了那一条 |
| 结构不清晰(嵌套/表格) | 解析成"另一层"的值 |
| 缺少"该看什么"的指引 | 抓了"最容易看到的" |
第一项的"量化"是"关键行的位置":它落在第 150 行,而"注意窗口"只有前 50 行——于是"漏掉"是位置问题,不是"粗心"。
第一项是"上下文工程"问题:返回 200 行日志,Agent 只"注意"前几十行——于是"最后那句 ERROR"被漏掉。
修法不是"让它更仔细",而是"在工具侧做筛选":只返回"匹配条件"的行,最多 N 条,并附"共匹配多少"。
// 两种"工具返回"的差别(同一份日志)
差:返回 200 行原始日志 → Agent 读到第 30 行就"开始总结"
好:返回"匹配 ERROR 的 12 行 + 首末时间戳 + 总行数 18,432"
→ Agent 一眼看到全貌,且知道"还有多少没看"
3. "决策失败"的三种成因
| 成因 | 表现 |
|---|---|
| 工具描述不可区分 | 选了"看起来像"的 |
| 缺少"上一步结果"的摘要 | 忘了"已经查过" |
| 目标判据模糊 | 不知道"够了没有" |
第三项的"具体表现"是"同一个任务两次运行结果不同":因为"够了没有"没有判据,于是每次停的位置都不同——而它看起来像"模型不稳定"。
第三项与"终止条件"是同一件事:"什么时候算完成"没有可检查的定义,于是它要么过早停、要么跑到预算耗尽。
4. "行动失败"的三种成因
| 成因 | 表现 |
|---|---|
参数名相似(id vs uid) |
填错字段 |
| 必填项没写清 | 缺参数报错 |
| 依赖顺序(先查再改) | 顺序错 |
第一项在"工具多了以后"必然发生:order_id、order_no、orderId 三种命名并存第一项的"成本"很具体:一次"填错字段"的失败,看起来像"工具坏了"——于是排查从"工具"开始,而问题在"命名"。
——于是"按字面猜"就会错。
修法是"统一命名 + 在描述里写"格式示例"":"order_id: 形如 O-12345" 比"订单 ID"清楚得多。
"工具返回"该长什么样,值得给一个模板(三段),它比"随便返回 JSON"有效:
// 好的工具返回(三段式)
[匹配] ERROR 级别 12 行(共 18,432 行日志)
[内容] 首条 14:02:11 connect refused;末条 14:31:05 pool exhausted
[提示] 若要定位"哪次请求失败",可再用 trace_id 过滤
// 关键:给"总数"、"首末"、"下一步建议" —— 于是"观察"变成"读摘要"
5. "三类失败怎么分开测"
| 段 | 测法 |
|---|---|
| 观察 | 给定返回,问"关键信息是哪一条" |
| 决策 | 给定状态,问"下一步该调哪个工具" |
| 行动 | 给定意图,问"参数怎么填" |
第四项(顺序)的"修法"是"把"前置检查"写进工具描述":"调用前请确认 order_id 存在(可先 list_orders)"——于是"顺序"变成"描述里的一行"。
这三项可以"离线测",而不必"跑完整任务"——因为"跑完整任务"的失败率是三者叠加,看不出"哪一段弱"。
// 一个"分段评测"的例子(10 条各一层)
观察层:给 10 份"含关键行的长返回",问"最该关注哪一行" → 命中率
决策层:给 10 个"中间状态",问"下一个工具" → 命中率
行动层:给 10 个"意图",问"参数 JSON" → 字段正确率
// 判据:哪层命中率最低,就先修那层(而不是"整体加提示")
"参数错"可以用"校验前置"解决,而它比"提示里写注意"可靠:
// 工具侧的三种校验(返回"可行动的错误")
1 类型/格式:order_id 必须以 O- 开头 → 不符即拒
2 存在性:先查一次"这个 id 是否存在"(可选,成本高)
3 互斥/依赖:cancel 与 update 不能同时调用
// 返回示例:{"ok":false,"field":"order_id","hint":"形如 O-12345","retryable":false}
6. "修法"与"失败段"的对应
| 失败段 | 有效修法 | 无效修法 |
|---|---|---|
| 观察 | 工具返回"筛选 + 摘要" | 提示里写"仔细看" |
| 决策 | 工具描述"负向说明" | 提示里写"别选错" |
| 行动 | 参数"示例 + 校验" | 提示里写"注意参数" |
第三列的"无效修法"有个共同点:它们都在"要求模型更小心"——而"更小心"不是可执行的指令。第三列的"共同点"值得记住:"要求更小心"不可执行,而"改变输入结构"可执行——于是修法的差异本质是"改环境"还是"改期望"。
"观测"不到位时,"分段修"会变成"猜哪段"——所以先补"中间产物的记录":
// 三条"该记的中间产物"(每条一次采集)
1 每次工具调用的"入参 + 返回摘要"(不是全文)
2 每次"决策"的候选(它考虑了哪几个工具)
3 每次"停止"的理由(判据达成 / 预算耗尽 / 无进展)
// 判据:能否从日志回答"这次失败属于哪一段"
7. 边界:什么情况不必分段修
| 场景 | 为什么 | 做法 |
|---|---|---|
| 单工具(无选择) | 决策层不存在 | 只修观察/行动 |
| 任务极短(1–2 步) | 失败率叠加不明显 | 整体调 |
| 有"人工确认每步" | 错误会被拦 | 不必细修 |
| 失败代价低(可重试) | 试错便宜 | 整体调 |
最后一条与"评测"有关的边界:分段评测要"固定随机性"(同一批输入、同一模型版本),否则"命中率波动"会被误读成"改了有效"。
判断口径一句话:"失败日志能不能归到"某一段"。能("它读错了行"/"它调错了工具"/"参数错了"),就分段修;不能(全是"结果不对"),说明"日志不够细",先补观测。
还有一个"组织"细节:"分段"这件事要写进"评测集的设计",而不是"事后分析"。因为"跑完整任务"的数据是"三段的混合",而"混合"无法归因——只有"每层单独出题",才能让"改了什么、好了多少"变成可测的。
8. 自测
- 三段循环各会错在哪?为什么"观察失败"最容易被忽略?
- "返回太长"为什么会导致"漏掉关键行"?"工具侧筛选"解决了什么?
- "目标判据模糊"为什么会让 Agent"要么早停要么跑满"?
- 为什么"分段测"比"跑完整任务"更能定位问题?
- 三类失败对应的"有效修法"分别是什么?"加提示"为什么常常无效?
- 判断"该不该分段修"的那个问题是什么?