Agent为什么会死循环?

文章目录

    • ------无限Tool调用、ReAct循环、上下文污染与业界解决方案深度解析
    • 一、为什么Agent会死循环?
    • 二、Agent执行流程到底是什么?
    • 三、Agent为什么会无限调用Tool?
      • 完整故障分析
      • [1. Tool结果不满足结束条件](#1. Tool结果不满足结束条件)
      • [2. Tool返回异常](#2. Tool返回异常)
      • [3. Prompt设计错误](#3. Prompt设计错误)
      • [4. Tool描述不清晰](#4. Tool描述不清晰)
      • [5. LLM判断失败](#5. LLM判断失败)
      • [6. 输出格式错误 / JSON解析失败](#6. 输出格式错误 / JSON解析失败)
      • [7. MCP返回异常](#7. MCP返回异常)
      • [8. Tool选择错误](#8. Tool选择错误)
      • [9. 工具之间互相调用](#9. 工具之间互相调用)
      • [10. 上下文信息丢失](#10. 上下文信息丢失)
    • 四、为什么ReAct最容易死循环?
    • [五、上下文污染(Context Pollution)](#五、上下文污染(Context Pollution))
      • 为什么Agent越来越"笨"?
      • 五种核心污染形式
      • [Context Drift(上下文漂移)](#Context Drift(上下文漂移))
      • [Context Pollution(上下文污染)](#Context Pollution(上下文污染))
      • [Claude Code 的验证](#Claude Code 的验证)
      • [Memory Pollution(记忆污染)](#Memory Pollution(记忆污染))
    • 六、目前业界如何解决死循环?(横向对比)
      • 各机制的优缺点分析
        • [Max Iteration / Turn --- 最简单也最常用](#Max Iteration / Turn — 最简单也最常用)
        • [Loop Detection --- OpenHands 的 StuckDetector](#Loop Detection — OpenHands 的 StuckDetector)
        • [Human Approval --- 人的判断是最终的防线](#Human Approval — 人的判断是最终的防线)
        • [Guardrails --- 预防而非治疗](#Guardrails — 预防而非治疗)
        • [Context Compression --- Claude Code 独有的深度机制](#Context Compression — Claude Code 独有的深度机制)
        • [Cost Budget --- 经济上的防护栏](#Cost Budget — 经济上的防护栏)
    • [七、Claude Code为什么很少死循环?](#七、Claude Code为什么很少死循环?)
      • [1. 多层上下文管理的"瑞士军刀"](#1. 多层上下文管理的"瑞士军刀")
      • [2. Sub-agent 隔离 --- 每个子任务都重新开始](#2. Sub-agent 隔离 — 每个子任务都重新开始)
      • [3. 为什么大量使用 Markdown?](#3. 为什么大量使用 Markdown?)
      • [4. 为什么强调 Project Context?](#4. 为什么强调 Project Context?)
      • [5. 工具限制的精细化](#5. 工具限制的精细化)
      • [6. 任务拆分与原子化](#6. 任务拆分与原子化)
      • [7. Token Budget 和边际效应检测](#7. Token Budget 和边际效应检测)
      • [关于 Loop Protection 的官方说明](#关于 Loop Protection 的官方说明)
    • 八、LangGraph如何避免死循环?
    • 九、如何设计不会死循环的Agent?
      • [1. 限制最大步骤数 --- 最基础但最有效【必做】](#1. 限制最大步骤数 — 最基础但最有效【必做】)
      • [2. 限制工具调用次数 --- 比限制步骤更精确【必做】](#2. 限制工具调用次数 — 比限制步骤更精确【必做】)
      • [3. 增加 Reflection 节点 --- 但不要让它循环【推荐】](#3. 增加 Reflection 节点 — 但不要让它循环【推荐】)
      • [4. 增加 Task Success 判断 --- 二进制的完成标准【推荐】](#4. 增加 Task Success 判断 — 二进制的完成标准【推荐】)
      • [5. 增加 Budget --- 用钱控制行为【必做】](#5. 增加 Budget — 用钱控制行为【必做】)
      • [6. 减少 Observation 污染 --- 只保留有效信息【必做】](#6. 减少 Observation 污染 — 只保留有效信息【必做】)
      • [7. Memory 压缩 --- 有选择地遗忘【推荐】](#7. Memory 压缩 — 有选择地遗忘【推荐】)
      • [8. Context Summary --- 定期"重新对齐"【推荐】](#8. Context Summary — 定期"重新对齐"【推荐】)
      • [9. 状态机设计 --- 消灭自由循环【进阶】](#9. 状态机设计 — 消灭自由循环【进阶】)
      • [10. 人工审批节点 --- 关键操作前必须确认【必做】](#10. 人工审批节点 — 关键操作前必须确认【必做】)
    • 十、未来Agent为什么一定会走向"可控执行"
      • 从"自主探索"到"确定性执行"的范式转移
        • [Deterministic Workflow + Agentic Intelligence](#Deterministic Workflow + Agentic Intelligence)
        • [Planning + Verification](#Planning + Verification)
        • [Tree Search + Self-Reflection](#Tree Search + Self-Reflection)
        • [Model Context Protocol(MCP)](#Model Context Protocol(MCP))
        • [Hybrid Agent --- Rule + LLM](#Hybrid Agent — Rule + LLM)
      • 总结

------无限Tool调用、ReAct循环、上下文污染与业界解决方案深度解析

真正优秀的Agent,不是最聪明,而是最稳定。


一、为什么Agent会死循环?

先看几个真实案例

先说 Claude Code。2025年8月,GitHub Issue #6004 记录了一个经典故障:用户让 Claude Code 为 Go 项目补充测试文件。Claude 正确识别了需要读取的5个已有测试文件,但在读取完成后,它进入了一个诡异的循环:

复制代码
Read test_a.go → Read test_b.go → Read test_c.go → 
Read test_d.go → Read test_e.go → [Compact conversation] → 
Read test_a.go → Read test_b.go → ...

它反复读取同样的文件、触发同样的对话压缩、然后重新开始------但一次都没有真正创建任何测试文件。这是一个过程失败(Process Failure),而非能力不足:所有需要的信息都已在第一轮获取,但控制流卡死了。

OpenHands 社区的典型场景更直观:Agent 被告知修复一个 SyntaxError。它修改了代码、执行、仍然报错------同样的语法错误。然后它再次修改、再次执行、再次报错。循环往复。但它从来不去想:"也许我应该换一种修复方式。"

Cursor 用户也经常碰见:Agent 在修改一个 React 组件后,因为缺少可以验证"修改是否正确"的测试或检查命令,于是它反复调整同一个文件的同一段代码------细微的参数变化、改回原名、再改回新名。"没有验证手段,Agent 就不知道该停手。"

AutoGen 的多 Agent 对话更离谱:如果没有设置 max_turnsis_termination_msg,两个 Agent 可以持续对话直到 API 预算耗尽------互相讲笑话、互相夸赞、永远不会主动说"够了"。

LangGraph 开发者碰到的是另一个版本:GRAPH_RECURSION_LIMIT。当 StateGraph 中的两个节点互相连接(A → BB → A)而没有设置 END 时,图会无限执行直到触碰递归限制。

核心问题

你可能会问:为什么模型越来越强(GPT-4 → Claude 4 → Gemini 2.5),但死循环问题反而越来越突出?

答案看起来矛盾但合理:正是因为模型能力足够强,所以它们被赋予了更多自主权。 当 Agent 能自己决定"要不要调用工具""调用哪个工具""要不要继续思考"时,它也获得了"永远不停下来"的能力。

Agent 的自主性 = Agent 的安全性风险。两者成正比。


二、Agent执行流程到底是什么?

不要先讲死循环------先理解正常循环

Agent 不是"一次提问、一次回答"的对话模型。它的核心是一个循环。这个循环本身不是Bug------它是 Agent 之所以是 Agent 的根本原因。
#mermaid-svg-DMz5iHGcGs30oiHx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DMz5iHGcGs30oiHx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DMz5iHGcGs30oiHx .error-icon{fill:#552222;}#mermaid-svg-DMz5iHGcGs30oiHx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DMz5iHGcGs30oiHx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DMz5iHGcGs30oiHx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DMz5iHGcGs30oiHx .marker.cross{stroke:#333333;}#mermaid-svg-DMz5iHGcGs30oiHx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DMz5iHGcGs30oiHx p{margin:0;}#mermaid-svg-DMz5iHGcGs30oiHx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DMz5iHGcGs30oiHx .cluster-label text{fill:#333;}#mermaid-svg-DMz5iHGcGs30oiHx .cluster-label span{color:#333;}#mermaid-svg-DMz5iHGcGs30oiHx .cluster-label span p{background-color:transparent;}#mermaid-svg-DMz5iHGcGs30oiHx .label text,#mermaid-svg-DMz5iHGcGs30oiHx span{fill:#333;color:#333;}#mermaid-svg-DMz5iHGcGs30oiHx .node rect,#mermaid-svg-DMz5iHGcGs30oiHx .node circle,#mermaid-svg-DMz5iHGcGs30oiHx .node ellipse,#mermaid-svg-DMz5iHGcGs30oiHx .node polygon,#mermaid-svg-DMz5iHGcGs30oiHx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DMz5iHGcGs30oiHx .rough-node .label text,#mermaid-svg-DMz5iHGcGs30oiHx .node .label text,#mermaid-svg-DMz5iHGcGs30oiHx .image-shape .label,#mermaid-svg-DMz5iHGcGs30oiHx .icon-shape .label{text-anchor:middle;}#mermaid-svg-DMz5iHGcGs30oiHx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DMz5iHGcGs30oiHx .rough-node .label,#mermaid-svg-DMz5iHGcGs30oiHx .node .label,#mermaid-svg-DMz5iHGcGs30oiHx .image-shape .label,#mermaid-svg-DMz5iHGcGs30oiHx .icon-shape .label{text-align:center;}#mermaid-svg-DMz5iHGcGs30oiHx .node.clickable{cursor:pointer;}#mermaid-svg-DMz5iHGcGs30oiHx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DMz5iHGcGs30oiHx .arrowheadPath{fill:#333333;}#mermaid-svg-DMz5iHGcGs30oiHx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DMz5iHGcGs30oiHx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DMz5iHGcGs30oiHx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DMz5iHGcGs30oiHx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DMz5iHGcGs30oiHx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DMz5iHGcGs30oiHx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DMz5iHGcGs30oiHx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DMz5iHGcGs30oiHx .cluster text{fill:#333;}#mermaid-svg-DMz5iHGcGs30oiHx .cluster span{color:#333;}#mermaid-svg-DMz5iHGcGs30oiHx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DMz5iHGcGs30oiHx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DMz5iHGcGs30oiHx rect.text{fill:none;stroke-width:0;}#mermaid-svg-DMz5iHGcGs30oiHx .icon-shape,#mermaid-svg-DMz5iHGcGs30oiHx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DMz5iHGcGs30oiHx .icon-shape p,#mermaid-svg-DMz5iHGcGs30oiHx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DMz5iHGcGs30oiHx .icon-shape .label rect,#mermaid-svg-DMz5iHGcGs30oiHx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DMz5iHGcGs30oiHx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DMz5iHGcGs30oiHx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DMz5iHGcGs30oiHx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

👤 用户输入
📋 Planner/System Prompt
🧠 Reason 推理
需要工具?
🔧 Tool 工具调用
👁️ Observation 观察结果
✅ Finish 完成
📤 最终输出

这个循环在行业内有不同叫法:Agent LoopTool Use LoopReasoning LoopAction-Observation Cycle,但本质相同------Agent 在"思考→行动→观察→再思考"之间循环,直到达成目标。

为什么一定会循环?

想象你要 Agent "帮我找到 Python 项目中所有未使用的导入"。它的执行过程天然就是循环的:

  1. Think:我需要先列出所有 Python 文件
  2. Act:执行 find . -name "*.py"
  3. Observe:得到了237个文件路径
  4. Think:需要逐个分析每个文件的导入和实际使用
  5. Act:执行 grep "import" file1.py
  6. Observe:file1.py 导入了 os, json, requests
  7. Think:需要检查哪些被实际使用了
  8. Act:分析文件中 os, json, requests 的使用
  9. Observe:requests 没有被使用
  10. ...继续处理 file2.py, file3.py...

这个循环不是 Bug,它是 Agent 完成复杂任务的必要手段。

什么时候循环变成死循环?

循环变成问题,取决于三个维度的判断:

判断维度 正常循环 死循环
进展 每轮产生新信息或状态变化 重复相同动作,无实质性推进
收敛 逐步逼近目标 偏离目标,或在原地打转
成本 消耗与复杂度匹配 消耗远超任务价值

用代码化的判断标准:

python 复制代码
# 这不是死循环
for file in project_files:
    analyze(file)  # 每次都处理不同文件
    # → 有进展,会收敛

# 这是死循环
while True:
    read_same_file()      # 反复读取同一文件
    compact_context()     # 触发压缩
    # → 无进展,不会收敛

Claude Code 的源码(query.ts)最直白地体现了这一点:它就是一个 while(true) 循环,只有当模型不再输出 tool_use、达到预算上限、或用户中断时才退出。整个 Agent 的生命就是在这个 while(true) 里度过的。


三、Agent为什么会无限调用Tool?

工具调用(Tool Call)是 Agent 与外部世界交互的唯一方式。也是死循环最常见的原因。以下是导致无限 Tool 调用的10大原因:

完整故障分析

#mermaid-svg-xU4ycYIWBtoFaPHi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xU4ycYIWBtoFaPHi .error-icon{fill:#552222;}#mermaid-svg-xU4ycYIWBtoFaPHi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xU4ycYIWBtoFaPHi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xU4ycYIWBtoFaPHi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xU4ycYIWBtoFaPHi .marker.cross{stroke:#333333;}#mermaid-svg-xU4ycYIWBtoFaPHi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xU4ycYIWBtoFaPHi p{margin:0;}#mermaid-svg-xU4ycYIWBtoFaPHi .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi .cluster-label text{fill:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi .cluster-label span{color:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi .cluster-label span p{background-color:transparent;}#mermaid-svg-xU4ycYIWBtoFaPHi .label text,#mermaid-svg-xU4ycYIWBtoFaPHi span{fill:#333;color:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi .node rect,#mermaid-svg-xU4ycYIWBtoFaPHi .node circle,#mermaid-svg-xU4ycYIWBtoFaPHi .node ellipse,#mermaid-svg-xU4ycYIWBtoFaPHi .node polygon,#mermaid-svg-xU4ycYIWBtoFaPHi .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xU4ycYIWBtoFaPHi .rough-node .label text,#mermaid-svg-xU4ycYIWBtoFaPHi .node .label text,#mermaid-svg-xU4ycYIWBtoFaPHi .image-shape .label,#mermaid-svg-xU4ycYIWBtoFaPHi .icon-shape .label{text-anchor:middle;}#mermaid-svg-xU4ycYIWBtoFaPHi .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xU4ycYIWBtoFaPHi .rough-node .label,#mermaid-svg-xU4ycYIWBtoFaPHi .node .label,#mermaid-svg-xU4ycYIWBtoFaPHi .image-shape .label,#mermaid-svg-xU4ycYIWBtoFaPHi .icon-shape .label{text-align:center;}#mermaid-svg-xU4ycYIWBtoFaPHi .node.clickable{cursor:pointer;}#mermaid-svg-xU4ycYIWBtoFaPHi .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xU4ycYIWBtoFaPHi .arrowheadPath{fill:#333333;}#mermaid-svg-xU4ycYIWBtoFaPHi .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xU4ycYIWBtoFaPHi .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xU4ycYIWBtoFaPHi .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xU4ycYIWBtoFaPHi .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xU4ycYIWBtoFaPHi .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xU4ycYIWBtoFaPHi .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xU4ycYIWBtoFaPHi .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xU4ycYIWBtoFaPHi .cluster text{fill:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi .cluster span{color:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xU4ycYIWBtoFaPHi .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xU4ycYIWBtoFaPHi rect.text{fill:none;stroke-width:0;}#mermaid-svg-xU4ycYIWBtoFaPHi .icon-shape,#mermaid-svg-xU4ycYIWBtoFaPHi .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xU4ycYIWBtoFaPHi .icon-shape p,#mermaid-svg-xU4ycYIWBtoFaPHi .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xU4ycYIWBtoFaPHi .icon-shape .label rect,#mermaid-svg-xU4ycYIWBtoFaPHi .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xU4ycYIWBtoFaPHi .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xU4ycYIWBtoFaPHi .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xU4ycYIWBtoFaPHi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
异常
缺少停止条件
Tool描述模糊
LLM误判
输出格式
MCP异常
选错工具


Agent 调用 Tool
结果满足结束条件?
❌ Tool结果不满足结束条件
❌ Tool返回异常
继续调用 Tool
Prompt设计?
❌ Prompt设计错误
❌ Tool描述不清晰
执行层面?
❌ LLM判断失败
❌ JSON解析失败
❌ MCP返回异常
❌ Tool选择错误
有进展?
💀 死循环

1. Tool结果不满足结束条件

最常见的原因。LLM 期待一个"完成"信号,但 Tool 返回的内容不够明确。

复制代码
Agent: 检查端口 3000 是否被占用
Tool: lsof -i :3000 → (空输出,端口未占用)
Agent: 不确定,再检查一次
Tool: lsof -i :3000 → (空输出)
Agent: 还是不确定,使用 netstat 检查
Tool: netstat -an | grep 3000 → (空输出)
...

根因:Tool 返回一个模糊结果(这里是一个空输出),LLM 无法判断"空输出 = 端口未占用 = 成功",于是反复验证。

2. Tool返回异常

OpenHands 的 StuckDetector 专门识别了一种模式:动作-错误循环 ------Agent 连续3次执行相同动作且均返回错误。典型场景是 Python 代码反复抛出相同的 SyntaxError,Agent 修改的不是语法错误,而是无关部分。

3. Prompt设计错误

给 Agent 的指令缺少明确的"完成标准"。比如只说"优化代码性能",但不说什么算"性能达标"(响应时间 < 200ms?内存下降 30%?),Agent 就会不断尝试优化,永远停不下来。

4. Tool描述不清晰

Tool 的 function description 如果过于模糊,LLM 就无法正确判断"这个 Tool 是否真的帮到了忙"。比如一个 Tool 叫 fetch_data,描述是 "获取数据"------获取什么数据?返回什么格式?成功标准是什么?LLM 拿到的就是一个黑盒。

5. LLM判断失败

大模型本身不是可靠的决策引擎。它可能在相同的输入下产生不同的输出,也可能因为 context 太长而"遗忘"已经完成的任务。这就是 Claude Code 无限压缩循环的根因------模型本应意识到"数据已获取 → 开始创建测试",但它判断错误,选择了重新读取。

6. 输出格式错误 / JSON解析失败

Tool 返回格式损坏时:

复制代码
Tool: {"status": "succes  ← 缺少闭合引号,JSON 非法
Agent: (解析失败) 重试...
Tool: {"status": "success"}
Agent: 现在正确了,但前面已经浪费了一轮

7. MCP返回异常

MCP(Model Context Protocol)是一个分布式的 Tool 调用协议。当 MCP Server 超时、返回错误或连接中断时,Client 端的 Agent 可能将"异常"理解为"需要重试",而非"需要换方法"。

8. Tool选择错误

Agent 选用了错误的工具来解决问题。比如应该用 Replace 精确替换字符串,却用了 Write 重写整个文件------结果其他内容丢了,问题更复杂,需要更多 Tool 调用来修复。

9. 工具之间互相调用

在多 Agent 系统中,Agent A 调用 Tool B,Tool B 内部又触发 Agent C,Agent C 又调用 Tool A------形成跨 Agent 的循环。

10. 上下文信息丢失

当 Agent 执行了很多步之后,早期的关键信息可能因为上下文太长而被"遗忘"或"稀释"。Agent 可能回到起点,重新执行已经被完成的操作------不是它不想停,而是它不记得已经做过了。


四、为什么ReAct最容易死循环?

ReAct的经典模式

ReAct(Reasoning + Acting)是当前绝大多数 Agent 的基础范式。它的流程如下:
#mermaid-svg-M7YsRWVnzy2DsfVV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M7YsRWVnzy2DsfVV .error-icon{fill:#552222;}#mermaid-svg-M7YsRWVnzy2DsfVV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M7YsRWVnzy2DsfVV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M7YsRWVnzy2DsfVV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M7YsRWVnzy2DsfVV .marker.cross{stroke:#333333;}#mermaid-svg-M7YsRWVnzy2DsfVV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M7YsRWVnzy2DsfVV p{margin:0;}#mermaid-svg-M7YsRWVnzy2DsfVV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV .cluster-label text{fill:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV .cluster-label span{color:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV .cluster-label span p{background-color:transparent;}#mermaid-svg-M7YsRWVnzy2DsfVV .label text,#mermaid-svg-M7YsRWVnzy2DsfVV span{fill:#333;color:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV .node rect,#mermaid-svg-M7YsRWVnzy2DsfVV .node circle,#mermaid-svg-M7YsRWVnzy2DsfVV .node ellipse,#mermaid-svg-M7YsRWVnzy2DsfVV .node polygon,#mermaid-svg-M7YsRWVnzy2DsfVV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M7YsRWVnzy2DsfVV .rough-node .label text,#mermaid-svg-M7YsRWVnzy2DsfVV .node .label text,#mermaid-svg-M7YsRWVnzy2DsfVV .image-shape .label,#mermaid-svg-M7YsRWVnzy2DsfVV .icon-shape .label{text-anchor:middle;}#mermaid-svg-M7YsRWVnzy2DsfVV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M7YsRWVnzy2DsfVV .rough-node .label,#mermaid-svg-M7YsRWVnzy2DsfVV .node .label,#mermaid-svg-M7YsRWVnzy2DsfVV .image-shape .label,#mermaid-svg-M7YsRWVnzy2DsfVV .icon-shape .label{text-align:center;}#mermaid-svg-M7YsRWVnzy2DsfVV .node.clickable{cursor:pointer;}#mermaid-svg-M7YsRWVnzy2DsfVV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M7YsRWVnzy2DsfVV .arrowheadPath{fill:#333333;}#mermaid-svg-M7YsRWVnzy2DsfVV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M7YsRWVnzy2DsfVV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M7YsRWVnzy2DsfVV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M7YsRWVnzy2DsfVV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M7YsRWVnzy2DsfVV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M7YsRWVnzy2DsfVV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M7YsRWVnzy2DsfVV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M7YsRWVnzy2DsfVV .cluster text{fill:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV .cluster span{color:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M7YsRWVnzy2DsfVV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M7YsRWVnzy2DsfVV rect.text{fill:none;stroke-width:0;}#mermaid-svg-M7YsRWVnzy2DsfVV .icon-shape,#mermaid-svg-M7YsRWVnzy2DsfVV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M7YsRWVnzy2DsfVV .icon-shape p,#mermaid-svg-M7YsRWVnzy2DsfVV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M7YsRWVnzy2DsfVV .icon-shape .label rect,#mermaid-svg-M7YsRWVnzy2DsfVV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M7YsRWVnzy2DsfVV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M7YsRWVnzy2DsfVV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M7YsRWVnzy2DsfVV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 💭 Thought
🎬 Action
👁️ Observation

  • Thought:思考下一步做什么
  • Action:执行具体操作
  • Observation:观察操作结果
  • 循环:基于观察再次思考

为什么天然容易无限循环?

ReAct 论文(Yao et al., 2022)本身并没有专门讨论"无限循环"问题,但从其设计可以清晰看出三个内在风险:

风险一:没有内置终止机制

ReAct 的终止完全依赖 LLM 自行判断"任务已完成"。但 LLM 的判断本身是不确定的------同样的状态,不同的采样可能产生"继续"和"停止"两个相反的结论。更糟糕的是,当任务复杂时,LLM 倾向于过度谨慎:"我再检查一遍以确保万无一失。"

风险二:Thinking越来越长

随着循环次数增加,Thought 中包含的历史信息越来越多。每一轮 Thought 都在"回顾"之前的所有 Observation,导致 Thought 长度累加:

复制代码
第1轮:Thought (50 tokens)
第2轮:Thought (150 tokens) --- 回顾了第1轮的Observation
第3轮:Thought (300 tokens) --- 回顾了第1、2轮
第5轮:Thought (800 tokens) --- LLM花费大量token在"记忆"而非"推进"

这导致两个问题:(1)Token成本指数增长;(2)LLM在长上下文中推理质量下降。

风险三:Observation污染Context

这是最隐蔽也最致命的问题。每次 Tool 调用返回的 Observation 都会进入上下文。如果 Observation 中包含了大量无关信息(比如运行测试输出了300行日志,但只有最后5行是关键错误),这些噪声会:

  • 稀释有效信息
  • 误导 LLM 关注无关细节
  • 推动上下文总量迅速膨胀

Claude Code 的源码验证了这一点:每轮迭代结束时,tool_result 以 user 消息类型拼回对话,该消息会出现在下一轮 API 调用的完整上下文中。如果一个任务需要30轮 Tool 调用,Agent 的上下文会包含30组完整的 Action-Observation 对。

改进方向

Reflexion(Shinn et al., NeurIPS 2023)提出了"语言强化学习":Agent 在任务失败后,不是简单地重试,而是生成一段"反思"------分析为什么失败、下次应该怎么做。这段反思存储在长期记忆中,帮助后续尝试避开同样的陷阱。

Self-Refine(Madaan et al., 2023)则是让 LLM 对自己的输出进行"自我批评和改进",在同一个推理步骤内迭代优化,而非反复调用外部工具。

ReWOO(Reasoning WithOut Observation, Xu et al., 2023)提出了更激进的思路:把"推理"和"工具执行"彻底解耦。Agent 先一次性生成完整的"推理链+工具调用计划",然后批量执行所有工具,最后用工具结果一次性生成最终答案。这从根本上消灭了"每轮 Observation 都回到循环起点"的结构------循环只有一轮。

说白了,这些方法没有改掉 ReAct 的底层循环逻辑。它们只是帮 Agent 更聪明地喊停,而不是让循环本身消失。


五、上下文污染(Context Pollution)

为什么Agent越来越"笨"?

如果你长期使用 Claude Code 或 Cursor Agent,一定有过这样的体验:Agent 一开始表现很聪明,但随着对话推进,它越来越迟钝、越来越容易犯错、甚至在简单任务上也出错。这不是模型变差了,是上下文被污染了。

五种核心污染形式

#mermaid-svg-JMMWgG0UW7WTVFiL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JMMWgG0UW7WTVFiL .error-icon{fill:#552222;}#mermaid-svg-JMMWgG0UW7WTVFiL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JMMWgG0UW7WTVFiL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JMMWgG0UW7WTVFiL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JMMWgG0UW7WTVFiL .marker.cross{stroke:#333333;}#mermaid-svg-JMMWgG0UW7WTVFiL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JMMWgG0UW7WTVFiL p{margin:0;}#mermaid-svg-JMMWgG0UW7WTVFiL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL .cluster-label text{fill:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL .cluster-label span{color:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL .cluster-label span p{background-color:transparent;}#mermaid-svg-JMMWgG0UW7WTVFiL .label text,#mermaid-svg-JMMWgG0UW7WTVFiL span{fill:#333;color:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL .node rect,#mermaid-svg-JMMWgG0UW7WTVFiL .node circle,#mermaid-svg-JMMWgG0UW7WTVFiL .node ellipse,#mermaid-svg-JMMWgG0UW7WTVFiL .node polygon,#mermaid-svg-JMMWgG0UW7WTVFiL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-JMMWgG0UW7WTVFiL .rough-node .label text,#mermaid-svg-JMMWgG0UW7WTVFiL .node .label text,#mermaid-svg-JMMWgG0UW7WTVFiL .image-shape .label,#mermaid-svg-JMMWgG0UW7WTVFiL .icon-shape .label{text-anchor:middle;}#mermaid-svg-JMMWgG0UW7WTVFiL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-JMMWgG0UW7WTVFiL .rough-node .label,#mermaid-svg-JMMWgG0UW7WTVFiL .node .label,#mermaid-svg-JMMWgG0UW7WTVFiL .image-shape .label,#mermaid-svg-JMMWgG0UW7WTVFiL .icon-shape .label{text-align:center;}#mermaid-svg-JMMWgG0UW7WTVFiL .node.clickable{cursor:pointer;}#mermaid-svg-JMMWgG0UW7WTVFiL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-JMMWgG0UW7WTVFiL .arrowheadPath{fill:#333333;}#mermaid-svg-JMMWgG0UW7WTVFiL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-JMMWgG0UW7WTVFiL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-JMMWgG0UW7WTVFiL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JMMWgG0UW7WTVFiL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-JMMWgG0UW7WTVFiL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JMMWgG0UW7WTVFiL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-JMMWgG0UW7WTVFiL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-JMMWgG0UW7WTVFiL .cluster text{fill:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL .cluster span{color:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-JMMWgG0UW7WTVFiL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-JMMWgG0UW7WTVFiL rect.text{fill:none;stroke-width:0;}#mermaid-svg-JMMWgG0UW7WTVFiL .icon-shape,#mermaid-svg-JMMWgG0UW7WTVFiL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-JMMWgG0UW7WTVFiL .icon-shape p,#mermaid-svg-JMMWgG0UW7WTVFiL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-JMMWgG0UW7WTVFiL .icon-shape .label rect,#mermaid-svg-JMMWgG0UW7WTVFiL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-JMMWgG0UW7WTVFiL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-JMMWgG0UW7WTVFiL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-JMMWgG0UW7WTVFiL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 上下文窗口
📝 历史Observation

越来越多
☠️ 上下文污染
❌ 错误信息

反复留存
🔄 重复Tool输出

占据空间
📏 Prompt膨胀

Token爆炸
💾 Memory碎片

相互矛盾
📉 推理质量持续下降

Context Drift(上下文漂移)

2026年1月,一篇题为 "Agent Drift: Quantifying Behavioral Degradation in Multi-Agent LLM Systems" 的论文(arXiv:2601.04170)量化了这个现象。研究发现:

  • 任务成功率从 87.3% 下降到 50.6%(-42.0%),仅因为长时间交互
  • 人工干预需求从每任务 0.31 次暴增到 0.98 次(+216.1%)
  • 漂移在第 73 次交互时开始出现,到第 500 次交互后加速恶化

论文提出了三种漂移类型:

漂移类型 表现 示例
语义漂移 输出逐渐偏离原始任务意图 金融分析 Agent 从风险导向变成机会导向
协调漂移 多 Agent 间共识崩溃 路由 Agent 开始偏袒特定子 Agent
行为漂移 发展出初始交互中不存在的策略 合规 Agent 开始在对话历史中缓存中间结果

Context Pollution(上下文污染)

在 Agent 实际运行中,上下文窗口是一个有限资源。每次 Tool 调用的输出都会挤占这个空间。污染的来源包括:

  1. 历史 Observation 堆积:每轮循环都在上下文里留下一份 Observation。30轮过后,20%的上下文可能都是历史记录。

  2. 错误信息越来越多:每次失败的尝试都在上下文里留下错误日志。这些错误日志不仅占空间,还会误导 LLM:"上次 error 是 X,让我看看是不是又发生了 X。"

  3. 重复 Tool 输出 :当 Agent 多次调用同一个 Tool(比如反复 ls 同一个目录),重复的输出不仅浪费 Token,还制造了"回声效应"------LLM 看到的上下文中有大量相似内容。

  4. Prompt Injections 累积:Tool 输出可能包含不可信的外部内容(网页抓取、API 响应等),这些内容本质上是对 Agent 的一种"间接注入",可能改变 Agent 的行为方向。

Claude Code 的验证

Claude Code 源码分析(query.ts)证实,它的每次 API 调用都在处理不断增长的上下文。为了应对这个问题,它设计了六层上下文管理机制

层级 机制 激进程度
1 Snip 保守 --- 删除过时 Tool 消息
2 Microcompact 保守 --- 编辑 API cache key
3 Context Collapse 中等 --- 折叠旧消息为摘要行
4 Auto Compact 激进 --- Fork Agent 做摘要压缩
5 Reactive Compact 激进 --- API 413 错误后的紧急压缩
6 Manual Compact 激进 --- 用户触发 /compact

其中最值得关注的是 Auto Compact :当 Token 用量接近上限时,系统启动一个 fork 子 Agent 来总结历史对话。为了防止这项操作本身陷入死循环,它还设计了熔断器:连续失败3次后自动停止。

Memory Pollution(记忆污染)

在多轮交互中,Agent 可能形成"错误记忆":比如第一次调用 API 返回了 rate_limit 错误,Agent 可能在后续所有 API 调用前都加上了"请确保不要触发频率限制"的逻辑,即使这个限制早已被解除。这是 Memory 变成了束缚


六、目前业界如何解决死循环?(横向对比)

在研究各框架的源码和文档后,我梳理了16种通用的防死循环机制,以及它们在主流框架中的支持情况:

机制 Claude Code OpenAI Agents LangGraph OpenHands AutoGen CrewAI Google ADK Mastra LlamaIndex
Max Iteration / Turn
Tool Budget
Cost Budget
Timeout
Loop Detection
Reflection
Human Approval
Guardrails
Stop Condition
State Machine
Checkpoint/Persistence
Context Compression
Memory Summary
Confidence Score
Retry Policy
Sub-agent Isolation

注:✅ = 明确支持,❌ = 未在公开文档/源码中找到支持证据。"官方未公开具体实现"的标记为 ❌,不做事实性断言。

各机制的优缺点分析

Max Iteration / Turn --- 最简单也最常用

原理:设置最大循环次数,达到后强制终止。

优点 :零心智负担,一行配置。

缺点:是"暴力截断",任务可能未完成就被终止。

LangGraph 的做法更为优雅:它提供了 RemainingSteps 托管值,允许 Node 在达到限制前 主动感知剩余步数并优雅收尾,而不是等 GraphRecursionError 抛出来再处理。

Loop Detection --- OpenHands 的 StuckDetector

原理:通过模式识别检测重复行为。

OpenHands 的 StuckDetector 可以识别5种卡死模式,这是目前开源框架里唯一的系统化死循环检测方案。

优点 :能发现"表层看起来不同但本质重复"的循环。

缺点:检测滞后,需要循环发生几次后才能发现。

Human Approval --- 人的判断是最终的防线

原理:在关键节点暂停执行,等待人工批准。

Claude Code 的权限模式系统(permission_mode)允许开发者设置 defaultacceptEditsplandontAskbypassPermissions 等多种审批级别。AutoGen 的 human_input_mode 可以设置为 NEVERALWAYSTERMINATE

优点 :人能识别 LLM 无法识别的"无意义循环"。

缺点:打断执行流,需要人实时在线。

Guardrails --- 预防而非治疗

OpenAI Agents SDK 的 Guardrails 分为 Input 和 Output 两类:Input Guardrails 在输入阶段拦截问题,Output Guardrails 在最终输出阶段验证。但只有第一个 Agent 的 Input Guardrails 会被执行,后续 Handoff 的 Agent 不会再次触发------这是一个容易被忽视的安全边界。

Context Compression --- Claude Code 独有的深度机制

Claude Code 的六层上下文管理是目前市面上最成熟的方案。它的 Auto Compact 不是简单的截断,而是启动一个子 Agent 来"读懂并总结"历史对话。但这个机制本身也是死循环的来源------GitHub Issue #6004 就是压缩循环本身变成了问题。

Cost Budget --- 经济上的防护栏

Claude Code 的 max_budget_usd 是基于花费阈值来限制循环的:一旦 API 调用费用达到预算,循环停止。这是一个非常实用的"商业化思维"------用钱来控制风险,而非用技术手段猜测风险。


第六章展示了各框架的共同防线,但 LangGraph 的防死循环策略有本质不同------它不是靠限制次数,而是靠状态机消除循环本身。


七、Claude Code为什么很少死循环?

很多深度使用过 Claude Code 和 Cursor 的开发者都有一个共同感受:Claude Code 虽然偶尔会出问题,但整体稳定性远超同类工具。 原因不在于某个单独的魔法,而是一套精心设计的工程体系。

1. 多层上下文管理的"瑞士军刀"

如前所述,Claude Code 的六层上下文管理(Snip → Microcompact → Collapse → Auto Compact → Reactive Compact → Manual Compact)确保上下文不会无限膨胀。这解决了循环的最底层诱因------上下文污染导致判断力下降 → 判断力下降导致重复决策 → 重复决策导致更多上下文污染。

2. Sub-agent 隔离 --- 每个子任务都重新开始

这是 Claude Code 防循环效果最明显的机制之一。当 Agent 需要执行子任务时,它会 spawn 一个子 Agent。这个子 Agent 拥有全新的上下文,不继承父 Agent 的对话历史。 它完成任务后只将最终结果返回给父 Agent------父 Agent 看到的只是一条简洁的结果摘要,而不是子 Agent 的完整执行过程。
#mermaid-svg-FqBrDjvs9K6rDZHC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FqBrDjvs9K6rDZHC .error-icon{fill:#552222;}#mermaid-svg-FqBrDjvs9K6rDZHC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FqBrDjvs9K6rDZHC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FqBrDjvs9K6rDZHC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FqBrDjvs9K6rDZHC .marker.cross{stroke:#333333;}#mermaid-svg-FqBrDjvs9K6rDZHC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FqBrDjvs9K6rDZHC p{margin:0;}#mermaid-svg-FqBrDjvs9K6rDZHC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC .cluster-label text{fill:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC .cluster-label span{color:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC .cluster-label span p{background-color:transparent;}#mermaid-svg-FqBrDjvs9K6rDZHC .label text,#mermaid-svg-FqBrDjvs9K6rDZHC span{fill:#333;color:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC .node rect,#mermaid-svg-FqBrDjvs9K6rDZHC .node circle,#mermaid-svg-FqBrDjvs9K6rDZHC .node ellipse,#mermaid-svg-FqBrDjvs9K6rDZHC .node polygon,#mermaid-svg-FqBrDjvs9K6rDZHC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FqBrDjvs9K6rDZHC .rough-node .label text,#mermaid-svg-FqBrDjvs9K6rDZHC .node .label text,#mermaid-svg-FqBrDjvs9K6rDZHC .image-shape .label,#mermaid-svg-FqBrDjvs9K6rDZHC .icon-shape .label{text-anchor:middle;}#mermaid-svg-FqBrDjvs9K6rDZHC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FqBrDjvs9K6rDZHC .rough-node .label,#mermaid-svg-FqBrDjvs9K6rDZHC .node .label,#mermaid-svg-FqBrDjvs9K6rDZHC .image-shape .label,#mermaid-svg-FqBrDjvs9K6rDZHC .icon-shape .label{text-align:center;}#mermaid-svg-FqBrDjvs9K6rDZHC .node.clickable{cursor:pointer;}#mermaid-svg-FqBrDjvs9K6rDZHC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FqBrDjvs9K6rDZHC .arrowheadPath{fill:#333333;}#mermaid-svg-FqBrDjvs9K6rDZHC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FqBrDjvs9K6rDZHC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FqBrDjvs9K6rDZHC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FqBrDjvs9K6rDZHC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FqBrDjvs9K6rDZHC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FqBrDjvs9K6rDZHC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FqBrDjvs9K6rDZHC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FqBrDjvs9K6rDZHC .cluster text{fill:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC .cluster span{color:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FqBrDjvs9K6rDZHC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FqBrDjvs9K6rDZHC rect.text{fill:none;stroke-width:0;}#mermaid-svg-FqBrDjvs9K6rDZHC .icon-shape,#mermaid-svg-FqBrDjvs9K6rDZHC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FqBrDjvs9K6rDZHC .icon-shape p,#mermaid-svg-FqBrDjvs9K6rDZHC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FqBrDjvs9K6rDZHC .icon-shape .label rect,#mermaid-svg-FqBrDjvs9K6rDZHC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FqBrDjvs9K6rDZHC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FqBrDjvs9K6rDZHC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FqBrDjvs9K6rDZHC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Sub-agent 2 上下文
Sub-agent 1 上下文
父Agent上下文
用户查询
分析任务
Spawn Sub-agent 1
收到摘要1
Spawn Sub-agent 2
收到摘要2
输出最终结果
新上下文
执行任务
返回摘要
新上下文
执行任务
返回摘要

效果:父 Agent 不会因为子任务的细节而污染上下文。即使子 Agent 内部出问题(比如某个文件反复读取),这个"局部混乱"不会扩散到全局。

3. 为什么大量使用 Markdown?

Claude Code 的核心配置通过 CLAUDE.md 文件进行。这不是巧合------Markdown 是一种结构化但自然的格式,LLM 对它理解得最好。在 CLAUDE.md 中写入的规则和项目上下文,会在每次 API 请求中被重新注入 。这意味着即使 Auto Compact 压缩了历史对话,CLAUDE.md 中的关键指令也不会丢失。

这一点和普通对话完全不同:你在对话中说的"记住 XYZ"可能被压缩掉,但写在 CLAUDE.md 里的指令是永久的。

4. 为什么强调 Project Context?

Claude Code 的 CLAUDE.md 支持多层级配置:全局级别(~/.claude/CLAUDE.md)和项目级别(项目根目录的 CLAUDE.md)。这种设计在启动时就将"你是谁、你要做什么、你的边界在哪里"编码进 System Prompt------在 Agent "自由发挥"之前先设好护栏。

5. 工具限制的精细化

Claude Code 的权限系统提供了5种模式,并且支持对单个工具进行作用域限制:

复制代码
# 只允许运行 npm 相关的 Bash 命令
"Bash(npm *)"

# 允许特定工具
allowed_tools: ["Read", "Glob", "Grep"]

权限越少,出错空间越小。 一个只有 ReadGlobGrep 权限的 Agent 几乎不可能死循环------它没有可以重复调用的"写操作"。

6. 任务拆分与原子化

Claude Code 的 Task 系统(TaskCreate、TaskUpdate、TaskList)强制任务分解。当一个大型任务被拆成10个小任务时,每个小任务都有明确的"完成标准"。这天然限制了每个步骤的循环次数------你不可能在"安装 psycopg2"这个任务上循环100次而不自知。

7. Token Budget 和边际效应检测

Claude Code 的 Token Budget 系统(query/tokenBudget.ts)不仅能追踪 Token 消耗,还能检测边际效应递减------当模型在后续轮次中产出越来越少时,它会提前终止而不是继续"磨洋工"。这是一个非常工程化的"智能止损"机制。

关于 Loop Protection 的官方说明

Claude Code 的 Agent SDK 文档明确列出了 max_turnsmax_budget_usdstop_reason 作为终止机制。但官方未公开是否存在专门的"循环检测"算法(如 OpenHands 式的模式匹配)。 从 GitHub Issues 来看(#6004、#2283、#2391等),Claude Code 确实存在循环问题,所以不能说它"完全免疫"------它的优势在于让循环发生的概率大幅降低,并在发生时能较快恢复。


八、LangGraph如何避免死循环?

LangGraph 的防死循环策略与其他所有框架有本质区别。大多数框架的做法是"在 while 循环里加限制",LangGraph 的做法是"不用 while 循环,用状态机。"

核心设计哲学

#mermaid-svg-fI7v6GBTHWHcrZkg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fI7v6GBTHWHcrZkg .error-icon{fill:#552222;}#mermaid-svg-fI7v6GBTHWHcrZkg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fI7v6GBTHWHcrZkg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fI7v6GBTHWHcrZkg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fI7v6GBTHWHcrZkg .marker.cross{stroke:#333333;}#mermaid-svg-fI7v6GBTHWHcrZkg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fI7v6GBTHWHcrZkg p{margin:0;}#mermaid-svg-fI7v6GBTHWHcrZkg .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg .cluster-label text{fill:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg .cluster-label span{color:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg .cluster-label span p{background-color:transparent;}#mermaid-svg-fI7v6GBTHWHcrZkg .label text,#mermaid-svg-fI7v6GBTHWHcrZkg span{fill:#333;color:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg .node rect,#mermaid-svg-fI7v6GBTHWHcrZkg .node circle,#mermaid-svg-fI7v6GBTHWHcrZkg .node ellipse,#mermaid-svg-fI7v6GBTHWHcrZkg .node polygon,#mermaid-svg-fI7v6GBTHWHcrZkg .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fI7v6GBTHWHcrZkg .rough-node .label text,#mermaid-svg-fI7v6GBTHWHcrZkg .node .label text,#mermaid-svg-fI7v6GBTHWHcrZkg .image-shape .label,#mermaid-svg-fI7v6GBTHWHcrZkg .icon-shape .label{text-anchor:middle;}#mermaid-svg-fI7v6GBTHWHcrZkg .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fI7v6GBTHWHcrZkg .rough-node .label,#mermaid-svg-fI7v6GBTHWHcrZkg .node .label,#mermaid-svg-fI7v6GBTHWHcrZkg .image-shape .label,#mermaid-svg-fI7v6GBTHWHcrZkg .icon-shape .label{text-align:center;}#mermaid-svg-fI7v6GBTHWHcrZkg .node.clickable{cursor:pointer;}#mermaid-svg-fI7v6GBTHWHcrZkg .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fI7v6GBTHWHcrZkg .arrowheadPath{fill:#333333;}#mermaid-svg-fI7v6GBTHWHcrZkg .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fI7v6GBTHWHcrZkg .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fI7v6GBTHWHcrZkg .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fI7v6GBTHWHcrZkg .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fI7v6GBTHWHcrZkg .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fI7v6GBTHWHcrZkg .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fI7v6GBTHWHcrZkg .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fI7v6GBTHWHcrZkg .cluster text{fill:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg .cluster span{color:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fI7v6GBTHWHcrZkg .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fI7v6GBTHWHcrZkg rect.text{fill:none;stroke-width:0;}#mermaid-svg-fI7v6GBTHWHcrZkg .icon-shape,#mermaid-svg-fI7v6GBTHWHcrZkg .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fI7v6GBTHWHcrZkg .icon-shape p,#mermaid-svg-fI7v6GBTHWHcrZkg .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fI7v6GBTHWHcrZkg .icon-shape .label rect,#mermaid-svg-fI7v6GBTHWHcrZkg .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fI7v6GBTHWHcrZkg .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fI7v6GBTHWHcrZkg .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fI7v6GBTHWHcrZkg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LangGraph
Conditional Edge
Conditional Edge
Conditional Edge
Node A
Node B
END
传统Agent
while True
Reason
Tool
Observe

传统 Agent 的结构是循环+条件判断

python 复制代码
while True:
    thought = llm.reason(context)
    if thought.is_final:
        break
    tool_result = execute(thought.action)
    context.append(tool_result)

LangGraph 的结构是图+状态驱动的条件跳转

python 复制代码
builder.add_conditional_edges(
    "reasoning_node",
    decide_next,  # 基于 State 决定下一步
    {
        "need_tool": "tool_node",
        "done": END      # ← 明确的终止节点
    }
)

核心区别 :在传统循环中,终止条件是一个隐式的 LLM 判断("我觉得我完成了")。在 LangGraph 中,终止是一个显式的路由决策,基于结构化 State,而非 LLM 的自然语言输出。

State驱动的条件跳转

LangGraph 的 State 是 TypedDict,每个字段都有明确的类型和 reducer:

python 复制代码
class AgentState(TypedDict):
    messages: Annotated[list, add_messages]
    task_completed: bool          # 显式的完成标志
    tool_call_count: int          # 工具调用计数
    remaining_steps: RemainingSteps  # 剩余步数(由框架管理)

条件边路由函数基于 State 做决策,而非 LLM 的文本输出:

python 复制代码
def decide_next(state: AgentState) -> str:
    if state["task_completed"]:
        return END
    if state["tool_call_count"] >= 10:
        return "force_summary"   # 超过限制 → 强制走收尾节点
    if len(state["messages"]) > 50:
        return "compress_context" # 上下文过长 → 压缩后继续
    return "continue_reasoning"

为什么这比 while 循环好? 因为 while 循环的终止条件是 LLM 的"意图"(我是否想停),而 LangGraph 的终止条件是 State 的"事实"(计数器到了、标志位设了)。前者是不可靠的,后者是确定性的。

Conditional Edge --- 让流程天生有终点

#mermaid-svg-xHMigrNQYQCG9QC4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xHMigrNQYQCG9QC4 .error-icon{fill:#552222;}#mermaid-svg-xHMigrNQYQCG9QC4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xHMigrNQYQCG9QC4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xHMigrNQYQCG9QC4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xHMigrNQYQCG9QC4 .marker.cross{stroke:#333333;}#mermaid-svg-xHMigrNQYQCG9QC4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xHMigrNQYQCG9QC4 p{margin:0;}#mermaid-svg-xHMigrNQYQCG9QC4 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 .cluster-label text{fill:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 .cluster-label span{color:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 .cluster-label span p{background-color:transparent;}#mermaid-svg-xHMigrNQYQCG9QC4 .label text,#mermaid-svg-xHMigrNQYQCG9QC4 span{fill:#333;color:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 .node rect,#mermaid-svg-xHMigrNQYQCG9QC4 .node circle,#mermaid-svg-xHMigrNQYQCG9QC4 .node ellipse,#mermaid-svg-xHMigrNQYQCG9QC4 .node polygon,#mermaid-svg-xHMigrNQYQCG9QC4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xHMigrNQYQCG9QC4 .rough-node .label text,#mermaid-svg-xHMigrNQYQCG9QC4 .node .label text,#mermaid-svg-xHMigrNQYQCG9QC4 .image-shape .label,#mermaid-svg-xHMigrNQYQCG9QC4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-xHMigrNQYQCG9QC4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xHMigrNQYQCG9QC4 .rough-node .label,#mermaid-svg-xHMigrNQYQCG9QC4 .node .label,#mermaid-svg-xHMigrNQYQCG9QC4 .image-shape .label,#mermaid-svg-xHMigrNQYQCG9QC4 .icon-shape .label{text-align:center;}#mermaid-svg-xHMigrNQYQCG9QC4 .node.clickable{cursor:pointer;}#mermaid-svg-xHMigrNQYQCG9QC4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xHMigrNQYQCG9QC4 .arrowheadPath{fill:#333333;}#mermaid-svg-xHMigrNQYQCG9QC4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xHMigrNQYQCG9QC4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xHMigrNQYQCG9QC4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xHMigrNQYQCG9QC4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xHMigrNQYQCG9QC4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xHMigrNQYQCG9QC4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xHMigrNQYQCG9QC4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xHMigrNQYQCG9QC4 .cluster text{fill:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 .cluster span{color:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xHMigrNQYQCG9QC4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xHMigrNQYQCG9QC4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-xHMigrNQYQCG9QC4 .icon-shape,#mermaid-svg-xHMigrNQYQCG9QC4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xHMigrNQYQCG9QC4 .icon-shape p,#mermaid-svg-xHMigrNQYQCG9QC4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xHMigrNQYQCG9QC4 .icon-shape .label rect,#mermaid-svg-xHMigrNQYQCG9QC4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xHMigrNQYQCG9QC4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xHMigrNQYQCG9QC4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xHMigrNQYQCG9QC4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} need_tool
done
max_steps
START
reasoning
tool_executor
END
observation_parser
force_finish

传统 while 循环的问题是:每一轮循环的终点都是下一轮循环的起点 。LangGraph 的图的问题是:每个 Node 都可以跳转到 END。 这不是语义差异,而是工程差异------在图结构中,END 是一个"物理存在"的节点,你可以通过多种路径到达它。

Interrupt / Checkpoint / Human-in-the-loop

这三者结合起来,形成了 LangGraph 最有用的一套安全机制------把人嵌入执行流。

python 复制代码
def potentially_dangerous_action(state):
    # 在执行前暂停,等待人工确认
    approval = interrupt(f"即将执行: {state['planned_action']},是否继续?")
    if approval != "yes":
        return Command(goto="abort")
    result = execute_action()
    return {"result": result}

interrupt() 会在该点暂停整个图的执行,保存一个 Checkpoint。人可以在任意时间后回来,检查当前状态,然后通过 Command(resume=...) 恢复执行。

Checkpoint 的额外价值:如果图在执行到第47步时崩溃了,重启后它不会从第1步重新开始------它从第46步的 Checkpoint 恢复。这意味着即使在异常情况下,Agent 也不会"失去进度"然后从头来过(这是一类常见的变相循环)。

Command / Send --- 动态路由

Command 允许 Node 在一次返回中同时完成"状态更新"和"控制流决策":

python 复制代码
def tool_executor(state) -> Command[Literal["reasoning", "error_handler"]]:
    try:
        result = run_tool(state["pending_action"])
        return Command(update={"result": result}, goto="reasoning")
    except ToolError as e:
        return Command(update={"error": str(e)}, goto="error_handler")

Send 实现动态并行------当你不确定需要并行执行多少个任务时:

python 复制代码
def fan_out(state):
    return [Send("sub_worker", {"task": t}) for t in state["tasks"]]

Recursion Limit --- 最后的防线

LangGraph 的 recursion_limit 相当于图中的"天花板"。每个 super-step 计数一次,默认值设计为适合复杂图的合理上限。当图达到限制时,触发 GraphRecursionError

但与传统的 max_turns 不同,LangGraph 还提供了 RemainingSteps------一个托管值,在图的内部被更新,节点可以主动读取:

python 复制代码
def my_node(state):
    if state["remaining_steps"] < 3:
        # 步数不多了,快速收尾
        return {"summary": quick_wrap_up(state)}
    # 正常执行
    return {"result": do_work(state)}

这是"主动优雅降级"而非"被动崩溃"。

为什么LangGraph推荐显式状态机而非自由循环?

  1. 可预测性:图的拓扑结构在编译时就确定了所有可能的执行路径。
  2. 可测试性:每个 Node 是纯函数(State in → State out),容易单独测试。
  3. 可观测性:每次状态转换都经过明确的 Edge,可以打点、记录、追踪。
  4. 可恢复性:Checkpoint 保存了整个 State,恢复后可以精确续接。
  5. 安全性interrupt() 在任何危险步骤前可以强制暂停。

九、如何设计不会死循环的Agent?

这一章不讲理论,只讲工程实践。

1. 限制最大步骤数 --- 最基础但最有效【必做】

python 复制代码
# Claude Code Agent SDK
agent.run(prompt, max_turns=25)

# OpenAI Agents SDK
Runner.run(agent, input, max_turns=20)

# LangGraph
graph.invoke(input, config={"recursion_limit": 50})

为什么有效? 因为"无限"是人类直觉中最大的漏洞。我们总觉得"Agent 应该能自己判断何时停止",但 LLM 的"判断"是一个概率采样过程,不是确定性的逻辑。设置硬上限就是承认这一点。

建议值:简单任务 5-10 步,中等任务 15-25 步,复杂任务 30-50 步。超过 50 步的任务应该拆分成多个子任务。

2. 限制工具调用次数 --- 比限制步骤更精确【必做】

限制步骤数是粗略的(一步可能包含多个工具调用),限制工具调用次数更直接:

python 复制代码
if tool_call_count >= 15:
    return "请基于已有信息给出最佳答案,不要再调用工具。"

为什么有效? 很多死循环是"一直在查、一直在搜、一直在读文件,但从不开始真正的输出"。限制工具调用次数就是在强制 Agent 从"收集模式"切换到"产出模式"。

3. 增加 Reflection 节点 --- 但不要让它循环【推荐】

在每个重要阶段后插入一个"反思步骤":

python 复制代码
def reflection_node(state):
    reflection = llm.generate(
        f"已完成的操作:{state['completed_actions']}"
        f"当前状态:{state['status']}"
        f"原始目标:{state['original_goal']}"
        "判断:任务是否已完成?如果是,输出 DONE;如果否,输出需要继续的下一步。"
    )
    if "DONE" in reflection:
        return {"status": "completed"}
    return {"next_action": parse_next_action(reflection)}

关键设计:Reflection 的输出应该是结构化的(DONE / CONTINUE),而不是让 LLM 自由决定。自由决定 = 可能永远不说 DONE。

4. 增加 Task Success 判断 --- 二进制的完成标准【推荐】

与其让 Agent 判断"我是不是做完了",不如给它一个可以自动验证的完成条件:

python 复制代码
task = {
    "goal": "修复 auth.py 中的类型错误",
    "success_criteria": "python -m mypy auth.py  -- 零错误",
    "max_attempts": 5
}

Cursor 的文档中强调:"Without a test or command it cannot tell it is done." 这是一个非常精准的总结。给 Agent 一个它可以运行的验证命令,让它自己检查自己。

5. 增加 Budget --- 用钱控制行为【必做】

python 复制代码
# 方式1:Token Budget
if total_tokens > 100_000:
    return force_conclusion()

# 方式2:Cost Budget(Claude Code 方式)
agent.run(prompt, max_budget_usd=2.00)

# 方式3:Time Budget
if elapsed_time > 300:  # 5分钟
    return force_conclusion()

为什么用钱? 因为技术指标(步数、Token 数)LLM 不一定理解其意义。但"这个任务只值 $2"是一个 LLM 能理解的商业约束------它会在预算内做出最优决策。

6. 减少 Observation 污染 --- 只保留有效信息【必做】

python 复制代码
def compress_observation(raw_output):
    """压缩 Tool 输出,只保留关键信息"""
    if len(raw_output) > 1000:
        return f"[工具输出过长,已压缩] 关键发现:{extract_key_info(raw_output)}"
    return raw_output

Claude Code 的 Snip 和 Collapse 机制就是做这件事。实战建议:

  • 测试输出:只保留失败的部分,截断成功的日志
  • 文件内容:只保留修改过的区域前后5行
  • 网页抓取:只保留和问题相关的段落

7. Memory 压缩 --- 有选择地遗忘【推荐】

python 复制代码
def compress_memory(messages, max_size=8000):
    """压缩对话历史"""
    # 始终保留:
    #   1. 原始任务描述
    #   2. 最近的3轮交互
    #   3. 已确认的关键发现(结构化存储)
    # 可以被压缩的:
    #   1. 中间的完整 Tool 输出
    #   2. 失败的尝试
    #   3. 中间的 Thought 过程

8. Context Summary --- 定期"重新对齐"【推荐】

每隔 10-15 步,生成一个当前状态的结构化摘要,把它作为新的起点:

python 复制代码
summary = {
    "original_goal": "...",
    "completed": ["步骤1", "步骤2", "步骤3"],
    "current_state": "正在处理步骤4,已读取3个文件...",
    "key_findings": ["发现X", "确认Y"],
    "remaining": ["步骤4", "步骤5", "步骤6"]
}
# 将 summary 替代之前的完整历史,减少上下文负担

9. 状态机设计 --- 消灭自由循环【进阶】

#mermaid-svg-W0HGzk24gTs8VgYY{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W0HGzk24gTs8VgYY .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W0HGzk24gTs8VgYY .error-icon{fill:#552222;}#mermaid-svg-W0HGzk24gTs8VgYY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W0HGzk24gTs8VgYY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W0HGzk24gTs8VgYY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W0HGzk24gTs8VgYY .marker.cross{stroke:#333333;}#mermaid-svg-W0HGzk24gTs8VgYY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W0HGzk24gTs8VgYY p{margin:0;}#mermaid-svg-W0HGzk24gTs8VgYY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W0HGzk24gTs8VgYY .cluster-label text{fill:#333;}#mermaid-svg-W0HGzk24gTs8VgYY .cluster-label span{color:#333;}#mermaid-svg-W0HGzk24gTs8VgYY .cluster-label span p{background-color:transparent;}#mermaid-svg-W0HGzk24gTs8VgYY .label text,#mermaid-svg-W0HGzk24gTs8VgYY span{fill:#333;color:#333;}#mermaid-svg-W0HGzk24gTs8VgYY .node rect,#mermaid-svg-W0HGzk24gTs8VgYY .node circle,#mermaid-svg-W0HGzk24gTs8VgYY .node ellipse,#mermaid-svg-W0HGzk24gTs8VgYY .node polygon,#mermaid-svg-W0HGzk24gTs8VgYY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W0HGzk24gTs8VgYY .rough-node .label text,#mermaid-svg-W0HGzk24gTs8VgYY .node .label text,#mermaid-svg-W0HGzk24gTs8VgYY .image-shape .label,#mermaid-svg-W0HGzk24gTs8VgYY .icon-shape .label{text-anchor:middle;}#mermaid-svg-W0HGzk24gTs8VgYY .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W0HGzk24gTs8VgYY .rough-node .label,#mermaid-svg-W0HGzk24gTs8VgYY .node .label,#mermaid-svg-W0HGzk24gTs8VgYY .image-shape .label,#mermaid-svg-W0HGzk24gTs8VgYY .icon-shape .label{text-align:center;}#mermaid-svg-W0HGzk24gTs8VgYY .node.clickable{cursor:pointer;}#mermaid-svg-W0HGzk24gTs8VgYY .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W0HGzk24gTs8VgYY .arrowheadPath{fill:#333333;}#mermaid-svg-W0HGzk24gTs8VgYY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W0HGzk24gTs8VgYY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W0HGzk24gTs8VgYY .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W0HGzk24gTs8VgYY .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W0HGzk24gTs8VgYY .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W0HGzk24gTs8VgYY .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W0HGzk24gTs8VgYY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W0HGzk24gTs8VgYY .cluster text{fill:#333;}#mermaid-svg-W0HGzk24gTs8VgYY .cluster span{color:#333;}#mermaid-svg-W0HGzk24gTs8VgYY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W0HGzk24gTs8VgYY .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W0HGzk24gTs8VgYY rect.text{fill:none;stroke-width:0;}#mermaid-svg-W0HGzk24gTs8VgYY .icon-shape,#mermaid-svg-W0HGzk24gTs8VgYY .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W0HGzk24gTs8VgYY .icon-shape p,#mermaid-svg-W0HGzk24gTs8VgYY .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W0HGzk24gTs8VgYY .icon-shape .label rect,#mermaid-svg-W0HGzk24gTs8VgYY .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W0HGzk24gTs8VgYY .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W0HGzk24gTs8VgYY .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W0HGzk24gTs8VgYY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过
失败




开始
分析任务
制定计划
执行步骤
验证结果
还有步骤?
重试次数<3?
报告失败
生成摘要
结束

状态机的好处:每个状态都有明确的后继状态,没有"自由发挥"的空间。 Agent 在 "EXECUTE" 状态下就是执行,在 "VERIFY" 状态下就是验证------它不能"在执行的同时又在思考要不要继续执行"。

10. 人工审批节点 --- 关键操作前必须确认【必做】

python 复制代码
dangerous_actions = [
    "删除文件",
    "修改数据库 Schema",
    "执行 git push --force",
    "超过 $1 的 API 调用",
    "修改超过 5 个文件"
]

if action in dangerous_actions:
    await human_approval(action)

不要让人审批所有操作 (那还不如自己写)。只在不可逆操作高频操作的阈值上设置审批。


十、未来Agent为什么一定会走向"可控执行"

从"自主探索"到"确定性执行"的范式转移

2026年的 Agent 领域正在经历一个微妙的转变:开发者不再追求"让 Agent 自己想办法",而是追求"给 Agent 划定清晰的执行边界"。

这种转变体现在几个关键趋势上:

Deterministic Workflow + Agentic Intelligence

Google ADK 2.0 的口号是 "Weave deterministic code with adaptive AI reasoning"。Mastra 强调 "Use models where you need reasoning, plain functions where you don't"。这两者指向同一方向:在确定性的工作流框架中,在需要判断的节点上调用 LLM。
#mermaid-svg-gIdHOW4Yn7sSOlIC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gIdHOW4Yn7sSOlIC .error-icon{fill:#552222;}#mermaid-svg-gIdHOW4Yn7sSOlIC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gIdHOW4Yn7sSOlIC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .marker.cross{stroke:#333333;}#mermaid-svg-gIdHOW4Yn7sSOlIC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gIdHOW4Yn7sSOlIC p{margin:0;}#mermaid-svg-gIdHOW4Yn7sSOlIC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .cluster-label text{fill:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .cluster-label span{color:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .cluster-label span p{background-color:transparent;}#mermaid-svg-gIdHOW4Yn7sSOlIC .label text,#mermaid-svg-gIdHOW4Yn7sSOlIC span{fill:#333;color:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .node rect,#mermaid-svg-gIdHOW4Yn7sSOlIC .node circle,#mermaid-svg-gIdHOW4Yn7sSOlIC .node ellipse,#mermaid-svg-gIdHOW4Yn7sSOlIC .node polygon,#mermaid-svg-gIdHOW4Yn7sSOlIC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .rough-node .label text,#mermaid-svg-gIdHOW4Yn7sSOlIC .node .label text,#mermaid-svg-gIdHOW4Yn7sSOlIC .image-shape .label,#mermaid-svg-gIdHOW4Yn7sSOlIC .icon-shape .label{text-anchor:middle;}#mermaid-svg-gIdHOW4Yn7sSOlIC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .rough-node .label,#mermaid-svg-gIdHOW4Yn7sSOlIC .node .label,#mermaid-svg-gIdHOW4Yn7sSOlIC .image-shape .label,#mermaid-svg-gIdHOW4Yn7sSOlIC .icon-shape .label{text-align:center;}#mermaid-svg-gIdHOW4Yn7sSOlIC .node.clickable{cursor:pointer;}#mermaid-svg-gIdHOW4Yn7sSOlIC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .arrowheadPath{fill:#333333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gIdHOW4Yn7sSOlIC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gIdHOW4Yn7sSOlIC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gIdHOW4Yn7sSOlIC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gIdHOW4Yn7sSOlIC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .cluster text{fill:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC .cluster span{color:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gIdHOW4Yn7sSOlIC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gIdHOW4Yn7sSOlIC rect.text{fill:none;stroke-width:0;}#mermaid-svg-gIdHOW4Yn7sSOlIC .icon-shape,#mermaid-svg-gIdHOW4Yn7sSOlIC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gIdHOW4Yn7sSOlIC .icon-shape p,#mermaid-svg-gIdHOW4Yn7sSOlIC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gIdHOW4Yn7sSOlIC .icon-shape .label rect,#mermaid-svg-gIdHOW4Yn7sSOlIC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gIdHOW4Yn7sSOlIC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gIdHOW4Yn7sSOlIC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gIdHOW4Yn7sSOlIC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Hybrid: 2026趋势
Step 1: 确定性
Step 2: LLM推理
Step 3: 确定性
Step 4: LLM决策
Step 5: 确定性
纯Agent
Reason
Tool
纯Workflow
Step 1
Step 2
Step 3

Planning + Verification

"Source Code Agent" 论文(arXiv:2508.02721, 2025)提出了一个激进的方案:将 Agent 的操作逻辑编码为确定性的源代码蓝图。 在执行前生成一个可审查的 Plan,Plan 通过后再执行。这本质上是在 Agent 的"黑盒推理"和"白盒执行"之间加了一层"可验证的蓝图层"。

不只是线性推理,未来的 Agent 会在关键决策点上展开搜索树------评估多个可能路径、选择最优、执行、验证、回溯。这需要比目前的 ReAct 循环更复杂的执行框架。

Model Context Protocol(MCP)

MCP 的核心价值不只是"统一工具调用协议",它还通过工具发现延迟加载 的方式减少了无效调用。Agent 不需要提前加载所有工具的定义,而是在需要时才通过 ToolSearch 查找------减少了上下文污染和"选错工具"的概率。

Hybrid Agent --- Rule + LLM

"规则引擎负责下限,LLM 负责上限"正在成为共识。一个生产级的 Agent 系统应该是:

复制代码
if is_simple_and_clear(task):
    use_rule_based_workflow(task)  # 确定、快速、零出错
else:
    use_llm_agent(task, constraints={
        "max_turns": 25,
        "human_approval_required": True,
        "verification_command": "pytest -x"
    })

总结

这篇文章的核心论点可以归纳为一句话:

Agent 的自主性是其能力的来源,也是其风险的来源。 你给 Agent 的自由度越大,它帮你完成复杂任务的潜力越大------但它陷入死循环的概率也越大。

所以设计 Agent 不是在"让它更聪明"和"让它更稳定"之间做选择。而是用工程手段来框定聪明的边界

具体来说:

  1. 硬限制:Max Turns、Tool Budget、Cost Budget------到了就停,不商量
  2. 状态机:StateGraph、条件边、显式终止节点------用图消灭循环本身
  3. 人肉兜底:Human-in-the-loop、审批节点------关键操作必须人点头
  4. 压缩:Context Compression、Memory Summary------把上下文里的噪声挤出去
  5. 隔离:Sub-agent 独立上下文、MCP 延迟加载------局部坏了不影响全局
  6. 检测:循环模式识别、Token 异常监控------发现苗头就掐掉

真正优秀的 Agent,不是最聪明的,而是最稳定的。

在工程中,"每次都做到 80 分"远比"有时候 100 分、有时候 0 分"更有价值。


参考资料:

  1. Anthropic. Claude Code Agent SDK Documentation --- Agent Loop. code.claude.com/docs, 2026.
  2. Anthropic. Claude Code GitHub Repository. github.com/anthropics/claude-code, 2025-2026.
  3. LangChain. LangGraph Documentation --- Graph API, GRAPH_RECURSION_LIMIT. docs.langchain.com, 2026.
  4. OpenAI. OpenAI Agents SDK Documentation --- Runner, Guardrails. openai.github.io/openai-agents-python, 2026.
  5. Google. Agent Development Kit (ADK) Documentation. adk.dev, 2026.
  6. OpenHands. OpenHands GitHub Repository --- StuckDetector. github.com/OpenHands/OpenHands, 2025-2026.
  7. Microsoft. AutoGen Documentation --- Chat Termination. microsoft.github.io/autogen, 2026.
  8. Yao, S. et al. ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629, 2022.
  9. Shinn, N. et al. Reflexion: Language Agents with Verbal Reinforcement Learning. NeurIPS 2023.
  10. Xu, B. et al. ReWOO: Decoupling Reasoning from Observations for Efficient Augmented Language Models. arXiv:2305.18323, 2023.
  11. Agent Drift: Quantifying Behavioral Degradation in Multi-Agent LLM Systems. arXiv:2601.04170, 2026.
  12. Mastra. Mastra AI Agent Framework Documentation. mastra.ai, 2026.
  13. CrewAI. CrewAI Documentation. docs.crewai.com, 2026.
  14. Source Code Agent: A Framework for Deterministic LLM Agents. arXiv:2508.02721, 2025.
  15. Yoyo_Lee. Claude Code 架构深度解析. yo666666yo.github.io, 2026.
  16. Learn Cursor. Cursor Agent Stuck in a Loop Fix. learncursor.dev, 2026.
  17. Ralphable. The Claude Code 'Infinite Loop' Bug. ralphable.com, 2026.
相关推荐
陈天伟教授2 小时前
FreeCAD 启动后小窗口闪现即退的解决思路
人工智能·机器人·工业设计
酒旅Agent开发实战2 小时前
AI 旅行规划助手如何接入真实酒旅数据:从自然语言到酒店预订的全流程 MCP 实战
人工智能·ai·旅游·skill·酒店api·机票api
workflower2 小时前
设备单元级(L1)实施路径
人工智能·线性代数·矩阵·机器人·开源
Dragon Wu2 小时前
ComfyUI Desktop 实例进入后一直loading的问题解决
人工智能·ai
“码”力全开2 小时前
AI视频分析飞书告警常见问题和排查清单
人工智能·音视频·飞书
leoZ2313 小时前
AI 辅助开发工具链 2026 版深度技术报告:从单点插件到全流程协同的范式重构
人工智能
hy95233 小时前
从零搭建生产级AI智能客服系统(七):基础优化与一键部署,打造开箱即用的生产级系统
人工智能
深度学习机器3 小时前
Ghostty终端使用体验
人工智能·命令行
Token炼金师3 小时前
幂律的预言:Kaplan 与 Chinchilla 的算力账本 —— Scaling Laws 与最优配比
人工智能·深度学习·大模型架构·kv cache·scaling laws