Agent 为什么会陷入 Tool 调用死循环?

结论先行:Tool 调用死循环通常不是单点 bug,而是 Agent 控制闭环失稳。定位时不要只看最后一次输出,要看每一轮的状态、动作、观察、记忆和终止判断是否在推动任务前进。

1. 先把 Agent 看成一个闭环系统

Agent 不是普通的 input -> output 函数。它更接近一个带反馈的控制系统:

no

yes

Goal
任务目标

State
当前状态

Planner
下一步决策

Action
选择工具和参数

Tool
执行外部操作

Observation
工具返回

Memory / State Update
写入事实和进度

Stop?

Final Answer / Done

一次正常推进的 Agent run,有三个必要条件:

条件 含义 反例
状态在变化 每轮获得了新事实、新约束或新结论 同一个 search 结果反复出现
决策在收敛 动作越来越接近完成目标 一直换 query,但语义目标不变
终止可判定 系统知道什么叫完成、失败、卡住 只有“继续试试”,没有退出分支

所以,死循环的更准确表达是:

Agent loop = 缺少有效进展判断 + 缺少退出/换策略机制

不是所有死循环都来自“信息不变”。有些场景里信息在变,但没有改变任务状态;也有些场景里工具持续失败,但控制器只会盲目重试。

2. 两种常见 Agent 模式里的循环点

视频里重点讲了两类构建模式:ReAct 和 Plan-And-Execute。它们都会调用工具,但循环风险不一样。

ReAct:边想边做,容易局部打转

Tool LLM User Tool LLM User loop [until done] 目标 Thought Action(tool, args) Observation 根据 Observation 决定下一步 Answer

ReAct 的优点是灵活,缺点是每一步都由当前上下文驱动。如果 Observation 模糊、历史过长或没有进展判定,它很容易进入:

Thought: 我还需要更多信息
Action: search(...)
Observation: 一些相似结果
Thought: 我还需要更多信息
Action: search(...)

所以 ReAct Agent 的测试重点是:每轮 action 是否带来新的事实、是否减少不确定性、是否有明确停止条件。

Plan-And-Execute:先计划再执行,容易计划失真

yes

no

User Goal

Planner
生成步骤列表

Executor
逐步执行

Tool

Observation

计划是否仍然有效?

Replan

Plan-And-Execute 的优点是结构清晰,缺点是计划可能和真实环境脱节。循环经常发生在两处:

位置 循环原因 例子
Execute 某一步无法完成,但 Executor 不会换策略 一直调用同一个失败工具
Replan 每次重新计划都生成同一套不可行步骤 计划 A 失败,再生成计划 A

所以 Plan-And-Execute 的测试重点是:计划是否可验证、失败后是否真正改变策略、replan 是否产生了语义差异。

3. 死循环的三种典型形态

渲染错误: Mermaid 渲染失败: Parse error on line 6: ...me args
如 search('pricing') x 10] -----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'PS'

最容易漏掉的是第二种:表面上每次 query 都不同,但语义上没有新方向。例如:

search("A 公司 2024 revenue")
search("A company 2024 annual revenue")
search("A 公司 去年收入")
search("A company financial result")

这些不一定是错误。如果每次搜索带来新的可靠证据,它就是探索;如果 extracted facts 没变,它就是语义重复。

4. 六个故障层级

原文里把“死循环 = Planner 在信息不变时反复做相同决策”作为一句话总结,这个说法有用,但不完整。工程上建议按六层定位:

Goal 层
目标和完成条件

Planner 层
计划和下一步选择

Action 层
工具和参数

Tool 层
契约和返回语义

Memory 层
事实、历史、进度

Controller 层
终止、重试、回退

层级 常见问题 可观测信号 修复方向
Goal 没定义完成条件 Agent 不知道何时回答 把目标拆成可验收条件
Planner 没有进展感 一直选择同类动作 注入 state diff、已尝试动作、剩余缺口
Action 参数语义错误 schema 合法但业务错误 参数预校验、语义校验、高风险确认
Tool 返回不可判定 pending、空字符串、模糊成功 设计明确状态码和 next_action
Memory 忘记已知事实 重复发现 A/B 外部状态表、facts ledger、action history
Controller 只会 retry 同错同参重复失败 retry budget、stuck detector、fallback

这张表比“怀疑 Tool”或“怀疑 Prompt”更可靠,因为它把 Agent 拆成可观测组件。

5. 最常见的根因:进展无法被度量

Agent 不是必须每一步都成功,但必须能判断“这一步有没有让任务更接近完成”。

一个实用的进展模型:

Progress =
    new_facts
  + reduced_uncertainty
  + completed_subgoals
  - repeated_actions
  - unresolved_errors

这不是为了追求数学精确,而是为了让系统有判断依据。

new facts

no new facts

yes

no

Observation

Extract facts

Compare with known facts

Update state

No information gain

Repeated?

Stop / fallback / ask human

Try different strategy

判断是否卡住,不能只看文本是否重复。更好的方式是看语义状态是否变化:

{
  "step": 5,
  "goal": "找到 A 公司 2024 年收入并给出来源",
  "action": {
    "tool": "search",
    "args": {"query": "A company 2024 annual revenue"}
  },
  "observation_hash": "9b1c...",
  "extracted_facts": [
    {"key": "revenue_2024", "value": "12.8B USD", "source": "annual_report"}
  ],
  "new_facts_count": 0,
  "repeated_action_score": 0.82,
  "open_questions": [],
  "answerable": true,
  "decision": "stop_and_answer"
}

关键字段是 new_facts_countopen_questionsanswerablerepeated_action_score。有了这些字段,循环检测才不是拍脑袋。

6. Tool 返回值必须是可判定契约

很多死循环来自工具返回设计太弱:

# 不推荐:Agent 无法判断下一步
return "请求已提交,请稍后查看"

# 推荐:返回可执行语义
return {
    "status": "PENDING",
    "retry_after_seconds": 30,
    "max_poll_attempts": 3,
    "operation_id": "op_123",
    "next_action": "poll_status"
}

工具返回至少应该回答四个问题:

问题 示例字段
成功、失败、还是等待? status: SUCCESS / FAILURE / PENDING / AMBIGUOUS
如果失败,失败类型是什么? error_code, recoverable
如果要重试,什么时候重试? retry_after_seconds, retry_budget
下一步建议是什么? next_action

否则 Agent 只能猜。猜错几轮后,就变成了循环。

7. Hard Stop 不是修复,只是保险丝

max_steps 必须有,但它不是根治方案。

for step in range(MAX_STEPS):
    result = agent.step()
    if result.done:
        return result.answer
    if stuck_detector.is_stuck(history):
        return fallback_or_fail(result)

max_steps 只能防止系统无限烧钱,不能解释为什么卡住。真正的修复要加三类机制:

机制 作用
Stuck Detector 判断是否重复、无信息增益、同错重试
Strategy Switch 从搜索切到读文档、从自动处理切到询问用户
Failure Report 输出失败原因、已尝试动作、缺失条件

失败也应该是一个明确状态,而不是沉默地继续运行。

8. 一个可落地的诊断流程

yes

no

yes

no

yes

no

yes

no

yes

no

发现 Tool 重复调用

工具参数完全相同?

检查 retry budget / controller

语义目标是否相同?

检查 information gain

是否仍在缩小问题空间?

可能是正常探索

Planner 缺少策略切换

错误是否相同?

加错误分类和参数修正

检查 Tool 稳定性

已有答案是否可生成?

Stop condition 缺失

补充缺口定义和下一步策略

这套流程的核心不是“第几次就停”,而是问:

这一轮相对上一轮,任务状态发生了什么可验证变化?

如果答不上来,就不能继续盲目调用工具。

9. 最小可用的 Agent 日志

没有日志,Agent 死循环只能靠猜。最小日志建议如下:

{
  "run_id": "run_001",
  "step": 7,
  "goal": "完成用户订单退款",
  "state_summary": "已确认订单存在,退款接口连续返回 PENDING",
  "action": {
    "tool": "refund_status",
    "args": {"operation_id": "op_123"}
  },
  "observation": {
    "status": "PENDING",
    "retry_after_seconds": 30
  },
  "state_diff": {
    "new_facts": [],
    "completed_subgoals": [],
    "open_questions": ["退款最终状态未知"]
  },
  "diagnostics": {
    "same_action_count": 3,
    "same_error_count": 0,
    "information_gain": 0,
    "answerable": false,
    "stuck": true
  },
  "controller_decision": "fallback_to_human_or_schedule_poll"
}

只要这个日志稳定存在,循环问题就能归类到 Planner、Tool、Memory 或 Controller,而不是靠感觉排查。

10. 最后的判断标准

一个可靠的 Agent 系统至少要满足:

能力 检查问题
目标清晰 完成条件能否被机器判断?
状态可见 每轮 state diff 是否可记录?
工具可判定 Tool 返回是否包含状态、错误、下一步?
记忆可靠 已知事实是否脱离上下文窗口持久化?
重试受控 是否有 retry budget 和 backoff?
失败可解释 卡住时能否输出原因和已尝试路径?

一句话总结:

Agent 死循环的本质,是系统无法区分“继续探索”和“原地重复”。

解决它,不是只改 prompt,也不是只加 max_steps。要把 Agent 当成一个工程控制系统:记录状态变化,约束工具契约,检测无进展,必要时切换策略或明确失败。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐