Agent Loop

Agent Loop 是 Claude Code 的心脏 — query() 函数通过 AsyncGenerator 实现了流式的"思考-执行-观察"循环。

核心循环概述

Claude Code 的 Agent Loop 遵循经典的 ReAct (Reasoning + Acting) 模式:

1. 输入
用户消息
2. 构建
System Prompt
+ 上下文
3. API 调用
Claude API
(流式)
4. 解析
文本 / 工具调用
5. 执行
权限检查
→ 工具执行
6. 循环
结果 → 下一轮
或返回
关键设计: 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 内部经历以下精确步骤:

1

预处理 & 附件注入

获取 memory 附件、agent 列表、skill 信息。应用 tool result budget(限制累计结果大小)。检查是否需要 snip compact。

2

消息标准化

normalizeMessagesForAPI() 过滤合成消息、验证 API 格式合规、应用上下文折叠、确保 tool_use 和 tool_result 配对。

3

System Prompt 组装

基础 prompt + 环境信息(cwd, shell, git 分支)+ CLAUDE.md 内容 + 用户/系统上下文。作为 cache-eligible 的 SystemPromptBlock 传入。

4

API 流式调用

调用 ask()(src/services/api/claude.ts),发送到 Claude API。支持多 provider:Anthropic 直连、AWS Bedrock、Google Vertex、Azure。

5

响应处理 & 工具调用

解析 BetaRawMessageStreamEvent。将文本/thinking 实时 yield。收集 tool_use blocks。

6

工具编排 (StreamingToolExecutor)

对每个 tool_use:权限检查 → 执行 → 收集结果。并行安全的工具可以并行执行。结果序列化为 ToolResultBlockParam

7

退出判断

有工具结果 → 继续循环 (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 限制)时:

Prompt Too Long 恢复

Fallback Model

上下文压缩

随着对话增长,上下文会超过模型的 token 限制。Claude Code 有三种压缩策略:

Auto Compact

Token 超过阈值时自动触发。需要至少 20 个 turn。调用模型生成摘要,替换历史消息。

Snip Compact

轻量级:直接删除中间消息,保留关键消息。无需模型推理,速度更快。

Reactive Compact

主动式:实时监控 token 预算,在接近限制前提前介入。