--- title: "Agent Loop" description: "理解 Claude Code 的核心循环机制——AI 如何自主决定工具调用、处理错误、管理上下文,直到任务完成。" keywords: ["Agentic Loop", "tool_use", "状态机", "auto-compact", "streaming"] --- ## 什么是 Agentic Loop 传统聊天机器人:你问一句,它答一句。 Claude Code 不一样:你说一个需求,它可能连续执行十几步操作才给你最终结果。 这背后的机制叫做 **Agentic Loop**(智能体循环)。它是一个"思考→行动→观察"的不断循环,直到任务完成或遇到终止条件。 Agentic Loop 循环图 ### 为什么需要循环而非一次回答 因为软件工程任务本质上是**探索性**的。AI 不可能在第一步就知道所有信息: - 它需要先读代码才能知道怎么改 - 它需要先运行命令才能知道结果 - 它需要先搜索才能找到相关文件 - 它需要先修改才能验证是否正确 每一步工具执行都产生**真实信息**——命令输出、文件内容、错误信息——这些是 AI 在执行前不可能预知的。因此,AI 必须在每一步后根据新信息重新决策。 ## 循环的四个阶段 每次循环迭代包含四个阶段,形成一个完整的"感知→决策→执行→反馈"周期。 ### 阶段一:上下文预处理 在调用 API 之前,系统会依次检查和处理上下文。这是一个串行管道,每一步的输出是下一步的输入: ``` 原始消息 → 工具结果截断(单条输出过长时截断) → 历史压缩(Snip 压缩旧消息) → 微压缩(工具结果摘要化) → 自动压缩(对话接近 token 上限时触发 AI 摘要) 处理后的消息 → 发往 API ``` **设计考量**:为什么是串行管道而非一次性处理?因为每个步骤释放的 token 数会影响下一步的决策。例如,如果 Snip 压缩已经释放了足够的 token,自动压缩就不需要触发了。 ### 阶段二:流式 API 调用 系统以流式方式调用 Claude API。流式传输不是"锦上添花"——它是核心设计决策: - **用户体验**:用户看到 AI 逐字输出,而非等待数秒后一次性显示 - **工具并行执行**:AI 在流式输出过程中就可能发出工具调用,系统可以立即开始执行,不必等流结束 - **可取消性**:用户随时可以中断正在进行的流式响应 ### 阶段三:工具执行 如果 AI 请求了工具调用,系统执行工具并将结果回传。这里有两个关键设计: **并行执行**:当 AI 在一次响应中请求多个独立工具调用时(如同时读两个文件),系统并行执行它们。这直接减少了用户等待时间。 **权限检查**:每个工具执行前都经过权限验证。危险操作(如执行 shell 命令)需要用户确认,安全操作(如读文件)可以自动放行。 ### 阶段四:终止或继续 每次迭代结束时,系统判断是否需要继续: | 条件 | 结果 | |------|------| | AI 请求了工具调用 | 继续(下一轮迭代) | | AI 只返回文本,没有工具调用 | 终止(任务完成) | | 用户中断 | 终止(用户取消) | | 达到最大 turn 数 | 终止(安全限制) | | Token 预算耗尽 | 终止(成本控制) | ## 错误恢复:自愈的状态机 Agentic Loop 不是"正常路径走完就结束"的简单循环。它包含了多层错误恢复机制,使系统在各种异常情况下都能优雅处理。 ### 输出截断恢复 当 AI 的响应被 token 上限截断时(AI 话说了一半被切断): 1. **首次截断**:静默提升输出 token 上限,重试 2. **仍然截断**:注入提示消息让 AI "接着说",最多重试 3 次 3. **恢复耗尽**:将截断的响应作为最终结果返回 ### 上下文过长恢复 当对话历史超过 API 的 token 限制时(413 错误): 1. **压缩重试**:即时压缩对话历史,生成摘要后重试 2. **压缩后仍过长**:返回错误信息,让用户决定如何处理 关键设计:系统通过标志位防止无限循环——每种恢复路径只尝试一次,不会在"压缩→失败→压缩"之间死循环。 ### 模型降级 当主模型不可用时(过载、维护等): 1. 已收集的响应被保留为历史记录 2. 自动切换到备用模型 3. 通知用户发生了降级 4. 从中断点继续,而不是从头开始 ## 状态管理 每次迭代的状态是不可变更新的——系统创建新的状态对象而非就地修改。状态中包含: - **对话消息**:当前所有消息的数组 - **压缩跟踪**:压缩操作的累计状态 - **恢复计数**:各种错误恢复已尝试的次数 - **继续原因**:上一轮为什么继续(用于检测和避免循环) **设计考量**:状态中记录"继续原因"是一个关键的防循环机制。系统可以在后续迭代中检查"上一轮是因为压缩重试而继续的",从而避免在同一个恢复路径上反复尝试。 ## 为什么不是"一次规划,批量执行" 一个自然的疑问是:为什么不先让 AI 规划好所有步骤,然后一次性批量执行? 答案在于软件工程的**不确定性**: - **每步结果影响下一步**:搜索结果决定了要改哪些文件,修改后的编译结果决定了是否需要进一步调整 - **错误需要即时修正**:如果某步失败,AI 需要立即调整策略,而非继续执行无效计划 - **用户可能中途干预**:循环架构允许用户随时打断和修正方向 这不是说 AI 不做规划——事实上系统内置了规划模式(Plan Mode)用于复杂任务。但规划的结果仍然是逐步执行的,每一步都有机会根据新信息调整。 ## 一个完整的迭代示例 > 用户:"帮我找到项目里所有未使用的导入语句,然后删掉它们" ``` 迭代 1: 探索 AI: 先找到所有 TypeScript 文件 工具: Glob("**/*.ts") → 返回 42 个文件 决策: 需要进一步分析 → 继续 迭代 2: 分析 AI: 搜索这些文件中的 import 语句 工具: Grep("import.*from") → 在 15 个文件中找到 120 条 import 决策: 结果太多,需要进一步筛选 → 继续 迭代 3: 精确修改 AI: 分析哪些 import 未被使用,删除它们 上下文预处理: 120 条结果被微压缩为摘要 工具: FileEdit × 3 → 删除 5 条未使用导入 决策: 需要验证 → 继续 迭代 4: 验证与总结 AI: 验证修改后编译通过 工具: Bash("tsc --noEmit") → 编译通过 决策: 任务完成 → 终止 ``` 注意这个过程中的关键特征: - AI 在每一步后根据结果自主决定下一步 - 上下文在迭代过程中动态调整(微压缩被触发) - 用户全程无需介入 ## 接下来 - **流式响应** — 理解流式传输的设计细节和用户体验考量 - **多轮对话** — 跨迭代的上下文管理和会话持久化 - **上下文压缩** — 深入理解自动压缩的触发条件和策略 - **工具系统** — 了解 AI 可以调用哪些工具及其设计