昨天 Anthropic 官方 Claude Code CLI 发生了泄漏,GitHub,X冲上热门。我也是快速download下来开始学习分析这个号称最强编程Agent IDE到底是如何实现的。
如果你也感兴趣源码,可以私聊我,我发你。
趁着热情没有消退,今天我们直接来分析最核心的Agent Loop Claude Code 是怎么做的。
Agent Loop 中涉及到的细节 -- 压缩/错误恢复/Token计费/Memory & Skill加载,我们会再拆分单元研究。
我也在做一个基于Claude Code的核心逻辑的Agent SDK。我把核心的Agent Loop 提取出来,包装成了SDK codenano方便大家学习Claude Code的核心逻辑。需要用到Agent的时候也可以直接嵌入Claude Code的Agent。
Claude Code Agent Loop 详细分析
总统架构流程
flowchart TD
Start([开始]) --> Init[初始化状态]
Init --> Loop{主循环}
Loop --> Compact[压缩检查<br/>Snip/Micro/Collapse/Auto]
Compact --> API[调用 Claude API<br/>流式输出]
API --> HasTools{有工具调用?}
HasTools -->|否| StopHooks[Stop Hooks<br/>质量检查]
HasTools -->|是| ExecTools[执行工具<br/>并发/流式]
ExecTools --> Attach[添加附件<br/>Memory/Skill]
Attach --> CheckTurns{达到最大轮次?}
CheckTurns -->|是| End([结束])
CheckTurns -->|否| Loop
StopHooks --> Blocking{阻塞错误?}
Blocking -->|是| Loop
Blocking -->|否| Budget{Token Budget?}
Budget -->|继续| Loop
Budget -->|完成| End
API -.错误.-> Recovery[错误恢复<br/>Collapse/Reactive/Escalate]
Recovery --> Loop
概述
Claude Code 的 Agent Loop 是一个复杂的状态机,位于 query.ts 的 queryLoop 函数中(第 241-1729 行)。
核心结构
1. 循环入口
async function* queryLoop(
params: QueryParams,
consumedCommandUuids: string[],
): AsyncGenerator<StreamEvent | Message, Terminal>
特点:
- 异步生成器函数
- 流式输出事件
- 返回终止原因
2. 状态管理
type State = {
messages: Message[]
toolUseContext: ToolUseContext
autoCompactTracking: AutoCompactTrackingState | undefined
maxOutputTokensRecoveryCount: number
hasAttemptedReactiveCompact: boolean
maxOutputTokensOverride: number | undefined
pendingToolUseSummary: Promise<ToolUseSummaryMessage | null> | undefined
stopHookActive: boolean | undefined
turnCount: number
transition: Continue | undefined
}
设计要点:
- 所有状态集中在一个对象
- 每次 continue 时整体替换
- transition 字段记录为什么继续
字段详解
- messages: Message[] 用途:存储当前对话的所有消息
生命周期:
- 初始化:从 params.messages 复制
- 更新:每次 continue 时添加新消息
- 压缩:可能被压缩机制修改
示例:
messages: [
{ role: 'user', content: 'Hello' },
{ role: 'assistant', content: 'Hi!' },
{ role: 'user', content: 'tool_result', ... }
]
- toolUseContext: ToolUseContext 用途:工具执行的上下文环境
包含内容:
- options: 工具列表、模型配置等
- abortController: 中断控制器
- readFileState: 文件读取缓存
- queryTracking: 查询追踪信息
更新时机:
- 工具执行后可能更新
- 每次迭代开始时可能添加 queryTracking
- autoCompactTracking: AutoCompactTrackingState | undefined 用途:追踪自动压缩状态
结构:
{
compacted: boolean // 是否已压缩
turnId: string // 压缩的唯一 ID
turnCounter: number // 压缩后的轮次计数
consecutiveFailures: number // 连续失败次数
}
生命周期:
- 初始:undefined
- 压缩成功:设置为新的 tracking 对象
- 压缩失败:更新 consecutiveFailures
- maxOutputTokensRecoveryCount: number 用途:追踪 max_tokens 恢复次数
限制:最多 3 次 重置时机:每次成功完成一轮后重置为 0
- hasAttemptedReactiveCompact: boolean 用途:标记是否已尝试 Reactive Compact
作用:防止无限循环重试 重置时机:每次正常工具执行后重置
- maxOutputTokensOverride: number | undefined 用途:覆盖默认的 max_tokens
场景:
- 8K → 64K 升级
- 临时调整输出限制
- pendingToolUseSummary: Promise<...> | undefined 用途:异步生成工具使用摘要
特点:
- 在工具执行时启动(不阻塞)
- 在下一轮迭代时消费
- 使用 Haiku 快速生成
- stopHookActive: boolean | undefined stopHook 我们后面会讲解
用途:标记 Stop Hook 是否已激活
作用:防止 Stop Hook 无限循环重试
- turnCount: number 用途:当前轮次计数
用途:
- 追踪执行进度
- 检查是否达到 maxTurns
- transition: Continue | undefined 用途:记录为什么继续循环
可能的值:
- collapse_drain_retry - Context Collapse 恢复
- reactive_compact_retry - Reactive Compact 恢复
- max_output_tokens_escalate - Token 限制升级
- max_output_tokens_recovery - Token 恢复消息
- stop_hook_blocking - Stop Hook 阻塞
- token_budget_continuation - Token 预算继续
- next_turn - 正常下一轮
作用:
- 调试和追踪
- 测试验证
- 分析循环行为
主循环流程
while (true) 循环结构
while (true) {
1. 解构状态
2. 预取和初始化
3. 压缩检查
4. API 调用
5. 错误恢复
6. 工具执行
7. Stop Hooks
8. 继续或退出
}
每次迭代的步骤
Step 1: 解构状态 (第 311-321 行)
let { toolUseContext } = state
const {
messages,
autoCompactTracking,
maxOutputTokensRecoveryCount,
hasAttemptedReactiveCompact,
maxOutputTokensOverride,
pendingToolUseSummary,
stopHookActive,
turnCount,
} = state
Step 2: 压缩流程 (第 365-468 行)
执行顺序:
- Tool Result Budget - 限制工具结果大小
- Snip Compact - 剪切历史
- Microcompact - 压缩工具结果
- Context Collapse - 上下文折叠
- Autocompact - 自动压缩
关键点:
- 按顺序执行,每个都可能修改 messagesForQuery
- snipTokensFreed 传递给 autocompact
- 压缩后更新 tracking 状态
Step 3: API 调用 (第 653-863 行)
流程:
while (attemptWithFallback) {
try {
for await (const message of deps.callModel(...)) {
// 处理流式消息
// 启动流式工具执行
// 收集 tool_use blocks
}
} catch (FallbackTriggeredError) {
// 切换到 fallback 模型重试
}
}
关键特性:
- 流式输出
- 错误扣留(withheld)
- Streaming Tool Execution
- Model Fallback
Step 4: 错误恢复 (第 1062-1183 行)
恢复策略:
-
Prompt Too Long (413)
- Context Collapse drain
- Reactive Compact
- 错误扣留 → 恢复 → 成功则对用户透明
-
Max Output Tokens
- 升级:8K → 64K
- 注入恢复消息(最多 3 次)
-
Media Size Error
- Reactive Compact strip-retry
Step 5: 工具执行 (第 1364-1409 行)
流程:
const toolUpdates = streamingToolExecutor
? streamingToolExecutor.getRemainingResults()
: runTools(toolUseBlocks, assistantMessages, canUseTool, toolUseContext)
for await (const update of toolUpdates) {
if (update.message) {
yield update.message
toolResults.push(...)
}
}
特点:
- 流式工具执行器优先
- 并发执行工具
- 实时返回结果
Step 6: Stop Hooks (第 1267-1306 行)
什么是 Stop Hooks?
想象你在写代码,AI 助手帮你完成了一段工作。在它说"完成了"之前,Stop Hooks 就像一个自动检查员,会先检查一遍:
- 代码有没有语法错误?
- 测试通过了吗?
- 格式规范吗?
如果检查不通过,它会让 AI 重新修改,直到满足要求。
简单来说:Stop Hooks 是在 AI 每次回答结束时自动运行的检查程序。
工作原理
执行时机 AI 完成一轮回答后,如果没有需要调用工具(比如读文件、运行命令),就会触发 Stop Hooks。
返回结果 Stop Hook 检查完后会告诉系统两件事:
- 有没有错误需要修复(blockingErrors)- 如果有,AI 会重新工作
- 要不要停止继续(preventContinuation)- 如果是,整个流程就结束了
四种执行结果
✅ 成功(Success) 检查通过,一切正常,继续下一步。
⚠️ 非阻塞错误(Non-Blocking Error) 有小问题,但不影响继续,只是记录一下。
例子:代码格式不太规范,但功能正确。
🚫 阻塞错误(Blocking Error) 有严重问题,必须修复才能继续。
例子:测试失败,AI 必须重新修改代码。
🛑 停止继续(Stopped Continuation) 检查发现不应该继续了,直接结束。
例子:发现用户取消了任务。
核心特性
-
并行执行 如果配置了多个检查(比如同时检查代码格式和运行测试),它们会同时进行,节省时间。
-
实时进度 可以看到每个检查的进度,比如"正在运行测试... 50%"。
-
错误汇总 所有检查完成后,会生成一份报告,告诉你哪些通过了,哪些失败了。
两种模式
阻塞模式(blocking: true)
- 检查失败 → AI 必须重新修改
- 适合:测试、编译等必须通过的检查
非阻塞模式(blocking: false)
- 检查失败 → 只记录警告,继续执行
- 适合:代码格式、文档检查等非关键项
防止死循环
系统会记录是否已经重试过,避免 AI 无限次修改同一个问题。
Step 7: Token Budget (第 1308-1355 行)
自动继续机制:
if (decision.action === 'continue') {
incrementBudgetContinuationCount()
state = {
...state,
messages: [...messages, createUserMessage({
content: decision.nudgeMessage,
isMeta: true,
})],
}
continue
}
特点:
- 追踪 token 使用
- 接近预算时自动继续
- 检测收益递减
Continue 触发点
循环通过 continue 语句重新开始,主要触发点:
- collapse_drain_retry - Context Collapse 恢复
- reactive_compact_retry - Reactive Compact 恢复
- max_output_tokens_escalate - Token 限制升级
- max_output_tokens_recovery - Token 恢复消息
- stop_hook_blocking - Stop Hook 阻塞
- token_budget_continuation - Token 预算继续
- next_turn - 正常下一轮
退出条件
循环通过 return 退出,主要原因:
- completed - 正常完成
- aborted_streaming - 用户中断(流式阶段)
- aborted_tools - 用户中断(工具执行阶段)
- blocking_limit - 达到阻塞限制
- prompt_too_long - 提示过长且无法恢复
- image_error - 图片错误
- model_error - 模型错误
- stop_hook_prevented - Stop Hook 阻止
- hook_stopped - Hook 停止
- max_turns - 达到最大轮次
关键设计模式
1. 状态机模式
- 所有状态集中管理
- 每次 continue 整体替换
- transition 字段追踪状态转换
2. 错误扣留 (Error Withholding)
- 扣留可恢复的错误
- 尝试恢复
- 成功则对用户透明
3. 多层恢复策略
- Context Collapse → Reactive Compact → Surface Error
- 每层独立尝试,失败则进入下一层
4. 流式执行
- 工具在模型流式输出时就开始执行
- 实时返回结果
- 提升整体性能
总结
Claude Code 的 Agent Loop 是一个高度优化的状态机,包含:
- 5 种压缩机制(之后章节详解)
- 3 层错误恢复(之后章节详解)
- 流式工具执行(之后章节详解)
- Stop Hooks 系统
- Token Budget 管理(之后章节详解)