多步骤 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=0跟top_p=1,锁定模型版本。 - 即使 temperature 0,现代模型因为 GPU 非确定性也不是完全确定性 —— 但接近够了。
- 真要确定性:把已知坏 run 的精确 response 存到 fixture 文件,重播 agent loop 用 fixture 不打活 API,这样可以用 debugger 一步一步走。
系统化流程
出包时按这个顺序走:
- 拿到一次失败的干净 trace。 能重现最好。
- 找出事情走歪的精确步骤。 读每步的 input/output,不要跳。
- 对到上面 7 种失败模式之一。
- 形成单一假设。 「我认为是模式 3,因为工具参数跟我的 schema 不对。」
- 只改一件事。 不要同时改 prompt、工具 schema、跟模型。
- 重跑同一个输入。 改动修了症状吗?
- 再跑 5 个其他输入。 改动有没有破坏别的东西?
- 还坏就回第 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。