万字长文|Claude Code 源码深度解析:架构设计与工程智慧
本文基于 Claude Code 源码仓库进行深度技术分析,从核心架构、工具系统、权限管理、上下文压缩等多个维度剖析这一革命性 AI 编程助手的设计哲学与实现细节。
目录
-
项目概述与架构总览 -
核心查询循环:query.ts 深度剖析 -
工具系统设计:从抽象到实现 -
系统提示词工程:塑造 AI 行为 -
上下文管理:无限对话的秘密 -
权限系统:安全与便利的平衡 -
多智能体架构:Agent Tool 的设计哲学 -
并发执行与 StreamingToolExecutor -
技术债分析与改进建议 -
工程亮点与借鉴价值
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 采用以下核心技术栈:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
bun:bundle 特性开关 |
|
|
|
|
|
|
|
|
|
|
|
|
2. 核心查询循环:query.ts 深度剖析
2.1 query() 函数的设计哲学
query.ts 是整个系统的核心引擎,其主函数 query() 实现了一个状态机驱动的异步生成器。这个设计体现了几个关键理念:
- 生成器模式:
使用 AsyncGenerator实现流式输出,允许调用方逐步消费结果 - 状态机循环:
通过 while (true)循环和continue语句实现状态转移 - 不可变参数 + 可变状态:
参数对象不变,状态对象在循环中更新
// query.ts:108-119
exportasyncfunction* query(
params:QueryParams,
): AsyncGenerator<
| StreamEvent
| RequestStartEvent
| Message
| TombstoneMessage
| ToolUseSummaryMessage,
Terminal
> {
constconsumedCommandUuids:string[] = []
const terminal = yield* queryLoop(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?) {
// ... 完整的查询生命周期
}
}
核心职责:
- 消息持久化:
自动记录会话到磁盘 - 使用量追踪:
累计 API 调用的 token 消耗 - 权限追踪:
记录被拒绝的工具调用 - 中断处理:
支持外部中断正在进行的查询
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() ? [] : [GlobTool, GrepTool]),
ExitPlanModeV2Tool,
FileReadTool,
FileEditTool,
FileWriteTool,
// ... 条件加载的工具
...(feature('COORDINATOR_MODE') && coordinatorModeModule?.isCoordinatorMode()
? [AgentTool, TaskStopTool, getSendMessageTool()]
: []),
// ...
]
}
exportfunctiongetTools(permissionContext:ToolPermissionContext): Tools {
// --simple 模式:只有 Bash, Read, Edit
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
returnfilterToolsByDenyRules([BashTool, FileReadTool, FileEditTool], 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:string; toolName: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...`,
]
}
关键原则:
- 专用工具优先:
引导模型使用语义更明确的专用工具 - 并行调用鼓励:
支持一次响应中调用多个独立工具 - 任务管理集成:
引导使用 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:true, compactionResult: 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:number; boundaryMessage?: 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 对比:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.isConcurrencySafe) break
}
}
}
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:QueryState, event: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 { List, Record } from'immutable'
typeMessageStore = List<Message>
functionaddMessage(store:MessageStore, message: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 实现流式处理,这是一个值得学习的模式:
asyncfunction* processStream(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 编程助手的新范式:它不仅仅是一个代码补全工具,而是一个能够理解开发者意图、自主执行任务的智能协作伙伴。其源码中蕴含的工程智慧——从状态机驱动的查询循环到多层次上下文管理,从精细的权限系统到优雅的并发控制——都值得深入研究和借鉴。
通过这篇分析,希望读者能够:
-
理解 AI 编程助手的核心架构设计 -
掌握 TypeScript 异步编程的高级模式 -
学习如何构建可扩展、可维护的大型系统 -
获得 AI 应用开发的最佳实践
Claude Code 仍在快速发展中,其设计理念和技术实现也在不断演进。期待看到更多创新,也期待社区能从这些设计中汲取灵感,构建出更多优秀的 AI 应用。
本文基于 Claude Code 源码仓库分析编写,所有代码引用均注明了源文件位置。如有疑问或建议,欢迎交流讨论。
夜雨聆风