🔄 Agent 死循环之谜:为什么你的 Tool 在疯狂重复执行?

封面

你是否遇到过这样的场景:Agent 调用了同一个 Tool 几十次,却始终在原地打转?Token 疯狂燃烧,任务毫无推进。今天我们就来深度拆解这个现象的根因


一、先理解:Agent 的决策闭环

在分析"死循环"之前,我们需要先理解一个正常运转的 Agent 是怎样工作的。

Agent 的本质不是"一问一答",而是一个多步决策闭环。

Agent执行闭环

每一轮迭代的核心链路:

Goal → State → Planner → Action → Tool Observation → Memory Update → Stop/Proceed

🔑 关键洞察:闭环中的每个节点都可能成为死循环的触发点。只要有一个环节"卡住",Agent 就会陷入无限重试。


二、正常流程 vs 死循环流程

看一张对比图,立刻明白差异:

正常vs死循环

维度 ✅ 正常流程 ❌ 死循环流程
状态 每步更新 原地不动
记忆 累积事实 反复遗忘
决策 基于新信息 基于旧信息
终止 条件明确 条件缺失

一句话总结:死循环 = Planner 在信息不变的情况下反复做出相同的决策。


三、六大死循环失败模式

六大失败模式

让我们逐一深入分析最关键的四种模式 👇


模式 ①:Planner 的"无进展感"

这是最常见的死循环根因。

Planner无进展感

Planner 陷入"地鼠日"(Groundhog Day)——每次醒来,世界都是一样的。

根本原因拆解:

原因 机制 后果
Context Window 溢出 对话历史超长,旧信息被截断 Planner 忘记自己已经做过什么
State 未更新 Action 执行后未修改状态变量 Planner 看到的"世界"没变
缺少进度指标 没有 steps_completedprogress % 等度量 Planner 无法判断"是否前进了"

💡 这就好比:你让一个人走迷宫,但每走一步就擦掉他的脚印。他永远不知道自己走过哪里,于是不断重复同一条路。


模式 ②:Tool 返回"不可判定"结果

当 Tool 的返回值既不是明确的成功、也不是明确的失败时——陷阱就来了。

Tool观察陷阱

典型的"不可判定"返回场景:

# 😱 这些返回值都会让 Agent 困惑
tool_result = "操作已提交,请稍后查看结果"   # 成功了吗?不确定
tool_result = "部分数据已更新"              # 全部完成了吗?不确定  
tool_result = {"status": "pending"}          # 要等多久?不确定
tool_result = ""                             # 空返回,什么都不知道

死循环的形成过程:

Agent: "帮我查询用户数据"
Tool:  "请求已提交" (← 不可判定!没说查到了什么)
Agent: "我还没拿到数据,再查一次"
Tool:  "请求已提交" (← 相同的模糊回答)
Agent: "还是没拿到,再来..."
... ♻️ 无限循环

⚠️ 核心问题:Agent 无法区分"需要重试"和"方法根本不对"。


模式 ③:记忆丢失——上下文窗口的诅咒

记忆丢失链

LLM 的上下文窗口是有限的。当对话轮次增多:

Iteration 1: 发现事实 A ✅ → 存入上下文
Iteration 2: 发现事实 B ✅ → A 仍在记忆中
Iteration 3: 上下文溢出 → 事实 A 被丢弃 ❌
Iteration 4: "咦,我好像还不知道 A" → 重新去查 A
Iteration 5: 查到 A → 但 B 又被丢弃了 ❌
Iteration 6: "我好像还不知道 B" → 又去查 B
... ♻️ 永远在 A 和 B 之间轮转

这不是 Tool 的问题,而是 Agent 架构的"硬伤"。 没有外部持久化记忆的 Agent,在长任务中必然面临这个问题。


模式 ④:终止条件缺失

没有"什么时候该停下来"的定义,Agent 就永远不会停。

终止条件缺失

两种常见的终止条件缺失:

❌ 缺少硬性上限:

# 危险!没有 max_iterations
while not task_completed:
    agent.run_next_step()
    # 如果 task_completed 永远为 False?
    # → 无限循环 🔥

✅ 正确做法:

# 安全!有多重终止保障
for i in range(MAX_ITERATIONS):
    result = agent.run_next_step()
    if result.is_success:
        break
    if result.is_stuck(last_n=3):  # 最近3步没变化
        agent.escalate_or_change_strategy()
        break

四、根因总结:一张图看懂

把所有失败模式串联起来,根因可以归结为一个公式:

死循环 = f(信息不变) × g(决策不变) × h(无法终止)
层级 故障点 根因
🎯 Goal 层 Prompt 目标模糊 Agent 不知道"完成"长什么样
🧠 Planner 层 无进展感 每轮输入相同 → 输出相同
🔧 Tool 层 返回不可判定 Agent 无法判断下一步该怎么做
💾 Memory 层 事实丢失 重复发现已知信息
🚪 Exit 层 终止条件缺失 没有"该停了"的信号
🔄 Recovery 层 恢复策略单一 失败了只会重试,不会换方法

五、如何打破死循环?

解决方案

🔑 五把钥匙

① 进度感知(Progress Awareness)

# 每步记录 state diff
state_before = agent.get_state()
agent.execute_action()
state_after = agent.get_state()
diff = compute_diff(state_before, state_after)
# 将 diff 注入 Planner 的下一轮 prompt

让 Planner 看到变化,而不是每次面对相同的世界。

② 结果三级分类(Result Classification)

def classify_tool_result(result):
    if is_definitive_success(result):
        return "SUCCESS"      # → 继续下一个子目标
    elif is_definitive_failure(result):
        return "FAILURE"      # → 切换策略
    else:
        return "AMBIGUOUS"    # → 增加探测步骤,而非简单重试

③ 持久记忆(Persistent Memory)

# 关键事实不放在上下文里,而是写入外部存储
memory_store.save("user_id_123_data", discovered_facts)
# 每轮开始时从外部加载
relevant_facts = memory_store.load(current_task_context)

④ 硬性终止(Hard Stop)

MAX_ITERATIONS = 15
STUCK_THRESHOLD = 3  # 连续3步无变化则视为卡住

for i in range(MAX_ITERATIONS):
    result = agent.step()
    if is_stuck(history[-STUCK_THRESHOLD:]):
        break  # 强制退出

⑤ 多策略回退(Multi-Strategy Fallback)

strategies = [strategy_a, strategy_b, strategy_c, human_escalation]
for strategy in strategies:
    result = strategy.execute(task)
    if result.is_success:
        break
# 所有策略都失败 → 上报人类

六、写在最后

Agent 死循环的本质,不是 Tool 出了 Bug,而是决策闭环中的反馈链断裂了。

闭环完整 → 每步都有新信息 → 决策不断演进 → 任务推进 ✅
闭环断裂 → 信息原地踏步   → 决策重复相同 → 死循环   ❌

下次当你发现 Agent 在疯狂重复调用 Tool 时,不要急着怀疑 Tool 本身——回到闭环,找到断裂的那个环节

🧭 记住这个诊断顺序

  1. Goal 是否清晰?
  2. Planner 是否感知到了进展?
  3. Tool 返回值是否可判定?
  4. Memory 是否保持了关键事实?
  5. 是否定义了终止条件?
  6. 失败时是否有替代策略?

检查这 6 个点,80% 的死循环问题都能定位。


觉得有用?欢迎转发分享给你身边正在构建 Agent 的朋友 🚀

Logo

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

更多推荐