乐于分享
好东西不私藏

Claude Code 源码架构拆解

Claude Code 源码架构拆解

2026 年 3 月 31 日,Claude Code v2.1.88 的 npm 发布包中因未剥离 source map 文件,导致 1906 个源文件、51 万行 TypeScript 代码完整暴露。这不是入侵,不是内部泄密,是构建配置错误——sourceMap: false 未设置、.npmignore 未排除 .map 文件。Anthropic 数小时内发版修复,但社区已广泛备份。

从安全分析师的视角读完这套源码,我认为它值得拆解的原因只有一个:这是目前公开的、工程化程度最高的工业级 Agent 系统之一。不管是做安全检测、SOC 分析还是 AI 工程落地,这套代码里都有可以直接借鉴的模式。


一、全局架构:单循环 + Harness,不是多 Agent 对等协作

Claude Code 的源码里有 Coordinator、Worker、Fork 这些概念,乍看像多 Agent Swarm。但读完代码会发现,它是分层调度的——一个主循环在顶层,需要时往下派生子 Agent,子 Agent 干完活把结果交回来。上下级关系,不是对等协作。

1.1 核心公式

Claude Code = one agent loop  + ~40 tools (bash, read, write, edit, glob, grep, browser...)  + on-demand skill loading  + context compression  + subagent spawning  + team coordination with async mailboxes  + worktree isolation for parallel execution  + permission governance

源码按功能划分为 30+ 个顶层目录,每个目录对应一个 Harness 子系统:

目录 角色 说明
src/tools/
Agent 的双手
~40 个工具实现,文件 I/O、Shell 执行、代码搜索
src/commands/
用户指令接口
~50 个斜杠命令
src/services/
外部服务集成
API 客户端、MCP 协议、OAuth 认证、LSP
src/coordinator/
多智能体协调
子智能体的编排、调度和协作
src/skills/
按需知识加载
可复用工作流,按需注入领域知识
src/bridge/
IDE 桥接层
连接 VS Code、JetBrains 与 CLI
src/hooks/
权限与生命周期
工具权限校验、会话管理
src/plugins/
插件扩展
第三方插件加载
src/components/
终端 UI
~140 个 React + Ink 组件
src/memdir/
持久记忆
跨会话记忆存储
src/tasks/
任务系统
带依赖图的任务管理
src/state/
状态管理
全局应用状态

三个关键文件撑起了整个系统:

  • QueryEngine.ts(~46K 行)—— LLM 查询引擎:流式响应、工具调用循环、思考模式、重试、Token 计数
  • Tool.ts(~29K 行)—— 工具基础类型定义(Zod v4)
  • commands.ts(~25K 行)—— 斜杠命令注册与执行

1.2 一个关键洞察

所有模式——单 Agent、Coordinator、Worker——共用同一个引擎。不管是普通对话还是多 Agent 编排,底层跑的都是同一个 QueryEngine 的 Think → Act → Observe → Repeat 循环。Coordinator 和普通 Agent 的区别,仅仅是换了一套 System Prompt。

二、Agent Loop:核心循环的工程实现

QueryEngine.ts 是 Claude Code 的大脑,循环结构很直白。

下面是从 query.ts(1730 行)中精简出来的核心循环骨架:

// query.ts — queryLoop 核心结构(精简版)async function* queryLoop(params: QueryParams){let state = { messages: params.messages, turnCount: 1 }while (true) {let messagesForQuery = getMessagesAfterCompactBoundary(state.messages)    // 1. 上下文压缩(接近 Token 上限时自动触发)    const { compactionResult } = await autocompact(messagesForQuery, ...)if (compactionResult) messagesForQuery = buildPostCompactMessages(compactionResult)    // 2. 调用 Claude API,流式接收响应for await (const msg of callModel({ messages: messagesForQuery, tools, systemPrompt })) {if (msg.type === 'assistant') assistantMessages.push(msg)    }    // 3. 没有工具调用 → 结束循环,返回结果if (toolUseBlocks.length === 0) return { reason: 'end_turn' }    // 4. 执行工具 → 收集结果 → 追加到对话历史 → 下一轮    const toolResults = await runTools(toolUseBlocks, canUseTool, toolUseContext)    state = { messages: [...messagesForQuery, ...assistantMessages, ...toolResults], turnCount: state.turnCount + 1 }  }}

2.1 几个值得注意的工程细节

工具调度是全手搓的。没有用 LangChain、LangGraph 或任何第三方 Agent 框架。约 40 个 Tool 的注册、描述、参数校验、调用分发,全在 QueryEngine 里自己管。对于 Anthropic 这种体量的团队,可控性比开发效率更重要。

工具调用是同步递归的。API 返回工具调用请求 → 执行工具 → 结果追加到对话历史 → 再调 API。串行,没有异步消息队列。简单、可控、容易调试。

每轮循环有显式的 Token 追踪。一旦接近上下文窗口上限,自动触发 Context Compaction。这个机制保证了长时间运行的任务不会因为上下文溢出而崩溃。

三、动态 Prompt 组装:六层拼装

Claude Code 的系统提示词不是一个写死的长字符串。context.ts 在每次调用 API 之前,从六个不同来源拼出完整上下文:

层次 来源 内容 更新频率
System
硬编码
基础角色定义、安全规则
每版本更新
Mode
coordinatorMode / workerMode
Coordinator/Worker/Normal 行为规范
每次模式切换
Tools
40+ 工具注册 + MCP 动态加载
工具描述和使用规范
MCP 连接时
Project
CLAUDE.md
项目级持久记忆
用户/Agent 主动更新
Git
context.ts 实时读取
仓库状态快照(分支、改动文件)
每轮对话
History
对话历史 + Compaction 摘要
会话上下文
每轮对话

这种分层的好处是每一层可以独立更新,互不干扰。MCP 接入新工具只更新 Tools 层;Agent 切换到 Coordinator 模式只替换 Mode 层。这解释了为什么”Coordinator 本质上就是换了个 Prompt 的普通 Agent”——模式切换只动了六层里的一层。

四、Coordinator-Worker:需要时才启用的多 Agent 编排

4.1 Coordinator 的 370 行系统提示词

coordinatorMode.ts 里的 getCoordinatorSystemPrompt() 返回了一份超过 370 行的 Prompt,本身就是一份多 Agent 编排的工程规范文档。

核心是四阶段工作流:

阶段 执行者 职责
Research(调研)
Worker(可并行)
调查代码库、定位文件、理解问题
Synthesis(综合)
Coordinator 自己
阅读调研结果,理解问题,撰写实施方案
Implementation(实施)
Worker
按方案做针对性修改
Verification(验证)
Worker
验证改动是否正确

Synthesis 阶段是关键。Prompt 用了非常强硬的语气(第 255-259 行):

Always synthesize — your most important job. When workers report research findings, you must understand them before directing follow-up work. Read the findings. Identify the approach. Then write a prompt that proves you understood by including specific file paths, line numbers, and exactly what to change.

紧接着给了一个反面教材(第 262-268 行):

// 反面教材 —— 偷懒式委派AgentTool({ prompt: "Based on your findings, fix the auth bug", ... })// 正面示例 —— 综合后的精确指令AgentTool({ prompt: "Fix the null pointer in src/auth/validate.ts:42.The user field on Session is undefined when sessions expire but thetoken remains cached. Add a null check before user.id access —if null, return 401 with 'Session expired'. Commit and report the hash.", ... })

“Based on your findings, fix the bug” 这种写法把理解工作推给了 Worker,Coordinator 自己没做任何综合判断。正确的做法是自己消化理解,给 Worker 一个包含文件路径、行号和修改方案的精确指令。

4.2 AgentTool:Fork vs Fresh Agent

AgentTool/prompt.ts 定义了子 Agent 的两种模式:

场景 推荐方式 原因
调研结果覆盖了要修改的文件
Continue(同一 Worker)
已有相关文件上下文
调研范围广但实施范围窄
Spawn fresh(新建 Worker)
避免把调研噪声带入实施
纠正失败或扩展最近的工作
Continue
Worker 有错误上下文
验证另一个 Worker 刚写的代码
Spawn fresh
验证者需独立视角

Prompt 里还定义了两条规范:

  • Don’t peek(不要偷看)—— Fork 返回结果后,Coordinator 不要去读 Fork 的中间过程文件。”Reading the transcript mid-flight pulls the fork’s tool noise into your context, which defeats the point of forking.”
  • Don’t race(不要抢跑)—— Fork 还在运行时,Coordinator 不能预测或编造结果。”Never fabricate or predict fork results in any format.”

五、三层记忆系统:短期压缩、中期萃取、长期整合

这是我认为源码中技术含金量最高的部分。

5.1 Context Compaction:9 段式结构化摘要

services/compact/prompt.ts(375 行)定义了上下文压缩逻辑。不是简单截断历史,而是用专门的 LLM 调用把对话压缩成结构化摘要,严格 9 段格式:

1. Primary Request and Intent(主要需求和意图)2. Key Technical Concepts(关键技术概念)3. Files and Code Sections(涉及的文件和代码片段,含实际代码)4. Errors and fixes(遇到的错误和修复方案)5. Problem Solving(问题解决过程)6. All user messages(所有用户消息,逐条保留,不能遗漏)7. Pending Tasks(待办任务)8. Current Work(当前工作状态)9. Optional Next Step(可选的下一步,含原始对话直接引用,防止任务漂移)

两个工程细节:

<analysis> scratchpad 机制 — 摘要生成时,模型先在 <analysis> 标签里写草稿梳理思路,生成后 formatCompactSummary() 自动剥离。用额外 Token 换取更高质量摘要,但不长期占用上下文窗口。

Partial Compaction — 不是全部压缩,只压缩靠前的旧内容,保留最近几轮原始对话(recentMessagesPreserved 控制)。近的保持原貌,远的变成高密度摘要。

5.2 ExtractMemories:后台自动记忆萃取

services/extractMemories/prompts.ts 定义了一个后台子 Agent,作为主对话的 Fork 独立运行,自动从对话中提取值得长期保存的信息。

前置判断:如果主 Agent 已经主动写过记忆文件(通过 hasMemoryWritesSince 检测),ExtractMemories 跳过,不重复提取。

执行约束:turn budget——要求先并行 Read 所有可能需要更新的记忆文件,再并行 Write,不允许交替读写。保证记忆更新的原子性。

记忆区分 private memory(个人记忆)和 team memory(团队记忆),写入不同路径。

5.3 AutoDream:记忆整合

services/autoDream/consolidationPrompt.ts 定义了 Dream 功能——Agent 空闲时自动整理已有记忆文件。四阶段执行:

阶段 动作
Orient(定位)
ls

 记忆目录,读 MEMORY.md 索引,搞清楚当前有哪些记忆
Gather recent signal
从最近的 session transcript 中 grep 新信息
Consolidate(整合)
新信号与旧记忆合并,删除矛盾旧记忆,相对日期转绝对日期
Prune and index
确保 MEMORY.md 索引不超过 200 行或 25KB

这借鉴了认知科学中睡眠与记忆整合的研究——人在睡眠期间大脑对短期记忆做整理,转化为长期记忆。Claude Code 在 Agent 不忙时做了同样的事。

5.4 三层协同总览

记忆层 机制 生命周期 触发条件 数据载体
短期
Context Compaction
单次会话
接近 Token 上限自动触发
对话内摘要
中期
ExtractMemories
跨会话
每轮对话结束后后台运行
CLAUDE.md 文件
长期
AutoDream
跨项目
Agent 空闲时异步运行
MEMORY.md + 索引

对比市面上的 Agent 框架(LangGraph Checkpointer、CrewAI Memory),Claude Code 在自动化和结构化程度上都高出一个量级。大多数框架的记忆还停留在”把对话历史存起来”,Claude Code 做到了自动提取、自动整合、自动修剪的完整闭环。

六、权限系统:6 级纵深防御

Claude Code 的安全体系拒绝”一层顶全部”,构建了多层嵌套、层层兜底的防御:

┌─────────────────────────────────────────────┐│ 用户交互层                                    ││ ┌─────────────────────────────────────────┐ ││ │ 权限决策层                               │  ││ │ ┌─────────────────────────────────────┐│  ││ │ │ 命令分析层                            │ │ ││ │ │ ┌─────────────────────────────────┐ │ │ ││ │ │ │ 密钥保护层                        │ │ │ ││ │ │ │ ┌─────────────────────────────┐ │ │ │ ││ │ │ │ │ 代码审计层                    │ │ │ │ ││ │ │ │ │ ┌─────────────────────────┐ │ │ │ │ ││ │ │ │ │ |  配置安全层              │ │ │ │ │ ││ │ │ │ │ └─────────────────────────┘ │ │ │ │ ││ │ │ │ └─────────────────────────────┘ │ │ │ ││ │ │ └─────────────────────────────────┘ │ │ ││ │ └─────────────────────────────────────┘ │ ││ └─────────────────────────────────────────┘ │└─────────────────────────────────────────────┘

6.1 子系统 1:YOLO 安全分类器

AI 驱动的权限守门人,两阶段决策:

用户请求 → Stage 1 快速分类 → 明确允许? → ✅ 放行              ↓ 不确定         Stage 2 深度思考 → 允许? → ✅ 放行              ↓ 仍不确定         ❌ 默认阻止 (Fail-Closed)

设计原则:最小权限、默认拒绝、上下文感知。分类器崩溃/超时自动阻止,绝不放行。

权限支持细粒度规则配置:

  • 工具类型:不同工具有不同的默认权限级别
  • 操作类型:读取比写入更容易获得批准
  • 文件路径模式:限制 Agent 只在特定目录下操作
  • 命令模式:BashTool 可配置允许的命令白名单

6.2 子系统 2:密钥扫描器

基于 30+ Gitleaks 正则规则,覆盖全场景密钥检测:

  • 云服务:AWS / GCP / Azure / DO 全系列凭证
  • AI 平台:Anthropic / OpenAI / HuggingFace Token
  • 版本控制:GitHub / GitLab 密钥
  • SaaS:Slack / Twilio / npm / PyPI / Databricks
  • 支付:Stripe / Shopify
  • 通用:RSA / EC / PGP 私钥、通用 API Key 模式

核心底线:密钥永不离开用户机器。发现密钥 → [REDACTED] 替换 → 脱敏后才上传。

6.3 子系统 3:Shell 命令安全验证

不是黑名单匹配,而是语义级 AST 分析。支持 PowerShell 完整解析:词法分析 → 语法树构建 → 语义分析 → 安全裁决。能识别别名、管道隐式删除、变量拼接构造的危险命令。

6.4 多级权限模式

模式 行为 场景
default
每次工具调用前提示用户审批
日常开发
plan
规划阶段只读,执行阶段需审批
大型重构
auto
自动批准低风险操作
信任环境
bypassPermissions
跳过所有检查
沙箱/测试

6.5 Worktree 隔离

EnterWorktreeTool / ExitWorktreeTool 实现 Git Worktree 文件系统隔离:

main branch: /project/ (shared)├── worktree-task-1/ (Agent A 的 sandbox)├── worktree-task-2/ (Agent B 的 sandbox)└── worktree-task-3/ (Agent C 的 sandbox)

每个子智能体在独立 sandbox 工作,不影响主分支。

七、可观测性:三层观测 + Agent 语义级 Span

Claude Code 把可观测性拆成了三个独立层次:

层 模块 回答什么问题
产品分析事件
services/analytics/
发生了什么产品事件
标准化 Telemetry
utils/telemetry/instrumentation.ts
指标和 traces 怎么接入后端
会话级 Tracing
utils/telemetry/sessionTracing.ts
一个 Agent turn 在 runtime 怎么展开的

关键设计是把 Agent 业务语义直接编码成 span 类型:

interaction(用户交互回合,root span)  ├── llm_request(模型调用)  ├── tool(工具被选中)  │     ├── tool.blocked_on_user(等待用户批准)  │     └── tool.execution(实际执行)  └── hook(钩子执行)

tool.blocked_on_user 单独追踪等待批准的时间。这意味着 trace 能回答:慢是因为模型慢,还是审批慢?某类工具成功率低,是执行问题还是被拒绝太多?

实现上用 AsyncLocalStorage 做上下文传播,WeakRef + 30 分钟 TTL cleanup 处理悬挂 span。注释写得很直接:这是 safety net for spans that were never ended——aborted streams、uncaught exceptions、mid-query 中断在 Agent runtime 里是常态,不是边缘情况。

事件出口还有隐私设计:sanitizeToolNameForAnalytics() 默认把 mcp__<server>__<tool> 收敛成 mcp_tool,防止自定义 MCP server 名包含个人信息泄露。_PROTO_* 字段在 Datadog fanout 前统一 stripProtoFields(),一个过滤点管住所有 sink。

八、极限工程细节:几个很实用的设计

8.1 Agent 列表从内联改 Attachment:省了 10.2% Cache Token

原来的 Agent 列表写在工具描述里。MCP 连接、插件加载、权限变更时列表变化 → 工具描述变化 → 整个 tools-block 的 Prompt Cache 失效。

源码注释记录了精确数据:

“The dynamic agent list was ~10.2% of fleet cache_creation tokens”

解决方案:Agent 列表从工具描述移到 agent_listing_delta attachment 消息注入。工具描述变静态,Cache 不再因列表变化失效。

通用启示:凡是用 Prompt Cache 的场景,把频繁变化的内容从 Cache 边界内移到边界外。

8.2 Cron 调度的 Jitter 设计

用户说”每天早上 9 点提醒”,不设成 0 9 * * *,而是偏移几分钟如 57 8 * * * 或 3 9 * * *。

Prompt 原文:

“Pick a minute that is NOT 0 or 30… the user will not notice, and the fleet will.”

全球用户说”9 点”都映射到 :00,不偏移就会整点集中爆发。用户无感,系统受益。

8.3 NO_TOOLS_PREAMBLE:防止模型在不该调工具时调工具

Context Compaction 通过 Fork 主对话执行,Fork 继承完整工具集(为命中 Prompt Cache)。但摘要任务不需要调用工具。Sonnet 4.6+ 有时自作主张调用工具,失败率精确到 **2.79%**(4.6 vs 4.5 的 0.01%)。

解决方案——在 Prompt 最前面加强硬约束:

CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.- Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool.- Tool calls will be REJECTED and will waste your only turn — you will fail the task.

约束放在 Prompt 开头而不是末尾,因为模型对 Prompt 开头的注意力最集中。

8.4 Feature Flags 的编译时消除

通过 bun:bundle 的 feature() 函数控制功能开关。不是运行时判断——是编译时 Dead Code Elimination。关闭的 flag 相关代码在构建产物里根本不存在。不会占内存,没有运行时分支开销。

泄露的 flags 暴露了未发布功能方向:PROACTIVE(主动模式)、KAIROS(定时任务系统)、DAEMON(守护进程模式)、VOICE_MODE(语音交互)、MONITOR_TOOL(监控工具)。

九、技术栈总览

类别 技术选型 意义
运行时
Bun
高性能,原生 bundle 特性标志支持
语言
TypeScript (strict)
类型安全,大型代码库可维护性
终端 UI
React + Ink
组件模型构建终端界面,140+ 组件
CLI 解析
Commander.js
成熟的命令行解析
Schema
Zod v4
工具输入输出的运行时类型安全
代码搜索
ripgrep
极快的正则搜索
外部协议
MCP SDK, LSP
标准化工具集成
遥测
OpenTelemetry + gRPC
可观测性基础设施
特性标志
GrowthBook
A/B 测试与渐进式发布
认证
OAuth 2.0, JWT, Keychain
多层次安全认证

十、隐藏功能:Feature Flag 背后的未发布内容

源码中发现的几个有意思的未发布功能:

Buddy — 电子宠物系统(Tamagotchi 风格 ASCII 宠物),18 种物种,6 种稀有度,闪光款设定。时间戳显示 4 月 1 日首次亮相 → 愚人节彩蛋。

Kairos — 持久化助手模式,跨会话长期记忆。你不使用时自动执行四阶段记忆整合(定向→收集→整合→修剪)。

Ultraplan — 使用 Opus 4.6 支持最长 30 分钟深度任务规划。Plan → Approve → Execute 三阶段模型。规划用强模型(高成本、低频),执行用快模型(低成本、高频)。

Undercover Mode — 向开源 PR 提交时移除 Anthropic 信息,让 AI 伪装成人类。引发社区争议。

共 35 个编译时特性标志、120+ 隐藏环境变量、200+ 远程控制开关。

十一、安全影响:源码公开后的已知风险

源码公开加速了安全研究,已发现并修复的问题包括:

  • CVE-2025-54795 — 代码执行漏洞
  • 符号链接绕过、沙箱逃逸、配置文件注入
  • 恶意仓库在用户确认信任前窃取 API 密钥
  • 管道 sed 操作绕过文件写入限制
  • .claude/settings.json 不存在时沙箱未正确保护,允许恶意代码注入持久化钩子

对于做安全检测的人来说,这套源码的价值在于:你可以精确理解一个工业级 Agent 的攻击面在哪里。权限模型的每个 hook 点、密钥扫描的每条正则、Shell 命令 AST 分析的每个边界条件——这些就是检测规则和防护策略的蓝本。

十二、总结

Claude Code 的源码给我们的最核心启示只有一句话:

“The model is the agent. The code is the harness.”

模型决定做什么,Harness 负责怎么做。Claude Code 不试图通过规则引擎或决策树来模拟智能——它完全信任模型的决策能力,把全部工程精力投入到为模型提供一个清晰、丰富、安全的工作环境。

从工具系统(40+ 原子工具、分层行动-感知体系)到知识管理(按需加载、渐进式披露),从上下文工程(三层压缩策略)到多智能体协调(六种架构模式、异步邮箱通信),从权限治理(6 级纵深防御)到性能优化(并行预取、懒加载、编译时死代码消除)——每一个子系统都展现了成熟的工程决策。

最好的 Agent 产品来自于那些理解”自己的工作是 Harness,而非智能”的工程师。当工程精力从”试图编程智能”转向”为智能构建世界”时,Agent 系统的能力上限将由模型本身的智能水平决定,而非被差劲的 Harness 设计所限制。


源码参考:https://github.com/instructkr/claude-code