跳到内容

进阶★★★★10 分钟阅读

Debug 一个行为怪怪的多步骤 agent

第 4 步歪掉但你不知道为什么。这是系统化 playbook。

登入以收藏

多步骤 agent 失败的方式跟单一 prompt LLM 应用不一样。模型前三步都对,第四步突然出包 —— 调用错工具、幻觉出参数、卡在重复同一个动作。你重跑 prompt 没办法重现,因为 prompt 已经不一样了(历史更多、中间结果不同)。

这是系统化 playbook,找出真正出错的地方,而不是乱枪打鸟。

开始之前:先拿到 trace

下面所有东西都假设你看得到完整 trace。如果你没有 observability(Langfuse、LangSmith、自制 log),停下来先加。花两小时加 trace log 会省下二十小时的「我猜是 prompt?改改看」。

好 trace 会显示:

  • 每次 LLM 调用的完整输入跟输出
  • 每次工具调用跟结果
  • 步骤之间传递的状态
  • 每步的 token 数跟延迟

7 种失败模式(按频率排序)

我 debug agent 的经验,所有怪行为都归到这七种 pattern 之一。按顺序走 —— 最常见的在前面。

1. Context 溢出

「agent 第 5+ 步抓狂」最常见的原因。message 历史已经长到 context window 80%,模型现在忽略 system 指示,优先吃最近的工具输出。

症状: 前几轮正常,第 4-6 轮变怪,第 8 轮以后完全坏掉。

修法:

  • 每轮打印总 token 数。
  • 长太快就摘要旧轮次或丢掉它们的工具结果。
  • 用 prompt caching 让 system prompt 不必每轮重送(部分厂商)。
  • max_model_len,强迫 agent 简洁。

2. 过时工具结果污染 context

agent 早期搜了一下,搜索回了 5000 token 的垃圾。这堆垃圾现在在每后续轮的 context 里,模型一直想把新问题跟它连起来。

症状: agent 卡在对话早期某个无关的东西上,放不下错的前提。

修法:

  • 把工具结果截断到 N tokens。
  • 工具结果消费过后,在历史里换成摘要:「[step 2 对 'X' 的搜索,完整输出见原步骤,摘要:Y]」。
  • 让 agent 可以用 forget(step_id) 工具明确丢掉旧 context。

3. Schema 漂移

模型应该调用 update_user(id: int, name: str) 但它打了 update_user(user_id: "123", new_name: "Alice")。agent 以为成功了,工具实际上错误或做错事。

症状: Log 显示工具被调用,DB 没反映变化。或工具错误但没返回上去,agent 以为一切顺利。

修法:

  • 工具输入 schema 写严格(additionalProperties: false、enum、精确字段名)。
  • 在 server 端验证 agent 的工具参数,验证失败回明确错误。
  • 确认工具错误有当 tool_result content 回给 agent,不是被吞掉。
  • 厂商支持的话,用 structured outputs / strict mode。

4. 模型以为它答完了其实没

agent 的工具回了一个错误,错误字符串里有「user's email is empty」之类的文字。模型把那段文字当成你问题的答案就停了。

症状: agent 没做有用的事就终止。输出看起来合理但里面有明显来自工具错误的片段文字。

修法:

  • 工具错误格式加明确前缀:"ERROR: <原因>",不要只给 <原因>
  • system prompt 写:「收到 ERROR 开头的工具结果,不要把错误文字放进最终答案。试别的方法或回报失败。」
  • 把意外的 stop_reason 当 bug,不是正常完成。

5. 工具调用循环

agent 一直调用同一个工具,参数略有不同,从来不收敛。要嘛它拿不到对的答案又不肯放弃,要嘛每次工具结果都让它更困惑。

症状: 撞 maxSteps。trace 显示 8 次搜索,query 都类似。成本飙升。

修法:

  • 在你的 loop 逻辑里侦测重复调用。同一工具用类似 input 调用两次,拦下来强迫 agent 试别的。
  • 改善工具描述:「如果回了空结果,代表用户数据不存在;不要用变体重试。」
  • system prompt 加步数计数:「你在第 5 步,共 10 步。第 8 步以后必须给最终答案或承认失败。」

6. Reasoning 区块没用 / 用错

对 reasoning 模型(o3、开 extended thinking 的 Claude、DeepSeek R1),模型有时会忽略自己的推理、最终答案跟推理矛盾、或推理被中途切掉时想到一半失去脉络。

症状: 推理 trace 写「答案是 42」但最终响应写 47。或模型生 4000 token 思考,然后一个简短又无关的最终答案。

修法:

  • 撞推理 token 上限就调高预算。
  • 把最终答案那步分开做明确 prompt:「根据你前面的推理,用一句话给最终答案。」
  • Claude extended thinking:确认 thinking 跟 response 两边预算都够。

7. 非确定性 + temperature

你跑同样 prompt 两次拿到不同结果,一次成功一次失败。bug 重现不了。

症状: 没办法可靠重现失败。昨天坏掉的 trace 跟今天的不一样。

修法:

  • debug 时设 temperature=0top_p=1,锁定模型版本。
  • 即使 temperature 0,现代模型因为 GPU 非确定性也不是完全确定性 —— 但接近够了。
  • 真要确定性:把已知坏 run 的精确 response 存到 fixture 文件,重播 agent loop 用 fixture 不打活 API,这样可以用 debugger 一步一步走。

系统化流程

出包时按这个顺序走:

  1. 拿到一次失败的干净 trace。 能重现最好。
  2. 找出事情走歪的精确步骤。 读每步的 input/output,不要跳。
  3. 对到上面 7 种失败模式之一。
  4. 形成单一假设。 「我认为是模式 3,因为工具参数跟我的 schema 不对。」
  5. 只改一件事。 不要同时改 prompt、工具 schema、跟模型。
  6. 重跑同一个输入。 改动修了症状吗?
  7. 再跑 5 个其他输入。 改动有没有破坏别的东西?
  8. 还坏就回第 4 步换个假设。

这里的反模式是同时改六件事,不知道是哪个修的(或哪个弄坏了新东西)。

反模式:换模型

agent 出包时诱惑就是换模型。「来试 Claude 4.7 替换 Sonnet 4.6」。有时这样有用。经常只是把底层 bug 盖住 —— agent 还是脆,你只是这个模型运气好。下次 prompt 一改它又会冒出来。

先搞清楚 agent 为什么失败。然后再决定要不要换更强模型。

一个很有用的 debug 技巧

agent 失败时,把整个 trace 复制到 Claude 或 GPT-5,问:「这个 agent 应该做 X,在第 N 步出包输出是 Y。走过 trace 告诉我模型在哪里走错,为什么。」

LLM 在这方面意外地强。它能读 5000 token 的 trace,点出触发连锁反应的那一步。把它当你人工 walk-through 之前的第一个 reviewer。

什么时候不要 debug

同一个 bug 卡 3 小时以上,停下 debug 重新考虑架构。agent 重复出怪行为通常代表任务对模型太开放,拆解可能比再改一次 prompt 有用。

具体:agent 有超过 5 个不同工具加开放任务定义,考虑拆成 planner agent(决定工具顺序)跟 executor agent(调用工具)。较小的决策空间失败较少。

下一步

  • 本 Learn 库里的「从零写 agent loop」那篇。
  • LangSmith 跟 Langfuse trace 范例。
  • Building effective agents —— Anthropic 2024 年 12 月。
  • 查这些词:chain-of-thought failure mode、agent decomposition、structured outputs、eval-driven prompt iteration。

最后更新: 2026-04-29

We use cookies

Anonymous analytics help us improve the site. You can opt out anytime. Learn more