乐于分享
好东西不私藏

万字长文|Claude Code 源码深度解析:架构设计与工程智慧

万字长文|Claude Code 源码深度解析:架构设计与工程智慧

本文基于 Claude Code 源码仓库进行深度技术分析,从核心架构、工具系统、权限管理、上下文压缩等多个维度剖析这一革命性 AI 编程助手的设计哲学与实现细节。


目录

  1. 项目概述与架构总览
  2. 核心查询循环:query.ts 深度剖析
  3. 工具系统设计:从抽象到实现
  4. 系统提示词工程:塑造 AI 行为
  5. 上下文管理:无限对话的秘密
  6. 权限系统:安全与便利的平衡
  7. 多智能体架构:Agent Tool 的设计哲学
  8. 并发执行与 StreamingToolExecutor
  9. 技术债分析与改进建议
  10. 工程亮点与借鉴价值

1. 项目概述与架构总览

1.1 Claude Code 是什么

Claude Code 是 Anthropic 官方推出的命令行 AI 编程助手,它将 Claude 大语言模型的能力直接嵌入开发者的终端环境。不同于传统的 AI 编程助手(如 GitHub Copilot),Claude Code 具有以下核心特征:

  • 终端原生:
    完全在命令行中运行,无需 IDE 集成
  • 工具调用能力:
    能够直接执行文件读写、代码搜索、Shell 命令等操作
  • 多智能体协作:
    支持启动子代理处理复杂任务
  • 无限上下文:
    通过自动压缩机制突破模型上下文窗口限制
  • 权限可控:
    细粒度的工具调用权限管理

1.2 源码仓库结构

src/
├── main.tsx                 # 主入口(4683行)- REPL交互主循环
├── query.ts                 # 查询核心循环(1729行)
├── QueryEngine.ts           # 会话引擎(1295行)
├── Tool.ts                  # 工具类型定义(792行)
├── tools.ts                 # 工具注册(389行)
├── commands.ts              # 斜杠命令(754行)
├── constants/
│   └── prompts.ts           # 系统提示词
├── tools/                   # 各工具实现
│   ├── AgentTool/           # 多智能体
│   ├── BashTool/            # Shell执行
│   ├── FileEditTool/        # 文件编辑
│   ├── FileReadTool/        # 文件读取
│   └── ...
├── services/
│   ├── api/                 # API调用层
│   ├── compact/             # 上下文压缩
│   ├── mcp/                 # MCP协议实现
│   └── analytics/           # 埋点分析
├── utils/                   # 工具函数
│   ├── permissions/         # 权限管理
│   ├── tokens.ts            # Token计算
│   └── messages.ts          # 消息处理
└── components/              # React/Ink UI组件

1.3 核心架构图

┌─────────────────────────────────────────────────────────────┐
│                      CLI Entry Point                        │
│                     (main.tsx)                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    QueryEngine                              │
│  - 会话生命周期管理                                          │
│  - 消息持久化                                                │
│  - SDK 接口封装                                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    query() 主循环                            │
│  - 消息处理流水线                                            │
│  - 工具调用协调                                              │
│  - 自动压缩触发                                              │
└─────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
        ┌──────────┐   ┌──────────┐   ┌──────────┐
        │  API层   │   │ 工具系统 │   │ 压缩服务 │
        │ claude.ts│   │Tool.ts   │   │compact.ts│
        └──────────┘   └──────────┘   └──────────┘

1.4 技术栈分析

Claude Code 采用以下核心技术栈:

技术
用途
特点
TypeScript
主要开发语言
强类型,支持泛型模式
Bun
运行时 + 打包器
原生支持 bun:bundle 特性开关
React + Ink
终端UI渲染
声明式UI,组件化设计
Zod
Schema验证
工具输入验证,类型推导
MCP
工具协议
外部工具集成标准

2. 核心查询循环:query.ts 深度剖析

2.1 query() 函数的设计哲学

query.ts 是整个系统的核心引擎,其主函数 query() 实现了一个状态机驱动的异步生成器。这个设计体现了几个关键理念:

  1. 生成器模式:
    使用 AsyncGenerator 实现流式输出,允许调用方逐步消费结果
  2. 状态机循环:
    通过 while (true) 循环和 continue 语句实现状态转移
  3. 不可变参数 + 可变状态:
    参数对象不变,状态对象在循环中更新
// query.ts:108-119
exportasyncfunctionquery(
params:QueryParams,
): AsyncGenerator<
  | StreamEvent
  | RequestStartEvent
  | Message
  | TombstoneMessage
  | ToolUseSummaryMessage,
Terminal
> {
constconsumedCommandUuids:string[] = []
const terminal = yieldqueryLoop(params, consumedCommandUuids)
// ... 命令生命周期通知
return terminal
}

2.2 状态对象设计

循环状态通过 State 类型封装,这是一个精心设计的结构:

// query.ts:91-103
typeState = {
messages:Message[]              // 消息历史
toolUseContext:ToolUseContext// 工具调用上下文
autoCompactTracking:AutoCompactTrackingState | undefined// 压缩追踪
maxOutputTokensRecoveryCount:number// 输出token恢复计数
hasAttemptedReactiveCompact:boolean// 是否尝试过响应式压缩
maxOutputTokensOverride:number | undefined
pendingToolUseSummary:Promise<ToolUseSummaryMessage | null> | undefined
stopHookActive:boolean | undefined
turnCount:number// 轮次计数
transition:Continue | undefined// 上次转移原因
}

设计亮点:将所有可变状态集中在一个对象中,使得状态转移变成原子操作:

// query.ts:1192
constnext:State = {
messages: [...messagesForQuery, ...assistantMessages, ...toolResults],
toolUseContext: toolUseContextWithQueryTracking,
autoCompactTracking: tracking,
turnCount: nextTurnCount,
// ...
}
state = next
continue// 进入下一轮循环

2.3 查询循环的九大阶段

queryLoop 函数将查询处理分为九个阶段:

┌─────────────────────────────────────────────────────────────┐
│ 阶段1: 消息预处理                                            │
│  - 工具结果预算应用                                          │
│  - Snip压缩                                                  │
│  - Microcompact                                              │
│  - Context Collapse                                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段2: 自动压缩检查                                          │
│  - Token阈值计算                                             │
│  - 会话内存压缩                                              │
│  - 传统压缩                                                  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段3: 阻塞限制检查                                          │
│  - 硬限制检查(自动压缩关闭时)                               │
│  - 跳过条件判断                                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段4: API调用                                               │
│  - 流式调用                                                  │
│  - 模型回退处理                                              │
│  - 错误恢复                                                  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段5: 响应处理                                              │
│  - 助手消息收集                                              │
│  - 工具调用提取                                              │
│  - 流式工具执行                                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段6: 错误恢复                                              │
│  - Prompt-too-long 恢复                                      │
│  - Max-output-tokens 恢复                                    │
│  - 媒体大小错误恢复                                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段7: Stop Hook处理                                         │
│  - 阻塞错误注入                                              │
│  - 终止条件判断                                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段8: 工具执行                                              │
│  - 并发安全检查                                              │
│  - 工具调度                                                  │
│  - 结果收集                                                  │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 阶段9: 附件处理与递归                                        │
│  - 内存预取消费                                              │
│  - 技能发现                                                  │
│  - 状态更新与递归                                            │
└─────────────────────────────────────────────────────────────┘

2.4 工具执行流程详解

当模型返回工具调用时,系统进入工具执行阶段:

// query.ts:1036-1071
const toolUpdates = streamingToolExecutor
  ? streamingToolExecutor.getRemainingResults()
  : runTools(toolUseBlocks, assistantMessages, canUseTool, toolUseContext)

forawait (const update of toolUpdates) {
if (update.message) {
yield update.message
    toolResults.push(...)
  }
if (update.newContext) {
    updatedToolUseContext = {...update.newContext, queryTracking}
  }
}

关键设计:

  • 支持流式工具执行(StreamingToolExecutor)和传统顺序执行两种模式
  • 工具可以修改上下文(contextModifier
  • 进度消息即时yield,不等待工具完成

2.5 错误恢复机制

系统实现了多层错误恢复策略:

2.5.1 Prompt-Too-Long 恢复

// query.ts:835-880
if (isWithheld413) {
// 首先尝试 drained context-collapses
if (contextCollapse && state.transition?.reason !== 'collapse_drain_retry') {
const drained = contextCollapse.recoverFromOverflow(messagesForQuery, querySource)
if (drained.committed > 0) {
      state = {..., transition: { reason:'collapse_drain_retry', ...}}
continue
    }
  }
// 然后尝试 reactive compact
if (reactiveCompact) {
const compacted = await reactiveCompact.tryReactiveCompact({...})
if (compacted) {
      state = {..., transition: { reason:'reactive_compact_retry' }}
continue
    }
  }
}

2.5.2 Max-Output-Tokens 恢复

// query.ts:902-929
if (isWithheldMaxOutputTokens(lastMessage)) {
// 升级到64k输出
if (maxOutputTokensOverride === undefined) {
    state = {..., maxOutputTokensOverride:ESCALATED_MAX_TOKENS}
continue
  }
// 注入恢复消息
if (maxOutputTokensRecoveryCount < MAX_OUTPUT_TOKENS_RECOVERY_LIMIT) {
const recoveryMessage = createUserMessage({
content:`Output token limit hit. Resume directly...`,
isMeta:true,
    })
    state = {..., messages: [..., recoveryMessage]}
continue
  }
}

2.6 QueryEngine:会话生命周期管理

QueryEngine 类封装了会话级别的状态管理和 SDK 接口:

// QueryEngine.ts:66-82
exportclassQueryEngine {
privateconfig:QueryEngineConfig
privatemutableMessages:Message[]
privateabortController:AbortController
privatepermissionDenials:SDKPermissionDenial[]
privatetotalUsage:NonNullableUsage
privatereadFileState:FileStateCache
private discoveredSkillNames = newSet<string>()
private loadedNestedMemoryPaths = newSet<string>()

async *submitMessage(prompt:string | ContentBlockParam[], options?) {
// ... 完整的查询生命周期
  }
}

核心职责:

  1. 消息持久化:
    自动记录会话到磁盘
  2. 使用量追踪:
    累计 API 调用的 token 消耗
  3. 权限追踪:
    记录被拒绝的工具调用
  4. 中断处理:
    支持外部中断正在进行的查询

3. 工具系统设计:从抽象到实现

3.1 Tool 类型定义

Tool.ts 定义了工具系统的核心抽象,这是一个约 800 行的类型定义文件,体现了精心的接口设计:

// Tool.ts:192-295
exporttypeTool<
InputextendsAnyObject = AnyObject,
Output = unknown,
  P extendsToolProgressData = ToolProgressData,
> = {
// 核心方法
call(args: z.infer<Input>, context:ToolUseContext, ...): Promise<ToolResult<Output>>
description(input, options): Promise<string>
readonlyinputSchema:Input
readonlyname:string

// 权限相关
checkPermissions(input, context): Promise<PermissionResult>
  validateInput?(input, context): Promise<ValidationResult>

// 行为标记
isEnabled(): boolean
isConcurrencySafe(input): boolean
isReadOnly(input): boolean
  isDestructive?(input): boolean
  interruptBehavior?(): 'cancel' | 'block'

// UI渲染
renderToolUseMessage(input, options): React.ReactNode
  renderToolResultMessage?(content, progressMessages, options): React.ReactNode

// 其他...
}

3.2 buildTool 工厂函数

为了避免重复实现默认方法,系统提供了 buildTool 工厂函数:

// Tool.ts:323-355
constTOOL_DEFAULTS = {
isEnabled:() =>true,
isConcurrencySafe:(_input?: unknown) =>false,  // 默认不安全
isReadOnly:(_input?: unknown) =>false,          // 默认写入
isDestructive:(_input?: unknown) =>false,
checkPermissions:(input, _ctx) =>
Promise.resolve({ behavior:'allow'updatedInput: input }),
toAutoClassifierInput:(_input?: unknown) =>'',
userFacingName:(_input?: unknown) =>'',
}

exportfunction buildTool<D extendsAnyToolDef>(def: D): BuiltTool<D> {
return {
    ...TOOL_DEFAULTS,
userFacingName:() => def.name,
    ...def,
  } asBuiltTool<D>
}

设计思想:

  • Fail-closed 默认值:isConcurrencySafe
     默认 false,isReadOnly 默认 false,保守策略避免意外并发问题
  • 类型安全:
    通过泛型约束确保 BuiltTool<D> 类型正确
  • 覆盖优先级:
    工具定义覆盖默认值,默认值覆盖基类

3.3 工具注册机制

tools.ts 实现了工具的注册和过滤逻辑:

// tools.ts:133-179
exportfunctiongetAllBaseTools(): Tools {
return [
AgentTool,
TaskOutputTool,
BashTool,
    ...(hasEmbeddedSearchTools() ? [] : [GlobToolGrepTool]),
ExitPlanModeV2Tool,
FileReadTool,
FileEditTool,
FileWriteTool,
// ... 条件加载的工具
   ...(feature('COORDINATOR_MODE') && coordinatorModeModule?.isCoordinatorMode()
      ? [AgentToolTaskStopToolgetSendMessageTool()]
      : []),
// ...
  ]
}

exportfunctiongetTools(permissionContext:ToolPermissionContext): Tools {
// --simple 模式:只有 Bash, Read, Edit
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
returnfilterToolsByDenyRules([BashToolFileReadToolFileEditTool], permissionContext)
  }

// 正常模式
const tools = getAllBaseTools().filter(tool => !specialTools.has(tool.name))
let allowedTools = filterToolsByDenyRules(tools, permissionContext)

// REPL模式:隐藏原始工具
if (isReplModeEnabled()) {
    allowedTools = allowedTools.filter(tool => !REPL_ONLY_TOOLS.has(tool.name))
  }

return allowedTools.filter((_, i) => isEnabled[i])
}

3.4 工具权限检查流程

工具调用前需要经过多层权限检查:

┌─────────────────────────────────────────────────────────────┐
│ 1. validateInput() - 输入验证                                │
│    检查输入是否合法,返回错误信息                             │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 检查 Deny Rules                                           │
│    如果工具有 blanket deny 规则,直接拒绝                     │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. checkPermissions() - 工具特定权限检查                     │
│    工具自定义的权限逻辑(如路径白名单)                        │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. 检查 Allow Rules                                          │
│    如果匹配 allow 规则,自动允许                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 5. 检查 Ask Rules                                            │
│    如果匹配 ask 规则,提示用户确认                            │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 6. 默认行为(根据 PermissionMode)                           │
│    - default: 提示用户                                       │
│    - accept-edits: 自动允许编辑                              │
│    - plan: 特殊处理                                          │
│    - bypass: 自动允许所有                                    │
└─────────────────────────────────────────────────────────────┘

3.5 MCP 工具集成

MCP (Model Context Protocol) 工具通过特殊的包装器集成:

// Tool.ts 中的 MCP 相关字段
exporttypeTool<...> = {
// ...
isMcp?: boolean
mcpInfo?: { serverName:stringtoolName:string }
readonlyshouldDefer?: boolean// 延迟加载
readonlyalwaysLoad?: boolean// 始终加载
}

MCP 工具名称格式:mcp__<server_name>__<tool_name>


4. 系统提示词工程:塑造 AI 行为

4.1 系统提示词架构

Claude Code 的系统提示词采用分层模块化架构,分为静态部分和动态部分:

系统提示词
├── 静态部分(可全局缓存)
│   ├── 身份与能力介绍
│   ├── 系统规则
│   ├── 任务执行指南
│   ├── 行为准则
│   ├── 工具使用指南
│   └── 输出效率要求

└── 动态部分(会话特定)
    ├── 会话特定指导
    ├── 内存内容(MEMORY.md)
    ├── 环境信息
    ├── 语言设置
    ├── MCP服务器指令
    └── 功能特性配置

4.2 核心提示词段落分析

4.2.1 身份与能力声明

// prompts.ts:159-162
functiongetSimpleIntroSection(outputStyleConfig): string {
return`
You are an interactive agent that helps users ${outputStyleConfig !== null
  ? 'according to your "Output Style" below...':'with software engineering tasks.'}


IMPORTANT: You must NEVER generate or guess URLs for the user unless you are confident...
`

}

设计意图:明确 AI 的角色定位,同时防止幻觉 URL。

4.2.2 工具使用指南

// prompts.ts:251-283
functiongetUsingYourToolsSection(enabledTools:Set<string>): string {
const items = [
`Do NOT use the Bash tool to run commands when a relevant dedicated tool is provided:`,
    [
`To read files use Read instead of cat, head, tail, or sed`,
`To edit files use Edit instead of sed or awk`,
`To create files use Write instead of cat with heredoc`,
`To search for files use Glob instead of find or ls`,
`To search the content of files, use Grep instead of grep or rg`,
    ],
`You can call multiple tools in a single response...`,
  ]
}

关键原则:

  1. 专用工具优先:
    引导模型使用语义更明确的专用工具
  2. 并行调用鼓励:
    支持一次响应中调用多个独立工具
  3. 任务管理集成:
    引导使用 TodoWrite 进行任务追踪

4.2.3 代码风格指导

// prompts.ts:179-199
const codeStyleSubitems = [
`Don't add features, refactor code, or make "improvements" beyond what was asked...`,
`Don't add error handling, fallbacks, or validation for scenarios that can't happen...`,
`Don't create helpers, utilities, or abstractions for one-time operations...`,
// Ant-only: 注释指导
`Default to writing no comments. Only add one when the WHY is non-obvious...`,
`Don't explain WHAT the code does, since well-named identifiers already do that...`,
]

设计哲学:

  • YAGNI 原则:
    不添加不需要的功能
  • 最小抽象:
    避免过度工程化
  • 信任框架:
    不在边界外做防御性编程

4.3 动态边界标记

为了支持提示词缓存,系统引入了动态边界标记:

// prompts.ts:62-70
exportconstSYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

/**
 * 边界标记:之前的内容可全局缓存,之后的内容是会话特定的
 */

缓存策略:

静态部分 ──> scope: 'global'(跨用户共享缓存)
动态部分 ──> scope: 'session'(会话级缓存或不缓存)

4.4 输出效率优化

针对 Ant 内部用户,系统增加了更详细的输出效率指导:

// prompts.ts:368-388 (Ant-only)
return`# Communicating with the user
When sending user-facing text, you're writing for a person, not logging to a console...
- Brief updates at key moments
- Complete, grammatically correct sentences
- Match the task: simple question → direct answer, not headers and numbered sections
- Inverted pyramid when appropriate (leading with the action)
`

4.5 主动模式提示词

当启用 Proactive 模式时,系统注入特殊的自主工作指导:

// prompts.ts:447-510
functiongetProactiveSection(): string | null {
return`
# Autonomous work
You are running autonomously. You will receive \`<tick>\` prompts...

## Pacing
Use the Sleep tool to control how long you wait between actions...

## Bias toward action
- Read files, search code, explore the project, run tests — all without asking
- Make code changes. Commit when you reach a good stopping point.
- If you're unsure between two reasonable approaches, pick one and go.

## Terminal focus
- Unfocused: Lean heavily into autonomous action
- Focused: Be more collaborative — surface choices, ask before committing
`

}

5. 上下文管理:无限对话的秘密

5.1 问题背景

大语言模型有固定的上下文窗口限制(如 200K tokens),当对话历史超过这个限制时,系统需要进行压缩。Claude Code 实现了多层次的上下文管理策略

5.2 自动压缩机制

5.2.1 触发条件

// autoCompact.ts:59-74
exportfunctiongetAutoCompactThreshold(model:string): number {
const effectiveContextWindow = getEffectiveContextWindowSize(model)
return effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS// 13K buffer
}

exportasyncfunctionshouldAutoCompact(
messages:Message[],
model:string,
querySource?: QuerySource,
  snipTokensFreed = 0,
): Promise<boolean> {
// 各种跳过条件...

const tokenCount = tokenCountWithEstimation(messages) - snipTokensFreed
const threshold = getAutoCompactThreshold(model)

return tokenCount >= threshold
}

触发阈值计算:

触发阈值 = 有效上下文窗口 - 13K tokens 缓冲区

有效上下文窗口 = 模型上下文窗口 - 预留输出tokens (20K)

5.2.2 压缩执行流程

// autoCompact.ts:140-190
exportasyncfunctionautoCompactIfNeeded(...): Promise<{
wasCompacted:boolean
compactionResult?: CompactionResult
}> {
// 熔断器:连续失败超过3次则跳过
if (tracking?.consecutiveFailures >= MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES) {
return { wasCompacted:false }
  }

// 首先尝试会话内存压缩
const sessionMemoryResult = awaittrySessionMemoryCompaction(...)
if (sessionMemoryResult) {
return { wasCompacted:truecompactionResult: sessionMemoryResult }
  }

// 回退到传统压缩
const compactionResult = awaitcompactConversation(...)
return { wasCompacted:true, compactionResult }
}

5.3 压缩策略详解

5.3.1 会话内存压缩

// sessionMemoryCompact.ts (简化)
exportasyncfunctiontrySessionMemoryCompaction(
messages:Message[],
agentId:string | undefined,
threshold:number,
): Promise<CompactionResult | null> {
// 使用专门的记忆模型进行压缩
// 保留关键上下文信息
// 返回压缩后的消息列表
}

5.3.2 传统压缩

// compact.ts:151-200 (简化)
exportasyncfunctioncompactConversation(
messages:Message[],
toolUseContext:ToolUseContext,
cacheSafeParams:CacheSafeParams,
suppressUserQuestions:boolean,
customInstructions?: string,
isAutoCompact:boolean,
recompactionInfo:RecompactionInfo,
): Promise<CompactionResult> {
// 1. 执行预压缩 hooks
awaitexecutePreCompactHooks(...)

// 2. 分析上下文,识别需要保留的关键信息
const analysis = awaitanalyzeContext(messages)

// 3. 调用压缩模型生成摘要
const summary = awaitcallCompactModel(...)

// 4. 构建压缩后的消息
const postCompactMessages = buildPostCompactMessages(summary, ...)

// 5. 执行后压缩 hooks
awaitexecutePostCompactHooks(...)

return {
    preCompactTokenCount,
    postCompactTokenCount,
summaryMessages: [...],
attachments: [...],
  }
}

5.4 响应式压缩

当 API 返回 prompt_too_long 错误时,系统触发响应式压缩:

// query.ts:843-865
if (isWithheld413) {
// 首先尝试 drain context-collapses
const drained = contextCollapse?.recoverFromOverflow(messagesForQuery, querySource)
if (drained?.committed > 0) {
    state = {..., transition: { reason:'collapse_drain_retry' }}
continue
  }

// 然后尝试 reactive compact
const compacted = await reactiveCompact?.tryReactiveCompact({
hasAttempted: hasAttemptedReactiveCompact,
    ...
  })
if (compacted) {
    state = {..., transition: { reason:'reactive_compact_retry' }}
continue
  }
}

5.5 Snip 压缩

最新的 Snip 压缩是一种更激进的裁剪策略:

// snipCompact.ts (简化)
exportfunctionsnipCompactIfNeeded(
messages:Message[],
options?: { force: boolean }
): { messages:Message[]; tokensFreed:numberboundaryMessage?: Message } {
// 识别可裁剪的消息段
// 保留最近的助手消息和用户消息
// 返回裁剪后的消息和释放的 tokens
}

5.6 压缩流程图

Token 使用量
    │
    │     ┌─────────────────────── 90% ───────────────────────┐
    │     │               Context Collapse Commit             │
    │     └───────────────────────────────────────────────────┘
    │
    │     ┌─────────────────────── 93% ───────────────────────┐
    │     │                Auto Compact 触发                  │
    │     └───────────────────────────────────────────────────┘
    │
    │     ┌─────────────────────── 95% ───────────────────────┐
    │     │              Context Collapse Blocking            │
    │     └───────────────────────────────────────────────────┘
    │
    │     ┌─────────────────────── 97% ───────────────────────┐
    │     │                 Manual Compact Limit              │
    │     └───────────────────────────────────────────────────┘
    │
    │     ┌─────────────────────── 100% ──────────────────────┐
    │     │                   API 413 Error                  │
    │     │              → Reactive Compact                  │
    │     └───────────────────────────────────────────────────┘
    │
    └─────────────────────────────────────────────────────────► 时间

6. 权限系统:安全与便利的平衡

6.1 权限模型概述

Claude Code 的权限系统采用多层级规则引擎设计:

权限决策流程
├── 1. Deny Rules(拒绝规则)—— 最高优先级
├── 2. 工具特定 checkPermissions()
├── 3. Allow Rules(允许规则)
├── 4. Ask Rules(询问规则)
└── 5. Permission Mode(权限模式默认行为)

6.2 权限规则类型

// PermissionRule.ts
exporttypePermissionRuleValue = {
toolName:string
ruleContent?: string// 可选的具体参数匹配
}

exporttypePermissionRule = {
source:PermissionRuleSource// 规则来源
ruleBehavior:'allow' | 'deny' | 'ask'
ruleValue:PermissionRuleValue
}

规则示例:

  • Bash
     —— 允许/拒绝所有 Bash 命令
  • Bash(git *)
     —— 允许/拒绝以 git 开头的命令
  • Read
     —— 允许/拒绝所有文件读取
  • mcp__server1
     —— 允许/拒绝某 MCP 服务器的所有工具

6.3 权限检查核心逻辑

// permissions.ts:150-200 (简化)
exportasyncfunctioncanUseTool(
tool:Tool,
input:unknown,
context:ToolUseContext,
assistantMessage:AssistantMessage,
toolUseID:string,
forceDecision?: boolean,
): Promise<PermissionResult> {
// 1. 检查 deny rules
const denyRule = getDenyRuleForTool(permissionContext, tool)
if (denyRule) {
return { behavior:'deny'reason:'deny_rule', ... }
  }

// 2. 调用工具特定的权限检查
const toolPermission = await tool.checkPermissions(input, context)
if (toolPermission.behavior !== 'allow') {
return toolPermission
  }

// 3. 检查 allow rules
const allowRule = toolAlwaysAllowedRule(permissionContext, tool)
if (allowRule) {
return { behavior:'allow'reason:'allow_rule', ... }
  }

// 4. 检查 ask rules
const askRule = getAskRuleForTool(permissionContext, tool)
if (askRule) {
return { behavior:'ask'reason:'ask_rule', ... }
  }

// 5. 根据 PermissionMode 决定
returngetDefaultPermissionResult(permissionContext.mode)
}

6.4 Permission Mode

系统支持多种权限模式:

// permissions.ts
typePermissionMode = 
  | 'default'// 每次都询问
  | 'accept-edits'// 自动接受编辑操作
  | 'plan'// 规划模式
  | 'bypass'// 跳过所有权限检查
  | 'auto'// 自动模式(基于分类器)

6.5 Bash 命令权限检查

Bash 工具有特殊的权限检查逻辑,支持子命令级别的控制:

// BashTool/checkPermissions.ts (简化)
exportasyncfunctioncheckBashPermissions(
input: { command: string },
context:ToolUseContext,
): Promise<PermissionResult> {
const { command } = input

// 解析命令,处理管道、子shell等
const subcommands = parseBashCommand(command)

// 对每个子命令单独检查
const results = awaitPromise.all(
    subcommands.map(cmd =>checkSingleCommand(cmd, context))
  )

// 如果任何子命令需要询问,则整个命令需要询问
if (results.some(r => r.behavior === 'ask')) {
return { 
behavior:'ask'
reason: { type:'subcommandResults'reasons: [...] } 
    }
  }

return { behavior:'allow' }
}

6.6 安全机制

6.6.1 沙箱隔离

// BashTool/shouldUseSandbox.ts
exportfunctionshouldUseSandbox(command:string): boolean {
// 在沙箱中执行命令,隔离风险
// 通过 SandboxManager 管理沙箱环境
}

6.6.2 危险操作检测

// permissions.ts
functionclassifyYoloAction(
tool:Tool,
input:unknown,
context:ToolUseContext,
): 'safe' | 'unsafe' | 'ambiguous' {
// 检测潜在的破坏性操作
// 如:rm -rf、强制推送、删除分支等
}

7. 多智能体架构:Agent Tool 的设计哲学

7.1 Agent Tool 概述

Agent Tool 是 Claude Code 实现多智能体协作的核心机制,它允许主对话启动专门的子代理来处理特定任务。

7.2 Agent 定义结构

// loadAgentsDir.ts
exporttypeAgentDefinition = {
agentType:string// 代理类型标识
whenToUse:string// 何时使用描述
tools?: string[]          // 可用工具白名单
disallowedTools?: string[] // 禁用工具黑名单
model?: string// 使用的模型
systemPrompt?: string// 自定义系统提示词
isBuiltin?: boolean// 是否内置代理
}

7.3 Fork 子代理

最新的 Fork 功能允许主对话创建一个继承完整上下文的子代理:

// prompt.ts:32-55
const whenToForkSection = `
## When to fork

Fork yourself (omit \`subagent_type\`) when the intermediate tool output isn't worth keeping in your context.

- **Research**: fork open-ended questions
- **Implementation**: prefer to fork implementation work that requires more than a couple of edits

**Don't peek.** The tool result includes an \`output_file\` path — do not Read it...
**Don't race.** After launching, you know nothing about what the fork found.
`

Fork vs Subagent 对比:

特性
Fork
Subagent
上下文继承
完全继承
空白开始
提示词缓存
共享父级
独立
适用场景
研究任务、实现工作
特定专业化任务
输出处理
后台执行,通知完成
前台执行,等待结果

7.4 Agent Tool 提示词设计

// prompt.ts:150-200
exportasyncfunctiongetPrompt(
agentDefinitions:AgentDefinition[],
isCoordinator?: boolean,
): Promise<string> {
return`
Launch a new agent to handle complex, multi-step tasks autonomously.

The Agent tool launches specialized agents (subprocesses) that autonomously handle complex tasks.

Available agent types:
${agentDefinitions.map(a => `- ${a.agentType}:${a.whenToUse}`).join('\n')}

## Writing the prompt
Brief the agent like a smart colleague who just walked into the room...
- Explain what you're trying to accomplish and why
- Describe what you've already learned or ruled out
- Give enough context about the surrounding problem

**Never delegate understanding.** Don't write "based on your findings, fix the bug"...
`

}

7.5 内置代理类型

// builtInAgents.ts
exportconstBUILTIN_AGENTS = [
  {
agentType:'doc-url-retriever',
whenToUse:'Fetches URLs to retrieve documentation',
tools: ['WebFetch'],
  },
  {
agentType:'code-reviewer',
whenToUse:'Reviews code for quality and security issues',
tools: ['Read''Grep''Glob'],
  },
  {
agentType:'test-runner',
whenToUse:'Runs tests and reports results',
tools: ['Bash''Read'],
  },
// ...
]

7.6 Worktree 隔离

Agent Tool 支持 Worktree 隔离模式:

// prompt.ts
`
- You can optionally set \`isolation: "worktree"\` to run the agent in a temporary git worktree
  - Gives it an isolated copy of the repository
  - Automatically cleaned up if no changes made
  - If changes made, returns worktree path and branch
`


8. 并发执行与 StreamingToolExecutor

8.1 设计目标

StreamingToolExecutor 解决的核心问题是:如何在保持工具执行顺序的同时,最大化并行执行效率?

8.2 并发安全分类

// StreamingToolExecutor.ts:14-18
typeTrackedTool = {
id:string
block:ToolUseBlock
status:ToolStatus// 'queued' | 'executing' | 'completed' | 'yielded'
isConcurrencySafe:boolean// 是否可并发执行
// ...
}

并发规则:

  • isConcurrencySafe = true
     的工具可以与其他安全工具并行
  • isConcurrencySafe = false
     的工具必须独占执行
  • 非安全工具必须等待所有执行中的工具完成

8.3 执行调度逻辑

// StreamingToolExecutor.ts:70-95
privatecanExecuteTool(isConcurrencySafe:boolean): boolean {
const executingTools = this.tools.filter(t => t.status === 'executing')
return (
    executingTools.length === 0 ||
    (isConcurrencySafe && executingTools.every(t => t.isConcurrencySafe))
  )
}

privateasyncprocessQueue(): Promise<void> {
for (const tool ofthis.tools) {
if (tool.status !== 'queued'continue

if (this.canExecuteTool(tool.isConcurrencySafe)) {
awaitthis.executeTool(tool)
    } else {
// 不能执行,且非并发安全工具必须等待
if (!tool.isConcurrencySafebreak
    }
  }
}

8.4 错误传播机制

当某个工具执行失败时,系统需要决定是否取消其他工具:

// StreamingToolExecutor.ts:150-180
privateasyncexecuteTool(tool:TrackedTool): Promise<void> {
// ...
forawait (const update of generator) {
const isErrorResult = update.message.type === 'user' && 
      update.message.message.content.some(c => c.type === 'tool_result' && c.is_error)

if (isErrorResult) {
// 只有 Bash 错误会取消兄弟进程
if (tool.block.name === BASH_TOOL_NAME) {
this.hasErrored = true
this.siblingAbortController.abort('sibling_error')
      }
    }
// ...
  }
}

设计决策:只有 Bash 命令错误才会取消并行执行的其他命令,因为 Bash 命令通常有隐式依赖链(如 mkdir 失败 → 后续命令无意义)。

8.5 进度消息即时输出

// StreamingToolExecutor.ts:200-220
*getCompletedResults(): Generator<MessageUpdate> {
for (const tool ofthis.tools) {
// 进度消息立即 yield,不等待工具完成
while (tool.pendingProgress.length > 0) {
const progressMessage = tool.pendingProgress.shift()!
yield { message: progressMessage, newContext:this.toolUseContext }
    }

if (tool.status === 'completed' && tool.results) {
for (const message of tool.results) {
yield { message, newContext:this.toolUseContext }
      }
    }
  }
}

8.6 执行流程图

工具到达顺序: [Read(A), Read(B), Bash(cmd), Read(C)]

时间线:
T0: Read(A) 到达,isConcurrencySafe=true,开始执行
T1: Read(B) 到达,isConcurrencySafe=true,与 A 并行执行
T2: Bash(cmd) 到达,isConcurrencySafe=false,进入队列等待
T3: Read(C) 到达,isConcurrencySafe=true,进入队列等待
T4: Read(A) 完成,yield 结果
T5: Read(B) 完成,yield 结果
T6: Bash(cmd) 开始执行(独占)
T7: Bash(cmd) 完成
T8: Read(C) 开始执行
T9: Read(C) 完成

输出顺序: Read(A)结果 → Read(B)结果 → Bash结果 → Read(C)结果

9. 技术债分析与改进建议

9.1 代码复杂度问题

9.1.1 main.tsx 过于庞大

问题:main.tsx 文件有 4683 行,包含了 REPL 主循环、UI 渲染、状态管理等大量逻辑。

建议:

// 重构前:main.tsx 包含所有逻辑
// 重构后:
src/
├── main.tsx              # 入口,只做引导
├── repl/
│   ├── REPL.tsx          # REPL 组件
│   ├── useREPLState.ts   # 状态管理 hook
│   └── useMessageProcessor.ts # 消息处理 hook

9.1.2 query.ts 状态管理复杂

问题:State 类型有 10 个字段,状态转移逻辑分散在多处。

建议:引入状态机库或模式匹配:

// 使用状态机模式
typeQueryState = 
  | { type:'idle' }
  | { type:'streaming'abortController:AbortController }
  | { type:'executing_tools'tools:ToolUseBlock[] }
  | { type:'compacting'previousMessages:Message[] }
  | { type:'terminal'reason:TerminalReason }

functiontransition(state:QueryStateevent:QueryEvent): QueryState {
// 集中的状态转移逻辑
}

9.2 类型安全问题

9.2.1 过多的类型断言

问题:代码中存在大量 as 类型断言,可能隐藏类型错误。

// 潜在问题示例
const toolUseBlocks = message.message.content.filter(
content => content.type === 'tool_use',
asToolUseBlock[]  // 类型断言,不够安全

建议:使用类型守卫:

functionisToolUseBlock(content:ContentBlock): content is ToolUseBlock {
return content.type === 'tool_use'
}
const toolUseBlocks = message.message.content.filter(isToolUseBlock)

9.3 测试覆盖

9.3.1 缺少单元测试

问题:核心逻辑缺少独立的单元测试,主要依赖集成测试。

建议:为核心函数添加测试:

// __tests__/query.test.ts
describe('query'() => {
it('should handle tool_use correctly'async () => {
const mockTool = createMockTool({ name:'Test', ... })
const result = awaitcollectAsyncIterator(query({
messages: [createUserMessage('test')],
tools: [mockTool],
// ...
    }))
expect(result).toContainEqual(expect.objectContaining({
type:'assistant'
    }))
  })
})

9.4 错误处理改进

9.4.1 错误信息丢失

问题:某些地方的错误处理丢失了原始错误信息。

// 问题代码
try {
awaitsomeOperation()
catch (error) {
logError(error)
return { wasCompacted:false }  // 错误信息丢失
}

建议:保留错误上下文:

try {
awaitsomeOperation()
catch (error) {
logError(error)
return { 
wasCompacted:false
error:toError(error),
errorContext: { operation:'someOperation', ... }
  }
}

9.5 性能优化建议

9.5.1 减少不必要的序列化

问题:消息在多处被序列化和反序列化。

建议:使用结构化共享:

// 使用 Immutable.js 或类似库
import { ListRecord } from'immutable'

typeMessageStore = List<Message>

functionaddMessage(store:MessageStoremessage:Message): MessageStore {
return store.push(message)  // 结构共享,O(log32 n)
}

9.5.2 缓存优化

问题:某些计算被重复执行。

// 问题代码
functiongetTokenCount(messages:Message[]): number {
return messages.reduce((sum, m) => sum + estimateTokens(m), 0)
}
// 每次调用都重新计算

建议:使用记忆化:

const getTokenCount = memoize(
(messages:Message[]) => messages.reduce((sum, m) => sum + estimateTokens(m), 0),
  { maxSize:100 }
)

10. 工程亮点与借鉴价值

10.1 异步生成器的精妙应用

Claude Code 大量使用 AsyncGenerator 实现流式处理,这是一个值得学习的模式:

asyncfunctionprocessStream(source:AsyncIterable<Event>): AsyncGenerator<Result> {
forawait (const event of source) {
if (event.type === 'data') {
yieldtransform(event)
    } elseif (event.type === 'end') {
return
    }
  }
}

// 使用
forawait (const result ofprocessStream(source)) {
console.log(result)
}

优点:

  • 背压控制:消费者可以控制消费速度
  • 取消支持:可以通过 break 或 .return() 取消
  • 组合性:可以轻松链式组合多个生成器

10.2 特性开关设计

系统使用 bun:bundle 的 feature() 函数实现编译时特性开关:

import { feature } from'bun:bundle'

const snipModule = feature('HISTORY_SNIP')
  ? require('./services/compact/snipCompact.js')
  : null

优点:

  • 零运行时开销:未启用的代码在编译时被移除
  • 安全性:敏感代码不会进入外部构建
  • 灵活性:通过环境变量动态控制

10.3 提示词缓存策略

系统将提示词分为静态和动态部分,实现了跨用户的缓存共享:

const staticSections = [
getSimpleIntroSection(),
getSimpleSystemSection(),
// ... 静态内容
]

const dynamicSections = [
getSessionSpecificGuidanceSection(),
loadMemoryPrompt(),
// ... 动态内容
]

return [...staticSections, SYSTEM_PROMPT_DYNAMIC_BOUNDARY, ...dynamicSections]

缓存策略:

  • 静态部分 → scope: 'global'(跨用户共享)
  • 动态部分 → scope: 'session'(会话级)

10.4 优雅的中断处理

系统实现了多层中断机制:

// 主中断控制器
const abortController = newAbortController()

// 子控制器(不影响父级)
const childController = createChildAbortController(abortController)

// 兄弟中断(取消并行工具但不中断查询)
const siblingController = createChildAbortController(toolAbortController)

10.5 渐进式错误恢复

系统实现了多层错误恢复策略:

错误发生
    │
    ├── 第一层:轻量级恢复(drain collapse)
    │       ↓
    ├── 第二层:中等恢复(reactive compact)
    │       ↓
    ├── 第三层:重量级恢复(重试机制)
    │       ↓
    └── 最终:向用户报告错误

10.6 类型安全的工具定义

buildTool 函数实现了类型安全的工具定义:

constMyTool = buildTool({
name:'my_tool',
inputSchema: z.object({
path: z.string(),
content: z.string(),
  }),
call:async (input, context) => {
// input 类型自动推导为 { path: string; content: string }
return { data:'result' }
  },
// ...
})

结语

Claude Code 代表了 AI 编程助手的新范式:它不仅仅是一个代码补全工具,而是一个能够理解开发者意图、自主执行任务的智能协作伙伴。其源码中蕴含的工程智慧——从状态机驱动的查询循环到多层次上下文管理,从精细的权限系统到优雅的并发控制——都值得深入研究和借鉴。

通过这篇分析,希望读者能够:

  1. 理解 AI 编程助手的核心架构设计
  2. 掌握 TypeScript 异步编程的高级模式
  3. 学习如何构建可扩展、可维护的大型系统
  4. 获得 AI 应用开发的最佳实践

Claude Code 仍在快速发展中,其设计理念和技术实现也在不断演进。期待看到更多创新,也期待社区能从这些设计中汲取灵感,构建出更多优秀的 AI 应用。


本文基于 Claude Code 源码仓库分析编写,所有代码引用均注明了源文件位置。如有疑问或建议,欢迎交流讨论。