2320 字
约 7 分钟
18
S21-A19 · 一次 Agent 任务的复盘:把"它没做好"拆成可修的项

用户让 Agent 改一个配置文件的三个字段。它读了一遍文件,改对了两个,第三个改成了"看起来对但语义不同"的值,然后报告"已完成"。复盘时说"模型能力不够"就结束了——但真正的问题在任务描述里:第三个字段的名字在当前文件里出现过两次,而描述里只说了一次"改 port"。

"Agent 没做好"不是一个可修的结论。可修的结论必须落到具体的一层:是输入的信息不足、是工具的返回不明确、是任务描述有歧义、还是模型的判断错。这四层对应的修法完全不同。


1. 复盘的四个层次

层 问题形态
输入 需要的信息不在上下文里
工具 返回不足以支持下一步判断
描述 指令有歧义或多解
判断 信息齐全但结论错

四层还有一个顺序特性:信息不足会伪装成判断错误。上下文缺一个字段,模型只能按现有信息给出最合理的结论,而这个结论在事后看就是"判断错"。所以归因必须从第一层往第四层查,不能跳。

多数失败集中在第一层与第三层,而团队习惯归因到第四层——因为"模型不行"最容易说出口,也最不需要改任何东西。

2. 逐层取证

层 取证方式
输入 检查该轮的完整上下文
工具 看工具返回的原始内容
描述 换个人读一遍,看会不会有第二种理解
判断 把信息补齐后重跑,看是否仍错

第一层的取证要看得足够细:不是"上下文里提没提",而是"提到时是否唯一、是否带单位、是否在关键位置"。同一个词在上下文里出现五次,信息量反而下降。

第四项是最有分辨力的实验:如果补齐信息后它就做对了,那问题从来不是"能力"。这个实验只需要重跑一次,成本极低。

第二项的取证常常有意外:工具返回了 200 但 body 里是错误信息,Agent 据此继续往下做,于是错在后面几步。这类问题看起来像"判断错",实际是"工具撒谎"。

3. 任务描述的三类缺陷

缺陷 例子
指代不明 "改 port"(文件里有两处)
完成条件模糊 "优化一下"
缺失败预案 没说找不到时该怎么办

三类缺陷有一个共同修法:把"要做什么"改成"怎么判断做完了"。完成条件一旦可判定,指代与预案的问题会同时消失大半,因为 Agent 会先去找判据。

第一项在多文件、多位置的任务里最常见:人靠上下文能猜对,而 Agent 只能按字面执行。所以给 Agent 的描述需要的精度高于给人。

第三项被忽略得最多:没有失败预案,Agent 就会"尽力完成",而"尽力完成"往往表现为编造一个合理的结果。

// 可执行的描述:三要素齐备
task: "把 config/app.yaml 里 database.port 从 5432 改为 5433"
locate: "只改 database.port;若存在多个 database 段,先报告不要改"
done_when: "文件里 database.port 的值为 5433,且其它字段未变"
on_failure: "若定位不到唯一目标,停止并输出候选位置,不要猜测"

复盘的证据来源要固定:完整的一轮记录(用户输入、每次工具调用与返回、最终输出)。只凭最终输出复盘,等于看着结果猜过程,而 Agent 的失败大多是过程性的。

// 复盘需要的五段记录
1. 用户原始输入(不改写)
2. 每轮的上下文摘要(长度与关键内容)
3. 每次工具调用:参数 + 原始返回
4. 模型的中间结论(若可见)
5. 最终输出与它声称"已完成"的依据
// 缺 3 → 无法判断是"工具撒谎"还是"判断错"

4. 工具返回的可判断性

返回 能否支撑下一步
只有"成功" 不能,缺细节
成功 + 新状态 能
失败 + 原因 + 可行动项 能
失败 + 泛化错误码 不能

还有一个容易忽视的返回缺陷:成功但结果为空。"没找到"与"请求成功但没有结果"在语义上是两件事,若工具只返回空列表而不说明原因,Agent 往往会再试一遍同样的调用。

// 空结果要带原因,否则会被当成"再试一次"
{"ok": true, "items": [], "empty_reason": "no_match",
 "searched": {"path": "config/", "pattern": "*.yaml"},
 "suggestion": "扩大到 config/** 或以 --recursive 重试"}

第四项会让 Agent 陷入"改参数重试"的死循环:它不知道错在哪,只能随机调整。所以工具的错误返回要带 field、expected、retryable 这类结构化信息。

// 失败返回:结构化到"能直接修"
{"ok": false, "error": {
  "code": "INVALID_VALUE",
  "field": "database.port",
  "expected": "integer 1-65535",
  "received": "5433 ",
  "retryable": true,
  "hint": "值末尾有空格"
}}

5. 复盘记录的写法

写法 可修性
"模型理解错了" 无
"第三步改错字段" 低
"文件里两处 port,描述未限定" 高
上一条 + "改为限定 database.port" + "重跑验证通过" 最高

第三条开始才是有效复盘:它指向一个可以改的东西(描述),而不是一个无法改的东西(能力)。

复盘记录还应当带"这一层有几类失败"的统计:连续十次任务里第一层占几次、第三层占几次。单次复盘的结论只是个案,分布才决定该优先修描述还是修工具。

第四条加上"重跑验证",把结论从"推测"变成"已验证"。

复盘的产出应当是"改动清单"而不是"结论":改哪句话、加哪个字段、拆哪个工具、补哪条验证。清单上的每一项都要能在下一次任务里被验证是否有效。

// 可验证的改动清单(每项带验证方式)
改:描述里"改 port" → "改 database.port"
验:同一任务重跑 5 次,5 次都定位到同一行
改:read_file 返回加 total_lines 与 matched_ranges
验:构造 3 处匹配的文件,Agent 不再只改第一处

6. 排错:五种"改了很多次还是不行"

现象 成因 处理方向
每次错在不同地方 描述歧义 限定目标与完成条件
反复重试同一动作 错误返回无信息 工具返回结构化
编造一个结果 无失败预案 明确"停下并报告"
明明信息在上下文里却不用 上下文过长被挤出 精简或摘要
简单任务也失败 工具粒度太粗 拆细工具

第五项与工具粒度是同一个问题:一个工具干三件事,模型就得先判断该用哪一件,而这个判断本身就是一次可能出错的动作。

第四项很实际:上下文塞得太满时,关键信息会被挤到边缘,模型倾向于使用最近与最显著的内容。所以"信息齐全"不等于"信息可用"。

7. 边界:什么情况不必深入复盘

场景 为什么 做法
一次性探索任务 不重复 记结论即可
任务本身不可判定 无客观标准 换评判方式
失败代价极低 不值得 重跑一次
已定位到明确原因 无需再拆 直接修

判断口径一句话:这个失败会不会再发生一次。会,就必须拆到可修的那一层;不会,记录一次就够。


8. 自测

  1. 四层归因中,团队最常归到哪一层?为什么那一层"最不需要改任何东西"?
  2. "补齐信息后重跑"这个实验能区分什么?为什么它的成本极低却少有人做?
  3. 任务描述的三类缺陷里,哪一类会导致 Agent"编造一个合理结果"?
  4. 工具返回"失败 + 泛化错误码"会让 Agent 陷入什么循环?正确的返回该包含哪些字段?
  5. 为什么"信息在上下文里"不等于"信息可用"?
  6. 判断"是否需要深入复盘"的那个问题是什么?
S21-A19 · 一次 Agent 任务的复盘:把"它没做好"拆成可修的项
https://www.clxhxhhr.top/posts/6451/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。