Agent Loop
Agent Loop 是 Claude Code 的心脏 — query() 函数通过 AsyncGenerator 实现了流式的"思考-执行-观察"循环。
核心循环概述
Claude Code 的 Agent Loop 遵循经典的 ReAct (Reasoning + Acting) 模式:
+ 上下文
(流式)
→ 工具执行
或返回
query() 是一个 AsyncGenerator,通过 yield 逐个返回事件。这使得调用者(REPL 或 SDK)可以实时接收和处理每一个流式事件。
query() 函数详解
位于 src/query.ts,这是整个系统最核心的函数:
async function* query(params: QueryParams): AsyncGenerator<StreamEvent | Message> { // 主循环:每次迭代 = 一个 "turn" while (true) { // 1. 获取附件消息 (memory, skills, agents) yield 'stream_request_start'; // 2. 构建 API 请求 (system prompt + messages + tools) const apiMessages = normalizeMessagesForAPI(messages); // 3. 调用 Claude API (流式) const response = await ask({ messages: apiMessages, tools, ... }); // 4. 流式接收响应 for await (const event of response) { yield event; // 实时转发给调用者 } // 5. 检查是否有工具调用 if (hasToolUse) { // 权限检查 → 执行工具 → 收集结果 const results = await runTools(toolUseBlocks); messages.push(...results); continue; // 继续下一个 turn } // 6. 无工具调用 = 对话结束,返回 return; } }
Turn 流程详解
每个 turn 内部经历以下精确步骤:
预处理 & 附件注入
获取 memory 附件、agent 列表、skill 信息。应用 tool result budget(限制累计结果大小)。检查是否需要 snip compact。
消息标准化
normalizeMessagesForAPI() 过滤合成消息、验证 API 格式合规、应用上下文折叠、确保 tool_use 和 tool_result 配对。
System Prompt 组装
基础 prompt + 环境信息(cwd, shell, git 分支)+ CLAUDE.md 内容 + 用户/系统上下文。作为 cache-eligible 的 SystemPromptBlock 传入。
API 流式调用
调用 ask()(src/services/api/claude.ts),发送到 Claude API。支持多 provider:Anthropic 直连、AWS Bedrock、Google Vertex、Azure。
响应处理 & 工具调用
解析 BetaRawMessageStreamEvent。将文本/thinking 实时 yield。收集 tool_use blocks。
工具编排 (StreamingToolExecutor)
对每个 tool_use:权限检查 → 执行 → 收集结果。并行安全的工具可以并行执行。结果序列化为 ToolResultBlockParam。
退出判断
有工具结果 → 继续循环 (goto 1)。
无工具调用 → 返回 (对话完成)。
max_output_tokens → 触发恢复策略。
prompt_too_long → 触发压缩。
流式处理架构
Claude Code 的流式处理基于 AsyncGenerator 链:
// 事件类型层级 StreamEvent ├── 'stream_request_start' // API 请求开始 ├── 'stream_delta' // 模型输出增量 ├── RequestStartEvent // API 调用即将发起 ├── Message // 完整消息 │ ├── AssistantMessage // 助手回复 │ ├── UserMessage // 用户输入 │ ├── ProgressMessage // 工具执行进度 │ └── ToolUseSummaryMessage // 工具输出压缩 └── TombstoneMessage // 已移除/替换的消息
工具执行流程
工具执行通过 StreamingToolExecutor 进行编排(src/services/tools/toolOrchestration.ts):
// 对每个 tool_use block: for (const toolUse of assistantMessage.tool_use_blocks) { // Step 1: 权限检查 const permission = await canUseTool(toolUse); if (permission === 'deny') { results.push({ type: 'tool_result', is_error: true }); continue; } if (permission === 'ask') { // 显示权限对话框,等待用户决定 } // Step 2: 执行工具 const result = await tool.call(toolUse.input, context); // Step 3: 收集结果 results.push({ type: 'tool_result', content: result }); }
错误恢复策略
Max Output Tokens 恢复
当模型输出被截断(达到 max_output_tokens 限制)时:
- 最多重试 3 次(MAX_OUTPUT_TOKENS_RECOVERY_LIMIT)
- 每次减少 max_output_tokens 配置
- 如果尚未尝试,触发 recompact
- 重新提交请求
Prompt Too Long 恢复
- 检测到 prompt_too_long 错误
- 触发 microcompact(紧急压缩)
- 重新提交压缩后的消息
Fallback Model
- 主模型调用失败时,自动切换到备用模型
- 抛出
FallbackTriggeredError - 记录分析事件
上下文压缩
随着对话增长,上下文会超过模型的 token 限制。Claude Code 有三种压缩策略:
Auto Compact
Token 超过阈值时自动触发。需要至少 20 个 turn。调用模型生成摘要,替换历史消息。
Snip Compact
轻量级:直接删除中间消息,保留关键消息。无需模型推理,速度更快。
Reactive Compact
主动式:实时监控 token 预算,在接近限制前提前介入。