什么是 Agentic Loop
传统聊天机器人:你问一句,它答一句。Claude Code 不一样:你说一个需求,它可能连续执行十几步操作才给你最终结果。 这背后的机制叫做 Agentic Loop(智能体循环),核心实现在
src/query.ts 的 queryLoop() 异步生成器函数。它是一个 while(true) 无限循环,每次迭代代表一次”思考→行动→观察”周期。

循环的完整结构
queryLoop() 的每次迭代(src/query.ts 中 while(true) 主循环)包含以下阶段:
阶段 1:上下文预处理(Pre-Processing Pipeline)
在调用 API 之前,依次执行 5 个压缩/优化步骤:snipTokensFreed),避免重复压缩。
阶段 2:流式 API 调用(Streaming Loop)
deps.callModel() 发起流式请求(src/query.ts 中 attemptWithFallback 循环内),返回一个 AsyncGenerator。在流式过程中:
- AssistantMessage 被收集到
assistantMessages[]数组 - tool_use 块 被提取到
toolUseBlocks[],设置needsFollowUp = true - StreamingToolExecutor 在流式过程中就开始并行执行工具(不等流结束)
- 可恢复的错误(prompt-too-long、max-output-tokens)被暂扣(withheld),先尝试恢复
backfillObservableInput()—— 为 tool_use 块回填可观察字段(如文件路径展开),但只在添加了新字段时才克隆消息,避免破坏 prompt cache 的字节一致性- 流式降级检测——如果
streamingFallbackOccured,已收集的消息被标记为 tombstone,清空后重试
阶段 3:工具执行(Tool Execution)
如果needsFollowUp 为 true,循环不会终止,而是执行工具:
normalizeMessagesForAPI() 标准化后,与原始消息合并,进入下一轮循环迭代。
阶段 4:终止或继续
每次迭代结束时,根据条件决定return(终止)或 continue(继续):
终止条件(源码级)
循环有多种终止路径,按触发时机排列:| 终止原因 | 触发位置 | 机制 |
|---|---|---|
| blocking_limit | 第 686 行 | Token 计数超过硬限制(非 autocompact 模式)→ 生成 PTL 错误消息 → 返回 |
| image_error | 第 1021 行 | ImageSizeError / ImageResizeError 异常 → 直接返回 |
| model_error | 第 1040 行 | callModel() 抛出不可恢复异常 → 生成错误消息 → 返回 |
| aborted_streaming | 第 1095 行 | abortController.signal.aborted(流式阶段)→ 为未完成的 tool_use 生成合成 tool_result → 返回 |
| prompt_too_long | 第 1219/1226 行 | 413 错误且 reactive compact 无法恢复 → 暂扣的错误消息被释放 → 返回 |
| completed | 第 1308 行 | API 错误(限流、认证失败等)导致无法继续 → 返回 |
| stop_hook_prevented | 第 1323 行 | Stop hook 返回 preventContinuation: true → 返回 |
| completed | 第 1401 行 | 正常完成:AI 未发出 tool_use → needsFollowUp = false → 经过 stop hooks → 返回 |
| aborted_tools | 第 1559 行 | abortController.signal.aborted(工具执行阶段)→ 返回 |
| hook_stopped | 第 1564 行 | 工具执行期间 hook 返回 shouldPreventContinuation → 返回 |
| max_turns | 第 1755 行 | 轮次计数超过 maxTurns 限制 → 返回 |
继续条件(恢复路径)
循环不仅是一个简单的”有 tool_use 就继续”,它还包含多种恢复/重试路径:1. 正常工具循环(next_turn)
needsFollowUp = true → 执行工具 → 新消息追加到 messagesForQuery → state 重新赋值 → continue
2. max_output_tokens 恢复(max_output_tokens_escalate / max_output_tokens_recovery)
当 AI 输出被截断时(apiError === 'max_output_tokens'),分两阶段恢复:
- 提升阶段(
max_output_tokens_escalate):首次截断时,将maxOutputTokens从默认值提升到ESCALATED_MAX_TOKENS(64K)。静默重试,不注入 meta 消息。 - 恢复阶段(
max_output_tokens_recovery):提升后仍然截断时,注入恢复消息”Output token limit hit. Resume directly…”,最多重试MAX_OUTPUT_TOKENS_RECOVERY_LIMIT = 3次。恢复耗尽后,暂扣的错误消息被释放。
3. Prompt-Too-Long 恢复(collapse_drain_retry / reactive_compact_retry)
当遇到 413 错误时,按优先级尝试两种压缩策略:
- Context Collapse Drain(
collapse_drain_retry):提交所有已暂存的折叠(collapse),释放空间后重试。如果上一轮已经是collapse_drain_retry则跳过,避免无限循环。 - Reactive Compact(
reactive_compact_retry):如果 collapse drain 无法恢复,触发即时压缩(reactive compact),生成摘要后重试。hasAttemptedReactiveCompact标志防止无限循环。
4. Stop Hook 阻塞重试(stop_hook_blocking)
Stop hook 可以注入阻塞错误消息,强制 AI 重新思考。新的消息(包含阻塞错误)被追加到对话中,stopHookActive = true,进入下一轮迭代。
5. Token Budget 继续提示(token_budget_continuation)
当 TOKEN_BUDGET feature 启用时,如果 token 消耗达到阈值但未超出预算,注入 nudge 消息让 AI 加速收尾,然后继续。
模型降级(Fallback)
当主模型不可用时(FallbackTriggeredError,src/query.ts 中 attemptWithFallback 循环的 catch 分支):
- 已收集的
assistantMessages被清空,tool_use 块收到合成 tool_result:“Model fallback triggered” - 思维签名块被移除(
stripSignatureBlocks)—— 因为思维签名与模型绑定,跨模型回放会 400 - 切换到
fallbackModel,更新toolUseContext.options.mainLoopModel - 生成系统消息:“Switched to due to high demand for ”
- 重新发起流式请求
状态机:State 对象
每次迭代的状态通过State 类型(src/query.ts,类型定义)传递:
continue 都创建新的 State 对象(不可变更新),而非就地修改。transition 字段记录了为什么继续——让后续迭代能检测特定恢复路径(如 collapse_drain_retry)避免循环。
Token Budget(实验性)
当TOKEN_BUDGET feature 启用时(src/query.ts 中 !needsFollowUp 分支内的预算检查逻辑),循环在终止前会检查 token 消耗:
- continuation:未达到预算但超过阈值 → 注入 nudge 消息,让 AI 加速收尾
- diminishing_returns:检测到收益递减 → 提前终止
- 预算数据来自
createBudgetTracker(),跨迭代累计
为什么不是”一次规划,批量执行”
源码揭示了为什么 Claude Code 选择逐步循环:
- 每一步都产生真实信息:
runTools()返回的toolResults是 API 不可能预知的——命令输出、文件内容、错误信息 - 动态上下文管理:每轮迭代前都重新评估压缩需求(autocompact → microcompact → snip),基于最新的 token 计数
- 错误即时恢复:工具失败不需要推倒重来——stop hook 可以注入阻塞错误让 AI 修正策略
- 用户可控:
abortController.signal在循环的多个检查点被检测(第 1059、1095、1529 行),用户按 ESC 可以优雅中断 - 成本控制:Token Budget 在每轮终止前检查,防止 AI 无效循环
一个完整的迭代示例
用户:“帮我找到项目里所有未使用的导入语句,然后删掉它们”