1. Claude Code 架构深度解析:从源码泄漏看Agent核心循环设计
2026年3月,Anthropic旗下Claude Code项目的完整源码意外泄漏,为我们打开了一扇窥探现代AI Agent系统设计的窗口。作为长期关注AI工程实践的开发者,我将基于泄漏的claude-code-new/src/目录核心文件,深入剖析这个支持复杂任务处理的Agent系统架构。
1.1 核心设计哲学
Claude Code的Agent系统建立在ReAct(Reasoning + Acting)范式之上,但进行了多项关键创新:
- 全链路流式处理:从用户输入到工具执行结果返回,整个流程采用TypeScript的AsyncGenerator实现惰性求值
- 迭代式状态管理:用
while(true)循环配合状态对象替代传统递归,避免调用栈溢出 - 并发控制体系:工具执行根据读写特性自动分区,实现安全并发
- 动态上下文管理:四级压缩策略应对长上下文挑战
1.2 核心模块拓扑
typescript复制// 典型调用链路示意
QueryEngine.submitMessage()
→ processUserInput()
→ query()
→ queryLoop() // 核心状态机
→ callModel()
→ queryModelWithStreaming()
→ queryModel() // 实际API调用
→ StreamingToolExecutor // 流式工具执行
→ runTools() // 非流式工具执行
→ runToolUse()
→ checkPermissionsAndCallTool()
2. 入口层:QueryEngine的设计奥秘
2.1 对话生命周期管理
QueryEngine类(src/QueryEngine.ts)是SDK和Headless模式的核心控制器,每个对话会话对应一个独立实例。其核心职责包括:
- 消息队列管理
- 中断控制(通过AbortController)
- 资源使用统计
- 文件状态缓存
typescript复制class QueryEngine {
private mutableMessages: Message[] // 对话历史
private abortController: AbortController // 中断信号
private totalUsage: NonNullableUsage // token统计
private readFileState: FileStateCache // 文件状态
async *submitMessage(prompt, options): AsyncGenerator<SDKMessage> {
// 处理流程...
}
}
2.2 消息处理流水线
当用户提交消息时,系统执行的标准处理流程:
- 输入预处理:解析斜杠命令、处理附件、构建消息对象
- 上下文注入:合并用户上下文(CLAUDE.md)和系统上下文(git status)
- 持久化记录:将消息写入transcript日志
- 进入核心循环:启动query()迭代器
关键细节:processUserInput()会处理特殊指令如
/reset,这些命令会修改引擎状态而不触发模型查询
3. 核心循环机制解析
3.1 queryLoop状态机设计
queryLoop()函数(src/query.ts)是系统的心脏,其状态对象设计值得关注:
typescript复制type State = {
messages: Message[] // 当前消息序列
toolUseContext: ToolUseContext // 工具运行时上下文
autoCompactTracking?: AutoCompactTrackingState
maxOutputTokensRecoveryCount: number // 输出token恢复尝试
hasAttemptedReactiveCompact: boolean // 是否尝试过反应式压缩
stopHookActive?: boolean // 停止钩子状态
turnCount: number // 当前轮次
transition?: Continue // 续转原因
}
3.2 单次迭代七阶段流程
每次循环迭代包含明确的阶段划分:
-
上下文准备阶段
- 启动技能预取(后台异步)
- 初始化查询追踪链(chainId + depth)
-
消息压缩管线
- Snip压缩:轻量级历史片段删除
- Microcompact:缓存友好的微压缩
- Context Collapse:上下文折叠
- Autocompact:完整上下文总结
-
模型调用阶段
- 初始化StreamingToolExecutor
- 动态模型选择(含降级逻辑)
- 流式API调用与实时工具触发
-
后处理阶段
- 中断检查
- 工具结果摘要生成
- 终止条件评估
-
工具执行阶段
- 并发/串行分区执行
- 权限检查与输入验证
- 执行结果收集
-
附件注入阶段
- 文件变更附件
- 记忆预取结果
- 队列命令消费
-
状态刷新阶段
- 工具上下文更新
- 轮次限制检查
- 构建下一轮状态
3.3 流式与非流式路径对比
| 特性 | 流式路径 | 非流式路径 |
|---|---|---|
| 触发条件 | API支持流式响应 | 流式失败降级 |
| 工具执行时机 | 边接收边执行 | 全部接收后执行 |
| 并发控制 | StreamingToolExecutor动态管理 | 显式分区后批量执行 |
| 内存占用 | 更低(增量处理) | 较高(需缓存完整响应) |
| 用户体验 | 响应更快 | 延迟明显 |
4. API调用层深度剖析
4.1 分层调用架构
Claude Code采用明确的分层设计隔离关注点:
code复制deps.callModel()
→ queryModelWithStreaming()
→ withStreamingVCR() // 录制/回放包装
→ queryModel() // 实际实现
→ anthropic.beta.messages.stream()
4.2 请求构建关键步骤
queryModel()函数(src/services/api/claude.ts)的请求准备流程包含多个优化点:
- 模型特性检测:检查Bedrock推理配置等平台特定特性
- 工具延迟加载:对大型工具集启用
defer_loading减少初始负载 - 消息规范化:确保tool_use与tool_result正确配对
- 系统提示组装:组合归属头、CLI前缀和用户提示
typescript复制function paramsFromContext(retryContext): BetaMessageStreamParams {
return {
model: normalizeModelStringForAPI(options.model),
max_tokens: getMaxOutputTokensForModel(options.model),
system: buildSystemPromptBlocks(systemPrompt),
messages: addCacheBreakpoints(messagesForAPI),
tools: toolSchemas,
thinking: thinkingConfig,
...getExtraBodyParams(betas)
}
}
4.3 流式事件处理机制
API流式响应通过精细的事件类型系统处理:
typescript复制for await (const event of stream) {
switch (event.type) {
case 'message_start':
// 记录首字节时间(TTFT)
case 'content_block_start':
// 区分thinking/text/tool_use块
case 'content_block_delta':
// 流式内容增量
case 'content_block_stop':
// 构建完整消息块
case 'message_delta':
// 更新usage统计
case 'message_stop':
// 最终统计处理
}
}
5. 工具执行系统详解
5.1 工具编排架构
工具执行层采用"编排-执行"分离设计:
code复制runTools() // 编排层
→ partitionToolCalls() // 并发分区
→ runToolsConcurrently() // 并行执行
→ runToolsSerially() // 串行执行
→ runToolUse() // 单个工具执行
→ checkPermissionsAndCallTool() // 带权限检查的执行
5.2 权限检查流水线
单个工具执行前经过严格的验证流程:
- 输入模式验证:使用Zod schema校验输入结构
- 自定义验证:执行工具定义的validateInput钩子
- 预执行钩子:
- 修改输入参数
- 实施权限控制
- 阻止继续执行
- 最终权限决策:综合hook结果和用户显式授权
typescript复制async function checkPermissionsAndCallTool(...) {
// 输入校验
const parsedInput = tool.inputSchema.safeParse(input)
// 自定义验证
const isValidCall = await tool.validateInput?.(parsedInput.data, ctx)
// Pre-Tool Hooks
for await (const result of runPreToolUseHooks(...)) {
// 处理各种hook结果
}
// 权限决策
const {decision} = await resolveHookPermissionDecision(...)
// 实际执行
const result = await tool.call(...)
// Post-Tool Hooks
for await (const hookResult of runPostToolUseHooks(...)) {
// 处理执行后修改
}
}
5.3 工具并发控制策略
工具执行根据安全特性自动分区:
| 工具特性 | 执行策略 | 典型工具 |
|---|---|---|
| 只读 + 无副作用 | 完全并行 | Web搜索、代码查询 |
| 写入操作 | 严格串行 | 文件编辑、数据库写入 |
| 系统级操作 | 隔离执行 | Bash命令、子进程调用 |
分区算法实现要点:
typescript复制function partitionToolCalls(toolUses) {
const batches = []
let currentBatch = []
for (const toolUse of toolUses) {
if (toolUse.isConcurrencySafe) {
currentBatch.push(toolUse)
} else {
if (currentBatch.length) batches.push(currentBatch)
batches.push([toolUse])
currentBatch = []
}
}
if (currentBatch.length) batches.push(currentBatch)
return batches
}
6. 流式工具执行器创新设计
6.1 StreamingToolExecutor核心机制
StreamingToolExecutor类(src/services/tools/StreamingToolExecutor.ts)实现了革命性的"边流边执行"模式:
- 动态队列管理:API流式响应时实时添加工具到执行队列
- 智能调度:根据工具安全特性自动决定并行/串行
- 结果流式回传:已完成工具结果立即yield,不等待整个响应结束
typescript复制class StreamingToolExecutor {
private tools: TrackedTool[] = []
private status: 'active'|'discarded' = 'active'
addTool(block: ToolUseBlock) {
// 立即评估并发安全性
const isSafe = tool.isConcurrencySafe(parsedInput)
this.tools.push({block, isConcurrencySafe: isSafe, status: 'queued'})
this.processQueue() // 触发执行
}
private canExecuteTool(isConcurrencySafe: boolean) {
const executing = this.tools.filter(t => t.status === 'executing')
return executing.length === 0 ||
(isConcurrencySafe && executing.every(t => t.isConcurrencySafe))
}
}
6.2 错误处理特殊机制
针对Bash工具的特殊处理体现了工程深度:
- 错误级联:Bash失败时通过siblingAbortController取消同级工具
- 安全隔离:非Bash工具错误不会影响其他工具执行
- 状态一致性:流式降级时自动废弃当前executor,新建实例保持干净状态
typescript复制// 在工具执行失败时的处理
if (isErrorResult && tool.block.name === BASH_TOOL_NAME) {
this.hasErrored = true
this.siblingAbortController.abort('sibling_error')
// 不会取消非并发安全的工具,确保写入操作原子性
}
7. 上下文管理高级策略
7.1 四级压缩管线
Claude Code采用渐进式压缩策略平衡成本和效果:
-
Snip压缩:
- 触发条件:启用HISTORY_SNIP特性标记
- 操作:删除旧消息片段+添加标记
- 成本:零API调用
-
Microcompact:
- 触发条件:每次迭代
- 操作:缓存友好的局部编辑
- 成本:零API调用
-
Context Collapse:
- 触发条件:启用CONTEXT_COLLAPSE特性
- 操作:折叠旧消息为可展开节点
- 成本:零API调用
-
Autocompact:
- 触发条件:token超阈值
- 操作:调用API生成完整摘要
- 成本:1次API调用
7.2 压缩算法实现示例
typescript复制async function applyCompactionPipeline(messages) {
// 阶段1:Snip压缩
let processed = snipCompactIfNeeded(messages)
// 阶段2:Microcompact
processed = await microcompact(processed)
// 阶段3:Context Collapse
const collapseResult = await contextCollapse.applyCollapsesIfNeeded(processed)
if (collapseResult.didCollapse) {
yield {type: 'context_collapse', ...}
processed = collapseResult.messages
}
// 阶段4:Autocompact
if (shouldAutocompact(processed)) {
const {compacted} = await autocompact(processed)
yield {type: 'autocompact', ...}
processed = compacted
}
return processed
}
7.3 动态token预算管理
当配置taskBudget时,系统会:
- 维护
taskBudgetRemaining计数器 - 每次压缩前快照上下文窗口大小
- 通过API的
output_config.task_budget.remaining传递剩余预算 - 服务端据此调整响应长度
8. 错误恢复体系解析
8.1 三级恢复机制对比
| 恢复类型 | 触发条件 | 恢复策略 | 重试类型 |
|---|---|---|---|
| Prompt Too Long | API返回413错误 | 1. Context Collapse释放暂存折叠 | collapse_drain_retry |
| 2. Reactive Compact紧急压缩 | reactive_compact_retry | ||
| 3. 放弃并返回错误 | prompt_too_long | ||
| Max Output Tokens | API返回token耗尽 | 1. 模型升级(8k→64k) | max_output_tokens_escalate |
| 2. 注入恢复提示 | max_output_tokens_recovery | ||
| 3. 终止对话 | completed | ||
| 模型降级 | 流式失败 | 切换fallbackModel | 普通继续 |
8.2 典型恢复流程示例
typescript复制async function handleModelError(error) {
if (error.type === 'prompt_too_long') {
// 第一级恢复尝试
const drained = await drainStagedCollapses()
if (drained) return {type: 'collapse_drain_retry'}
// 第二级恢复尝试
const compacted = await reactiveCompact()
if (compacted) return {type: 'reactive_compact_retry'}
// 最终失败
yield {type: 'error', message: 'Prompt too long'}
return {type: 'prompt_too_long'}
}
// 其他错误处理...
}
9. 子代理系统设计
9.1 runAgent核心流程
子代理通过runAgent()函数(src/tools/AgentTool/runAgent.ts)启动,关键步骤包括:
-
创建隔离上下文:
- 独立文件状态缓存
- 自定义用户/系统上下文
- 隔离的abortController
-
工具解析与初始化:
- 解析agent定义中的工具要求
- 初始化专属MCP服务器
- 注册frontmatter钩子
-
进入查询循环:
- 使用隔离参数调用主query()
- 记录sidechain transcript
- 处理清理操作
typescript复制export async function* runAgent({
agentDefinition, promptMessages, toolUseContext
}) {
// 上下文隔离
const agentContext = {
readFileState: cloneFileStateCache(toolUseContext.readFileState),
abortController: agentDefinition.isAsync
? new AbortController()
: toolUseContext.abortController
}
// 工具初始化
const {clients, tools, cleanup} = await initializeAgentMcpServers(...)
try {
// 进入主循环
for await (const msg of query({
messages: prepareMessages(),
systemPrompt: buildAgentPrompt(),
toolUseContext: agentContext,
// ...其他参数
})) {
await recordSidechainTranscript(msg)
yield msg
}
} finally {
await cleanup() // 资源释放
}
}
9.2 同步 vs 异步代理对比
| 特性 | 同步代理 | 异步代理 |
|---|---|---|
| 中断控制 | 共享父级AbortController | 独立实例 |
| 状态管理 | 共享setAppState | 隔离操作(rootSetAppState) |
| 权限提示 | 允许显示 | 强制避免(shouldAvoidPermissionPrompts) |
| 交互特性 | 继承父级 | 强制非交互(isNonInteractiveSession) |
| 典型场景 | Explore/Plan | 后台任务/workflow |
10. 工程实践启示
通过对Claude Code架构的剖析,我们可以提炼出以下值得借鉴的工程实践:
- 流式优先设计:全链路AsyncGenerator实现带来显著的响应速度提升
- 精细并发控制:基于工具特性的自动分区确保安全并行
- 渐进式压缩策略:四级压缩平衡性能与成本
- 弹性错误恢复:分级恢复机制最大化任务完成率
- 隔离子代理:通过上下文隔离实现安全的任务委派
这些设计理念对于构建复杂AI Agent系统具有普遍参考价值,开发者可以根据实际需求适当借鉴其中的架构模式和解决方案。