详情

首页手游攻略 Agent 为何会陷入 Tool 调用死循环?

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

佚名 2026-07-23 09:05:13

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

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

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

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

```mermaid

flowchart LR

G[Goal<br/>任务目标] --> S[State<br/>当前状态]

S --> P[Planner<br/>下一步决策]

P --> A[Action<br/>选择工具和参数]

A --> T[Tool<br/>执行外部操作]

T --> O[Observation<br/>工具返回]

O --> M[Memory / State Update<br/>写入事实和进度]

M --> E{Stop?}

E -- no --> S

E -- yes --> R[Final Answer / Done]

```

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

| 条件 | 含义 | 反例 |

|---|---|---|

| 状态在变化 | 每轮获得了新事实、新约束或新结论 | 同一个 search 结果反复出现 |

| 决策在收敛 | 动作越来越接近完成目标 | 一直换 query,但语义目标不变 |

| 终止可判定 | 系统知道什么叫完成、失败、卡住 | 只有“继续试试”,没有退出分支 |

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

```text

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

```

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

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

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

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

```mermaid

sequenceDiagram

participant U as User

participant L as LLM

participant T as Tool

U->>L: 目标

loop until done

L->>L: Thought

L->>T: Action(tool, args)

T-->>L: Observation

L->>L: 根据 Observation 决定下一步

end

L-->>U: Answer

```

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

```text

Thought: 我还需要更多信息

Action: search(...)

Observation: 一些相似结果

Thought: 我还需要更多信息

Action: search(...)

```

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

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

```mermaid

flowchart TD

U[User Goal] --> P[Planner<br/>生成步骤列表]

P --> E[Executor<br/>逐步执行]

E --> T[Tool]

T --> O[Observation]

O --> C{计划是否仍然有效?}

C -- yes --> E

C -- no --> R[Replan]

R --> E

```

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

| 位置 | 循环原因 | 例子 |

|---|---|---|

| Execute | 某一步无法完成,但 Executor 不会换策略 | 一直调用同一个失败工具 |

| Replan | 每次重新计划都生成同一套不可行步骤 | 计划 A 失败,再生成计划 A |

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

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

```mermaid

flowchart TB

L[Tool 调用死循环] --> A[重复同一动作]

L --> B[语义重复动作]

L --> C[失败后盲目重试]

A --> A1[same tool same args<br/>如 search('pricing') x 10]

B --> B1[same intent different wording<br/>如 pricing / price / cost 反复搜索]

C --> C1[same error no diagnosis<br/>如参数错误后继续原样调用]

```

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

```text

search("A 公司 2024 revenue")

search("A company 2024 annual revenue")

search("A 公司 去年收入")

search("A company financial result")

```

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

## 4. 六个故障层级

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

```mermaid

flowchart TD

G[Goal 层<br/>目标和完成条件] --> P[Planner 层<br/>计划和下一步选择]

P --> A[Action 层<br/>工具和参数]

A --> T[Tool 层<br/>契约和返回语义]

T --> M[Memory 层<br/>事实、历史、进度]

M --> C[Controller 层<br/>终止、重试、回退]

C --> G

```

| 层级 | 常见问题 | 可观测信号 | 修复方向 |

|---|---|---|---|

| 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 不是必须每一步都成功,但必须能判断“这一步有没有让任务更接近完成”。

一个实用的进展模型:

```text

Progress =

new_facts

reduced_uncertainty

completed_subgoals

- repeated_actions

- unresolved_errors

```

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

```mermaid

flowchart LR

O[Observation] --> F[Extract facts]

F --> D[Compare with known facts]

D -->|new facts| U[Update state]

D -->|no new facts| N[No information gain]

N --> K{Repeated?}

K -->|yes| X[Stop / fallback / ask human]

K -->|no| Q[Try different strategy]

```

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

```json

{

"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_count`、`open_questions`、`answerable` 和 `repeated_action_score`。有了这些字段,循环检测才不是拍脑袋。

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

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

```python

# 不推荐: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` 必须有,但它不是根治方案。

```python

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. 一个可落地的诊断流程

```mermaid

flowchart TD

S[发现 Tool 重复调用] --> Q1{工具参数完全相同?}

Q1 -- yes --> A[检查 retry budget / controller]

Q1 -- no --> Q2{语义目标是否相同?}

Q2 -- yes --> B[检查 information gain]

Q2 -- no --> Q3{是否仍在缩小问题空间?}

Q3 -- yes --> C[可能是正常探索]

Q3 -- no --> D[Planner 缺少策略切换]

A --> Q4{错误是否相同?}

Q4 -- yes --> E[加错误分类和参数修正]

Q4 -- no --> F[检查 Tool 稳定性]

B --> Q5{已有答案是否可生成?}

Q5 -- yes --> G[Stop condition 缺失]

Q5 -- no --> H[补充缺口定义和下一步策略]

```

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

```text

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

```

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

## 9. 最小可用的 Agent 日志

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

```json

{

"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? |

| 失败可解释 | 卡住时能否输出原因和已尝试路径? |

一句话总结:

```text

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

```

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

点击查看更多
推荐专题
热门阅读