Claude Code Agent 实现原理深度剖析

5 阅读17分钟

声明:本文源码标注的文件路径均来自 src/src/ 目录,为 Claude Code v2.1.88 反编译后的 TypeScript 源码,供学习与研究参考。


一、引言:从一个工具到一整个 Agent 系统

Claude Code 是 Anthropic 官方的 CLI 编程助手,它不仅是一个增强版的 REPL(Read-Eval-Print Loop),更是一个完整的 Agent 系统——它能够自主规划、调用工具、记忆上下文,并在多轮交互中持续推进复杂任务。

理解 Claude Code 的 Agent 实现原理,本质上是理解以下几个核心子系统的设计哲学与工程实践:

  • Agent 核心循环:消息如何驱动一轮又一轮的推理与执行
  • Tool 系统:工具的定义、执行与并发控制
  • 多 Agent 编排:子 Agent 与协调者模式的实现
  • 记忆与上下文管理:如何在有限上下文窗口内维护长期知识
  • 安全与权限体系:如何在开放能力的同时保障系统安全
  • Hooks 扩展机制:如何让用户干预 Agent 的每一步行为

本文将逐一展开,结合源码文件路径,帮助你在阅读后能够自行深入对应模块继续研究。


二、整体架构:从入口到 Agent 核心循环

2.1 程序入口与初始化

Claude Code 的入口由 src/src/entrypoints/cli.tsx 统一分发,根据命令行参数路由到不同模式(普通对话、Agent SDK、后台任务等)。真正的全局初始化在 src/src/entrypoints/init.ts 中完成,依次完成配置加载、网络初始化、遥测注册与清理函数注册。

src/src/main.tsx 中,我们可以看到完整的启动流水线:

// src/src/main.tsx(约第 50-80 行,简化摘录)
loadGrowthBookFeatureFlags();  // 特性开关(实验性功能)
loadModels();                  // 模型列表加载
loadPlugins();                 // 插件系统初始化
loadSkills();                  // 技能(Slash Commands)加载
loadAgents();                  // 自定义 Agent 定义加载
startMcpServers();             // MCP(Model Context Protocol)服务器启动
renderREPL();                  // 渲染主 REPL 界面

整个过程是同步串行的,每个步骤失败都会阻断后续流程,这与 Agent 系统对运行环境健壮性要求高的特点一致。

2.2 Agent 核心循环:query() 与 queryLoop()

源码文件src/src/query.ts

Agent 的核心是一个 while(true) 无限循环,由 query() 函数驱动。query() 是一个 AsyncGenerator,每次 yield 出一个流式事件(文本、思考过程、工具调用、工具结果等),直到遇到终止条件。

整个循环的状态机如下:

// src/src/query.ts - State 类型定义(简化)
type State = {
  messages: Message[]           // 消息历史
  toolUseContext: ToolUseContext // 工具执行上下文
  autoCompactTracking: AutoCompactTrackingState | undefined // 自动压缩追踪
  maxOutputTokensRecoveryCount: number  // max_tokens 恢复计数
  turnCount: number             // 当前轮次
  transition: Continue | undefined  // 继续指令
  // ...
}

每轮迭代经过以下阶段:

构建请求
  └─ system prompt(静态缓存部分 + 动态部分)
  └─ messages(带 cache breakpoints)
  └─ tools(Schema 转换后)

调用 API(流式)
  └─ message_start → content_block_start → content_block_delta → message_stop

处理响应块
  ├─ text 块 → 展示给用户
  ├─ thinking 块 → 展示推理过程
  └─ tool_use 块 → 交给 StreamingToolExecutor 执行

后处理
  ├─ 自动压缩(auto-compact)当上下文接近窗口上限
  ├─ Token 预算检查
  ├─ Stop Hooks 执行
  └─ 记忆预取(memory prefetch)

2.3 API 流式处理与容错

源码文件src/src/services/api/claude.ts

Claude Code 默认使用 SSE(Server-Sent Events)流式调用 Anthropic API,逐事件(event-by-event)处理,灵活性极高。核心处理流程为:

// src/src/query.ts - 流式事件分发(概念性伪代码)
for await (const event of stream) {
  switch (event.type) {
    case 'message_start':
      // 初始化消息元数据
      break
    case 'content_block_start':
      if (event.content_block.type === 'tool_use') {
        // 新工具调用开始,推入 StreamingToolExecutor
        streamingExecutor.addTool(event.content_block, assistantMessage)
      }
      break
    case 'content_block_delta':
      if (event.delta.type === 'thinking_block' || event.delta.type === 'text_delta') {
        // 实时 yield 显示
        yield event.delta
      }
      break
    case 'message_stop':
      // 消息接收完毕,处理所有工具结果
      for await (const result of streamingExecutor.getRemainingResults()) {
        yield result
      }
      break
  }
}

当流式传输失败时,系统会降级为非流式调用(最多一次),但代价是单次输出上限降至 64K tokens。如果遇到 max_output_tokens 错误,系统会依次尝试以下恢复策略:

  1. collapse_drain_retry — 尝试压缩上下文重试
  2. reactive_compact_retry — 响应式压缩重试
  3. max_output_tokens_escalate — 扩大 max_tokens
  4. max_output_tokens_recovery — 最多重试 3 次
  5. token_budget_continuation — 在 token 预算内继续
// src/src/query.ts - 恢复链(Recovery Chain)
const recoveryChain = [
  collapse_drain_retry,
  reactive_compact_retry,
  max_output_tokens_escalate,
  max_output_tokens_recovery,
  stop_hook_blocking,
  token_budget_continuation
]

三、Tool 系统:Agent 的四肢

3.1 统一的 Tool 接口定义

源码文件src/src/Tool.ts

Claude Code 定义了一个严格的 Tool 接口,每个工具都通过 buildTool() 工厂函数构建。以下是核心接口结构:

// src/src/Tool.ts - Tool 接口核心字段
type Tool<Input, Output, P> = {
  name: string                          // 唯一标识符
  inputSchema: Input                    // Zod Schema,参数校验

  call(
    args: z.infer<Input>,               // 解析后的参数
    context: ToolUseContext,            // 执行上下文(大量注入依赖)
    canUseTool: CanUseToolFn,          // 权限检查函数
    parentMessage: AssistantMessage,   // 父消息(用于推理链溯源)
    onProgress?: ToolCallProgress<P>   // 进度回调(支持长时间操作)
  ): Promise<ToolResult<Output>>

  description(input?, options?): Promise<string>   // 工具描述(给模型看)
  prompt(options?): Promise<string>                 // 使用说明(给系统 prompt 用)

  // 执行属性(影响并发调度)
  isConcurrencySafe(input?): boolean   // true = 可并行,false = 独占
  isReadOnly(input?): boolean          // 只读 = 不修改系统状态
  isDestructive?(input?): boolean       // 破坏性标记

  // 权限
  checkPermissions(input, context): Promise<PermissionResult>

  // UI 渲染(React 组件,返回 Ink 兼容节点)
  renderToolUseMessage?(input, options): React.ReactNode
  renderToolResultMessage?(content, progressMessages, options): React.ReactNode
  renderGroupedToolUse?(toolUses[], options): React.ReactNode | null
}

buildTool() 工厂函数使用 TOOL_DEFAULTS 填充所有可选字段:

// src/src/Tool.ts - 工厂默认值
const TOOL_DEFAULTS = {
  isEnabled: () => true,
  isConcurrencySafe: (_input?) => false,  // 默认非并发安全
  isReadOnly: (_input?) => false,
  checkPermissions: () => Promise.resolve({ behavior: 'allow', updatedInput: input }),
  // ...
}
export function buildTool<D extends AnyToolDef>(def: D): BuiltTool<D>

3.2 ToolUseContext:工具的"神经中枢"

源码文件src/src/Tool.tsToolUseContext 类型定义)

ToolUseContext 是一个约 150+ 字段的巨大对象,在每次工具调用时注入,为工具提供了 Agent 运行时的几乎所有上下文信息:

// src/src/Tool.ts - ToolUseContext 核心字段概览
type ToolUseContext = {
  // 工具相关
  options: {
    tools: Tools                      // 可用工具池
    commands: Command[]               // 可用 Slash Commands
    thinkingConfig: ThinkingConfig     // 思考配置
    mcpClients: McpClientMap          // MCP 客户端
  }

  // 中断与取消
  abortController: AbortController     // 用户可随时取消

  // 文件状态
  readFileState: FileStateCache       // 文件读取缓存(避免重复读)

  // 状态管理
  getAppState: () => AppState         // Zustand 全局状态读取
  setAppState: (updater) => void       // 状态更新

  // Agent 追踪
  agentId?: string                     // 当前 Agent ID(多 Agent 时有用)
  queryTracking?: QueryTracking       // 当前查询追踪信息

  // 上下文压缩状态
  contentReplacementState: ContentReplacementState

  // 权限上下文
  permissionContext: PermissionContext
  // ... 150+ 字段
}

这种设计使得工具无需自行获取这些信息——所有依赖都通过 context 注入,符合**依赖注入(DI)**的设计原则,极大提升了工具的可测试性和可组合性。

3.3 工具执行管道:权限 → Hooks → 调用 → 后处理

源码文件

  • src/src/services/tools/toolExecution.ts
  • src/src/services/tools/toolHooks.ts

一次工具调用的完整管道如下:

Zod 输入校验
  → tool.validateInput()
  → runPreToolUseHooks()       // 执行 PreToolUse 钩子(可修改输入或阻断)resolveHookPermissionDecision()  // 合并 Hook 和权限系统决策canUseTool()               // 最终权限检查
  → tool.call()                // 执行工具核心逻辑runPostToolUseHooks()     // 执行 PostToolUse 钩子(可触发后续行为)
  → (若出错) runPostToolUseFailureHooks()
// src/src/services/tools/toolExecution.ts - 工具执行管道核心
async function checkPermissionsAndCallTool(
  tool, toolUseID, input, toolUseContext,
  canUseTool, assistantMessage, messageId, requestId,
  mcpServerType, mcpServerBaseUrl, onToolProgress
): Promise<MessageUpdateLazy[]> {
  // Step 1: Zod 校验
  const parseResult = tool.inputSchema.safeParse(input)

  // Step 2: PreToolUse Hooks(可阻断)
  const preHookResults = await runPreToolUseHooks(tool, input, toolUseContext)

  // Step 3: 权限决策
  const permissionResult = await resolveHookPermissionDecision(preHookResults, canUseTool)

  // Step 4: 执行工具(支持长时间操作的进度回调)
  const result = await tool.call(parseResult.data, context, canUseTool, ...)

  // Step 5: PostToolUse Hooks
  await runPostToolUseHooks(tool, result, toolUseContext)

  return [{ message: toolResultMessage }]
}

⚠️ 关键设计:工具的输入参数是在一个克隆的副本上运行 tool.backfillObservableInput(),这样 Hooks 和权限检查不会意外修改原始参数,从而保护 Prompt Cache 的完整性。

3.4 并发控制:谁可以并行,谁必须串行

源码文件src/src/services/tools/StreamingToolExecutor.ts

这是整个工具系统中设计最精妙的部分之一。StreamingToolExecutor 维护了一个工具状态机:

// src/src/services/tools/StreamingToolExecutor.ts - 工具状态机
type TrackedTool = {
  id: string
  block: ToolUseBlock
  status: 'queued' | 'executing' | 'completed' | 'yielded'
  isConcurrencySafe: boolean
  promise?: Promise<void>
  results?: Message[]
  pendingProgress: Message[]    // 立即 yield 的进度消息
  contextModifiers?: Array<(context: ToolUseContext) => ToolUseContext>
}

// 并发判断逻辑
canExecuteTool(isConcurrencySafe: boolean): boolean {
  const executingTools = this.tools.filter(t => t.status === 'executing')
  return executingTools.length === 0 ||
    (isConcurrencySafe && executingTools.every(t => t.isConcurrencySafe))
  // 即:队列空闲时任何工具可执行;或者执行中只有并发安全工具时,新的并发安全工具可加入
}

执行策略

  • 并发安全的工具(如 ReadGlobGrep):最多 N 个同时执行(N 默认 10,可通过 CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY 配置)
  • 非并发安全的工具(如 WriteBash):独占执行,形成"门锁"
  • 如果一个非并发安全工具启动,会等待所有当前执行中的工具完成后再开始

这种设计的好处是:模型可以一次性请求多个只读操作(如读取多个文件),它们会被并行高效地执行,而写操作和命令执行则保证串行以避免竞态条件。


四、工具注册与组装

源码文件src/src/tools.ts

Claude Code 有 43 个内置工具,分为多个类别:

类别代表工具源码文件
文件操作FileReadTool, FileEditTool, FileWriteToolsrc/src/tools/FileReadTool/
搜索GlobTool, GrepToolsrc/src/tools/GlobTool/
执行命令BashTool, PowerShellTool, REPLToolsrc/src/tools/BashTool/
网络WebFetchTool, WebSearchToolsrc/src/tools/WebFetchTool/
多 AgentAgentTool, SendMessageToolsrc/src/tools/AgentTool/
任务管理TaskCreateTool, TaskUpdateTool, ...src/src/tools/TaskTool/
定时任务CronCreateTool, CronDeleteTool, CronListToolsrc/src/tools/CronTool/
工作流EnterPlanModeTool, ExitPlanModeTool, EnterWorktreeToolsrc/src/tools/PlanModeTool/
MCPMCPTool, ListMcpResourcesToolsrc/src/services/mcp/

工具组装的核心函数是 assembleToolPool(),它按以下顺序合并:

// src/src/tools.ts - 工具组装逻辑
export function assembleToolPool(permissionContext, mcpTools): Tools {
  // 1. 获取所有内置工具(已按名称排序)
  const builtins = getAllBaseTools()

  // 2. MCP 工具排序
  const sortedMcpTools = [...mcpTools].sort((a, b) => a.name.localeCompare(b.name))

  // 3. 去重:内置工具优先
  const merged = [...builtins, ...sortedMcpTools]
  const deduplicated = uniqBy(merged, 'name')

  // 4. 内置工具形成连续前缀(用于 Prompt Cache 稳定性)
  return deduplicated
}

源码注src/src/tools.ts 中使用了 feature('COORDINATOR_MODE')feature('AGENT_TRIGGERS') 等编译时特性开关,配合 bun:bundlefeature() 宏实现死代码消除(Tree Shaking),确保不相关特性的代码不会进入最终产物。


五、多 Agent 系统:子 Agent 与协调者模式

5.1 子 Agent(AgentTool):Agent 递归嵌套

源码文件

  • src/src/tools/AgentTool/AgentTool.tsx
  • src/src/tools/AgentTool/runAgent.ts
  • src/src/utils/forkedAgent.ts

Claude Code 的 AgentTool 允许一个 Agent 在运行时动态创建子 Agent,每个子 Agent 有独立的消息历史、工具池和系统提示。

子 Agent 的输入 Schema 定义了它的元信息:

// src/src/tools/AgentTool/AgentTool.tsx - 输入 Schema
const baseInputSchema = lazySchema(() => z.object({
  description: z.string(),      // 任务描述(显示用)
  prompt: z.string(),            // 子 Agent 的核心指令
  subagent_type: z.string().optional(),  // Agent 类型(general-purpose, explore 等)
  model: z.enum(['sonnet', 'opus', 'haiku']).optional(),  // 指定模型
  run_in_background: z.boolean().optional()  // 是否后台运行
}))

call() 方法中有四条 spawn 路径:

// src/src/tools/AgentTool/AgentTool.tsx - call() 方法逻辑框架
async call(input, context, canUseTool, parentMessage, onProgress) {
  // 路径1: Teammate 模式(团队协作)
  if (input.team_name && input.name) {
    return spawnTeammate(input, context)
  }

  // 路径2: Fork 子 Agent(轻量级,共享父 prompt cache)
  if (effectiveType === undefined && isForkSubagentEnabled()) {
    return runForkedAgent(input, context, parentMessage)
  }

  // 路径3: 普通子 Agent(完全独立)
  const agentDef = resolveAgentDefinition(input)
  return runAgent(agentDef, input, context)

  // 路径4: 远程隔离(git worktree 或远程会话)
  if (input.isolation === 'remote') {
    return runRemoteIsolationAgent(input, context)
  }
}

异步 vs 同步执行:如果 shouldRunAsync 为 true(后台运行、协调者模式、强制异步等),Agent 在后台线程中执行,主 Agent 不会等待结果:

// src/src/tools/AgentTool/AgentTool.tsx - 异步循环
while (true) {
  const result = await Promise.race([
    agentIterator.next(),        // Agent 迭代器下一步
    backgroundPromise            // 超时自动后台化
  ])

  if (result.done) {
    finalizeAgentTool(agentMessages, agentId, metadata)
    break
  }

  // 支持用户中断后自动后台化
  if (userInterrupted) {
    await agentIterator.return(undefined)
    continueInBackground(agentIterator)
    break
  }
}

5.2 Fork 子 Agent:共享 Prompt Cache 的轻量方案

源码文件src/src/utils/forkedAgent.ts

Fork 子 Agent 是 Claude Code 的一个高阶优化:普通子 Agent 会创建一个完全新的 API 会话,但 Fork Agent 通过 createSubagentContext() 确保父子 Agent 共享相同的缓存参数(如 cache_control 字段),使得 API 能够命中跨会话的 Prompt Cache,大幅降低 token 消耗和响应延迟。

// src/src/utils/forkedAgent.ts - 核心函数
export function createSubagentContext(
  parentParams: AnthropicMessageParam[],
  parentSystemPrompt: SystemPrompt,
  childSystemPrompt: SystemPrompt
): SubagentContext {
  // 确保 cache 的边界点(cache breakpoint)一致
  return {
    sharedCacheParams: extractCacheParams(parentParams),
    childSystemPromptHash: hashSystemPrompt(childSystemPrompt)
  }
}

5.3 协调者模式(Coordinator Mode):多 Agent 协作编排

源码文件src/src/coordinator/coordinatorMode.ts

协调者模式是 Claude Code 的多 Agent 生产级方案:主 Agent(协调者)负责任务分解、派发和结果综合,多个工作 Agent 并行执行子任务。

协调者模式的核心是继续 vs 派发决策矩阵

场景决策原因
研究结果刚好是要编辑的文件继续(SendMessageTool)Worker 有文件上下文和清晰计划
研究广泛但实现范围窄派发新 Agent(AgentTool)避免探索噪声
修正失败或扩展近期工作继续(SendMessageTool)Worker 有错误上下文
验证他人代码派发新 Agent(AgentTool)全新视角,无实现偏见
完全无关的任务派发新 Agent(AgentTool)无可复用上下文

协调者的系统提示约为 370 行(src/src/coordinator/coordinatorMode.ts),明确定义了 Research → Synthesis → Implementation → Verification 四个阶段的工作流程和工具使用规则。


六、记忆与上下文管理

6.1 记忆层次架构

Claude Code 实现了多层次的记忆系统:

┌─────────────────────────────────────────┐
│  长期记忆 (Long-term Memory)              │
│  · CLAUDE.md 文件 (memdir 系统)            │
│  · 自定义 Agent 定义 (.claude/agents/)     │
│  · Team Memory (团队共享知识)               │
├─────────────────────────────────────────┤
│  会话记忆 (Session Memory)                 │
│  · SessionMemory 服务(后台子 Agent 维护)  │
│  · 自动记录对话中的关键信息到 .md 文件        │
├─────────────────────────────────────────┤
│  工作记忆 (Working Memory)                │
│  · messages[] 数组(LLM 可见)              │
│  · ToolUseContext 中的 readFileState 等    │
├─────────────────────────────────────────┤
│  上下文压缩 (Context Compaction)            │
│  · 自动压缩(auto-compact)触发时由子 Agent │
│    生成摘要,替换旧消息段落                   │
└─────────────────────────────────────────┘

源码文件

  • src/src/memdir/memdir.ts — 记忆目录管理
  • src/src/memdir/findRelevantMemories.ts — 上下文相关记忆检索
  • src/src/services/SessionMemory/sessionMemory.ts — 会话记忆(后台 Agent 维护)
  • src/src/services/extractMemories/ — 记忆提取服务

6.2 自动上下文压缩(Auto-Compact)

源码文件src/src/query.tsautoCompactTracking 相关逻辑)

当上下文接近模型窗口上限时,系统会触发自动压缩流程:

// src/src/query.ts - 压缩决策逻辑(概念性)
async function maybeCompact(state: State): Promise<State> {
  const contextUsage = calculateContextUsage(state.messages, state.systemPrompt)
  const windowLimit = getModelWindowLimit(state.model)

  if (contextUsage / windowLimit > 0.85) {  // 85% 阈值
    // 创建一个压缩子 Agent,输入为即将被压缩的消息
    // 子 Agent 输出一个摘要,然后替换原始消息范围
    const summary = await runCompactAgent(state.messages, state.toolUseContext)
    return {
      ...state,
      messages: replaceWithSummary(state.messages, summary),
      autoCompactTracking: { ... }  // 记录压缩历史,避免重复压缩
    }
  }
  return state
}

七、Hook 扩展机制

源码文件

  • src/src/utils/hooks/execAgentHook.ts
  • src/src/utils/hooks/execPromptHook.ts
  • src/src/utils/hooks/execHttpHook.ts
  • src/src/services/tools/toolHooks.ts

Claude Code 提供了一个强大的 Hook 系统,允许用户在 Agent 运行的各个阶段拦截和干预行为。目前支持三种 Hook 类型:

Hook 类型执行时机可干预内容
PreToolUse工具执行前修改输入参数、阻断执行
PostToolUse工具执行后记录日志、触发后续 Agent
StopAgent 轮次结束发送通知、持久化数据

Hook 可以由多种机制触发:

// Hook 触发类型
type HookExecutionType =
  | { type: 'command', command: string }      // 执行 Shell 命令
  | { type: 'agent', prompt: string }         // 运行一个 Agent
  | { type: 'prompt', template: string }       // 渲染提示模板
  | { type: 'http', url: string, method: string, body: object }  // 发送 HTTP 请求

PreToolUse Hook 的返回值会与权限系统决策合并(resolveHookPermissionDecision),形成一个统一的允许/拒绝/需确认决策。


八、安全与权限体系

源码文件

  • src/src/utils/permissions/permissions.ts — 核心权限引擎
  • src/src/utils/permissions/filesystem.ts — 文件系统权限规则
  • src/src/tools/BashTool/bashSecurity.ts — Bash 危险命令检测
  • src/src/utils/bash/bashParser.ts — Bash AST 解析器(130KB)
  • src/src/hooks/useCanUseTool.tsx — 交互模式权限决策流程

Claude Code 实现了多层安全防御

  1. 工具级权限checkPermissions 方法):每个工具自行定义敏感操作的条件
  2. Bash 命令 AST 分析:将 Bash 命令解析为 AST,检测危险模式(如 rm -rf /、重定向到系统文件等)
  3. 权限模式(Permission Modes)
    • default:询问用户
    • plan:需要明确批准
    • bypass:允许所有
    • auto:由 YOLO 分类器自动决策
  4. 沙箱执行src/src/utils/sandbox/sandbox-adapter.ts 支持在受限环境中执行危险操作
// src/src/hooks/useCanUseTool.tsx - 权限决策流程(简化)
async function useCanUseTool(toolName, input, context): Promise<PermissionResult> {
  const tool = findToolByName(availableTools, toolName)

  // 1. 工具自身权限检查
  const toolPermission = await tool.checkPermissions(input, context)
  if (toolPermission.behavior !== 'allow') return toolPermission

  // 2. 全局权限规则检查(filesystem.ts 中的路径规则)
  const globalPermission = await checkGlobalPermissionRules(toolName, input)
  if (globalPermission.behavior !== 'allow') return globalPermission

  // 3. Bash 特殊检测(若为 BashTool)
  if (toolName === 'Bash') {
    const bashAnalysis = parseBashCommand(input.command)
    const dangerLevel = detectDangerousPatterns(bashAnalysis)
    if (dangerLevel > context.permissionMode.threshold) {
      return { behavior: 'block', reason: 'Dangerous pattern detected' }
    }
  }

  return { behavior: 'allow' }
}

九、Build 系统与特性开关

源码文件

  • src/src/tools.tsfeature() 调用处)
  • src/src/main.tsx(特性加载处)
  • 项目根目录 package.json

Claude Code 使用 Bun 作为构建工具,通过 bun:bundlefeature() 宏实现编译时特性开关

// src/src/tools.ts - 编译时特性开关示例
if (feature('COORDINATOR_MODE')) {
  // 只有开启协调者模式编译标志,才包含相关代码
  const { CoordinatorMode } = require('./coordinator/coordinatorMode')
}

if (feature('AGENT_TRIGGERS')) {
  // 定时任务和远程触发工具
  const { CronTool, RemoteTriggerTool } = require('./tools/CronTool')
}

这种设计带来了显著的好处:

  • 用户编译的产物只包含其开启的功能代码,体积最小化
  • 不同部署环境(Ant 用户 vs 普通用户)可以有不同的功能子集
  • 实验性功能(如 KAIROSVOICE_MODE)可以在不重新编译的情况下通过配置切换

十、QueryEngine:面向 SDK 的高级抽象

源码文件src/src/QueryEngine.ts

QueryEnginequery() 函数的高级封装,专为 SDK/Headless 模式设计。它将 Agent 的核心能力以类形式暴露:

// src/src/QueryEngine.ts - 核心 API
export class QueryEngine {
  constructor(config: QueryEngineConfig)

  // 提交用户消息,返回流式结果
  async *submitMessage(
    prompt: string | ContentBlockParam[],
    options?: { uuid?: string; isMeta?: boolean }
  ): AsyncGenerator<SDKMessage, void, unknown>

  interrupt(): void        // 中断当前 Agent
  getMessages(): readonly Message[]   // 获取消息历史
  getReadFileState(): FileStateCache   // 获取文件状态
  getSessionId(): string   // 会话 ID
  setModel(model: string): void  // 动态切换模型
}

在 SDK 模式下,Agent 的所有能力都可以通过 submitMessage() 调用,Agent 的循环、工具执行、权限处理对外部完全透明。Claude Code 的 /v1/agents/v1/sessions API 底层正是基于 QueryEngine 实现。


十一、整体架构图

┌──────────────────────────────────────────────────────────┐
│                      main.tsx                             │
│  (GrowthBook → Models → Plugins → Skills → Agents → MCP)   │
└──────────────────────────┬───────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────┐
│                  QueryEngine / query()                    │
│           ┌──────────────────────────────┐               │
│           │   while(true) Agent Loop     │               │
│           │  ① 构建请求 (System+Msgs+Tools) │              │
│           │  ② 调用 API (Streaming)        │              │
│           │  ③ 分发事件                    │              │
│           │    ├─ text → UI 展示           │              │
│           │    ├─ thinking → 推理展示      │              │
│           │    └─ tool_use → 工具执行器    │              │
│           │  ④ 工具执行管道                 │              │
│           │     ├─ PreToolUse Hooks       │              │
│           │     ├─ Permission Check       │              │
│           │     ├─ tool.call()           │              │
│           │     └─ PostToolUse Hooks     │              │
│           │  ⑤ Auto-Compact / 后处理       │              │
│           └──────────────────────────────┘               │
└──────────────────────────┬───────────────────────────────┘
                           │
        ┌──────────────────┼──────────────────┐
        ▼                  ▼                  ▼
┌───────────────┐  ┌──────────────┐  ┌──────────────────┐
│ Tool Pool      │  │ AgentTool    │  │ CoordinatorMode   │
│ (43 built-in  │  │ (子Agent递归) │  │ (多Agent编排)      │
│  + MCP tools)  │  │              │  │                   │
│               │  │ ├─ runAgent   │  │  Coordinator      │
│ Streaming     │  │ ├─ ForkAgent  │  │    ├─ Worker A    │
│ Executor      │  │ ├─ Teammate   │  │    ├─ Worker B    │
│ (并发/串行控制) │  │ └─ RemoteISO │  │    └─ Worker C    │
└───────────────┘  └──────────────┘  └──────────────────┘
        │                  │                  │
        ▼                  ▼                  ▼
┌──────────────────────────────────────────────────────────┐
│              ToolUseContext (150+ 字段依赖注入)            │
│  tools | abortController | readFileState | permissions   │
│  agentId | queryTracking | mcpClients | ...              │
└──────────────────────────────────────────────────────────┘
        │
        ▼
┌──────────────────────────────────────────────────────────┐
│ Memory System                                             │
│  CLAUDE.md (memdir) → SessionMemory → Auto-Compact       │
│  Hooks: PreToolUse | PostToolUse | Stop                  │
└──────────────────────────────────────────────────────────┘

十二、总结:核心设计哲学

纵观 Claude Code 的 Agent 实现,有几条贯穿始终的设计哲学值得学习:

  1. 一切皆 Generator:核心循环 query()AsyncGenerator,每个阶段的事件都可以被 yield 和实时处理,实现了真正的流式交互。

  2. 依赖注入替代全局状态ToolUseContext 统一注入所有上下文信息,工具不需要自行获取依赖,可测试性和可组合性极强。

  3. 并发安全分类:通过 isConcurrencySafe 标记,Agent 可以一次性请求大量只读操作并高效并行执行,而写操作则保证串行安全。

  4. Prompt Cache 优先:无论是系统提示的静态部分缓存,还是工具池的连续前缀排序,还是 Fork Agent 的缓存参数共享,都在最大化利用 Anthropic 的 Prompt Cache 以降低成本和延迟。

  5. 可恢复的错误处理:Agent 循环永远不会因为一次错误就崩溃——它有一整套恢复链,从压缩重试到 token 上限扩容,确保长任务能够尽可能完成。

  6. 编译时 vs 运行时特性分离:使用 feature() 宏在编译时实现特性开关,用户产物体积最小化,同时保留了运行时的灵活性。


延伸阅读建议(源码文件路径,供深入研究):

  • Agent 核心循环:src/src/query.ts
  • 工具框架:src/src/Tool.ts
  • 工具执行管道:src/src/services/tools/toolExecution.tssrc/src/services/tools/toolOrchestration.ts
  • 并发执行器:src/src/services/tools/StreamingToolExecutor.ts
  • 子 Agent 系统:src/src/tools/AgentTool/AgentTool.tsxsrc/src/utils/forkedAgent.ts
  • 协调者模式:src/src/coordinator/coordinatorMode.ts
  • SDK 封装:src/src/QueryEngine.ts
  • 权限系统:src/src/utils/permissions/permissions.ts
  • Hook 机制:src/src/services/tools/toolHooks.tssrc/src/utils/hooks/
  • 记忆系统:src/src/memdir/src/src/services/SessionMemory/
  • 工具注册:src/src/tools.ts