Claude Code 源码深度解读(一):总览与架构哲学
系列导读:本系列共7篇,源自对 Claude Code 51万行 TypeScript 源码的逆向解读。目标不仅是「看懂 Claude Code」,更是为构建你自己的 Agentic 系统提供可迁移的设计模式和工程经验。
一、Claude Code 是什么?
Claude Code 是 Anthropic 官方的 命令行 AI 编码助手,于 2025 年初推出。它不是一个 IDE 插件,而是一个直接运行在终端中的、完全自主的 AI Agent。
用一句话概括它的定位:
一个能在你的代码库里「自由行动」的 AI 工程师,它能读代码、写代码、运行命令、搜索文件、管理 Git,甚至能生成子代理来并行工作。
1.1 项目基本指标
|
|
|
|---|---|
|
|
|
|
|
Bun
|
|
|
~512,000+ 行 |
|
|
~1,900 个 |
|
|
|
|
|
|
|
|
|
让你惊讶的第一个事实:终端里运行的程序居然用 React 写 UI。这不是噱头——Ink 是一个将 React 组件渲染为终端输出的框架,意味着 Claude Code 的「界面层」和 Web App 共享同一套组件化思想。
二、一张图看懂整体架构
┌─────────────────────────────────────────────────────────────┐│ CLI 入口层 (main.tsx) ││ Commander.js 解析命令行 → React/Ink 渲染终端 UI ││ 启动时并行预取:MDM设置 + Keychain + API预连接 │├─────────────────────────────────────────────────────────────┤│ 查询引擎层 (QueryEngine.ts) ││ 会话管理 │ 消息生命周期 │ 系统Prompt组装 │ 使用量跟踪 │├─────────────────────────────────────────────────────────────┤│ 核心查询循环 (query.ts) ││ while(true) 主循环 │ 五层上下文压缩 │ 五重错误恢复 ││ 流式API调用 │ StreamingToolExecutor 并行工具执行 │├───────────────────────┬─────────────────────────────────────┤│ 工具系统 (tools/) │ 命令系统 (commands/) ││ ~40 个内置工具 │ ~80+ 个斜杠命令 ││ MCP 外部工具 │ 技能/插件/工作流命令 ││ Feature Flag 条件加载 │ 可用性门控 │├───────────────────────┴─────────────────────────────────────┤│ 服务层 (services/) ││ API │ MCP │ OAuth │ Compact(压缩) │ Analytics │ Session │├─────────────────────────────────────────────────────────────┤│ 基础设施层 ││ State(状态) │ Bridge(IDE桥接) │ Hooks │ Permissions ││ Memory(记忆) │ Skills(技能) │ Coordinator(协调器) │└─────────────────────────────────────────────────────────────┘
这个六层架构有一个核心设计原则:每一层只依赖下面的层,不向上依赖。这意味着你可以独立替换任何一层——比如把终端 UI 换成 Web UI(Bridge 模式做的正是这件事)。
三、架构哲学:七个核心设计决策
3.1 哲学一:「循环」而非「链」
很多 AI Agent 框架(如 LangChain)喜欢用链式思维:输入 → 处理1 → 处理2 → 输出。
Claude Code 的核心选择不同——它是一个 while(true) 无限循环:
while (true) { 压缩上下文 → 调用模型 → 执行工具 → 收集结果 → 注入附件 ↓ ↓ 如果模型说"我还没做完" ─────────────────────→ 继续循环 ↓ 如果模型说"我做完了" ──→ 检查Stop Hooks ──→ 退出}
为什么是循环而不是链?
因为真实的编码任务是不可预测的。模型可能需要调用 1 个工具就完成,也可能需要调用 50 个。链式架构需要预定义步骤数量,而循环架构是自适应的。
💡 给你的启发:如果你的 Agent 系统面对的任务复杂度变化很大,优先选择循环架构而非链式架构。循环给模型最大的「自主权」来决定什么时候停下来。
3.2 哲学二:Feature Flag 驱动的渐进式发布
Claude Code 大量使用 bun:bundle 的 feature() 函数实现编译时特性开关:
// 只有启用了 PROACTIVE 特性,SleepTool 才会被编译进最终产物const SleepTool = feature('PROACTIVE') || feature('KAIROS') ? require('./tools/SleepTool/SleepTool.js').SleepTool : null// 内部员工专用的 REPL 工具const REPLTool = process.env.USER_TYPE === 'ant' ? require('./tools/REPLTool/REPLTool.js').REPLTool : null
这不是普通的运行时 if/else,而是编译时死代码消除。当 feature('PROACTIVE') 为 false 时,整个 SleepTool 的代码不会出现在最终构建中。
已知的重要 Feature Flags:
|
|
|
|
|---|---|---|
PROACTIVE |
|
|
KAIROS |
|
|
COORDINATOR_MODE |
|
|
CONTEXT_COLLAPSE |
|
|
HISTORY_SNIP |
|
|
REACTIVE_COMPACT |
|
|
CHICAGO_MCP |
|
|
WEB_BROWSER_TOOL |
|
|
VOICE_MODE |
|
|
TOKEN_BUDGET |
|
|
BUDDY |
|
|
💡 给你的启发:即使你的系统不需要编译时消除,也应该为每个新能力设计 Feature Flag。这允许你「安全地向前冲」——代码先合入主干,flag 控制谁能看到。
3.3 哲学三:流式一切 (Stream Everything)
Claude Code 的核心数据流全部使用 AsyncGenerator(异步生成器):
// QueryEngine.submitMessage 返回的是 AsyncGeneratorasync *submitMessage(prompt): AsyncGenerator<SDKMessage, void, unknown> { // ... for await (const message of query(...)) { yield message // 逐条吐出,不是全部算完再返回 }}// query() 本身也是 AsyncGeneratorasync function* query(params): AsyncGenerator<Message> { while (true) { for await (const message of callModel(...)) { yield message // 模型返回一个 token 就吐一个 } // ... }}
这种设计意味着:
-
1. 用户立即看到输出——不用等整个工具执行完 -
2. 可以在任意点中断——Ctrl+C 能即时停止 -
3. 内存友好——不需要把所有消息存在内存里
💡 给你的启发:在设计 Agentic 系统时,用 AsyncGenerator(Python 里是
async def ... yield)作为核心数据管道。它天然支持流式、取消和背压。
3.4 哲学四:工具是一等公民
在 Claude Code 里,工具不是模型的附属品,工具是架构的核心。
整个系统围绕「模型决定调用什么工具 → 系统执行工具 → 结果反馈给模型」的循环运转。工具有自己的:
-
• 类型系统(Zod Schema 验证输入输出) -
• 权限模型(每个工具独立的权限检查) -
• 并发声明(声明自己是否线程安全) -
• 渲染系统(每个工具自定义 UI 展示)
┌──────────────────────────────────────────┐│ Tool 接口 │├──────────────────────────────────────────┤│ name │ 工具名称 ││ inputSchema │ Zod 输入验证 ││ call() │ 执行逻辑 ││ checkPerms() │ 权限检查 ││ isReadOnly() │ 是否只读 ││ isEnabled() │ 当前是否可用 ││ isConcSafe() │ 是否并发安全 ││ render*() │ 各种渲染钩子 │└──────────────────────────────────────────┘
💡 给你的启发:不要把工具当作「函数列表」。给每个工具设计完整的元数据(权限、并发性、只读性),系统才能智能地调度和管控它们。
3.5 哲学五:五层韧性(永远不崩)
Claude Code 对错误处理的执念到了偏执的程度。在核心查询循环中,实现了五重恢复机制:
API 调用 │ ┌─────▼─────┐ ┌─ NO─┤ 调用成功? ├─ YES ──→ 继续 │ └───────────┘ ▼ ┌─────────────────┐ │ FallbackTriggered│──→ 自动切换到备用模型 │ Error │ 清理残留消息,重试 └────────┬────────┘ │ 还是失败? ▼ ┌─────────────────┐ │ Context Collapse │──→ 释放已折叠的上下文 │ Drain Retry │ 减小 prompt 体积,重试 └────────┬────────┘ │ 还是 413? ▼ ┌─────────────────┐ │ Reactive Compact │──→ 紧急全量摘要压缩 │ Retry │ 用压缩后的对话重试 └────────┬────────┘ │ 输出截断? ▼ ┌─────────────────┐ │ Max Output Tokens│──→ 注入「继续」指令 │ Recovery (×3) │ 最多重试3次 └────────┬────────┘ │ 图片太大? ▼ ┌─────────────────┐ │ Media Size │──→ 裁剪超大图片 │ Recovery │ 重试 └─────────────────┘
最妙的是:这些恢复机制对用户是透明的。用户看到的只是「AI 正在思考」,背后可能已经经历了模型回退、上下文压缩、重试等一系列操作。
💡 给你的启发:Agentic 系统必须像潜水艇一样设计多层水密舱。每一层失败都有下一层兜底。用户不应该感知到任何内部故障恢复。
3.6 哲学六:可组合的「大系统」
Claude Code 不是一个单一的 Agent,它是一个可组合的 Agent 运行时:
┌─────────────────────────────────────────┐│ 组合方式 │├────────────┬────────────────────────────┤│ CLI 模式 │ 终端直接使用 ││ SDK 模式 │ 被其他程序调用 ││ Bridge 模式 │ 被 IDE 插件调用 ││ 协调器模式 │ 管理多个子代理 ││ 远程模式 │ 运行在云端服务器 ││ 无头模式 │ 无 UI,纯 API │└────────────┴────────────────────────────┘
同一份代码,通过不同的入口和配置,变成完全不同的产品形态。
3.7 哲学七:极致的启动性能
Claude Code 的启动流程令人印象深刻——在所有 import 语句执行之前就开始了并行 I/O:
// main.tsx 的前几行(在 import 之前!)import { profileCheckpoint } from './utils/startupProfiler.js';profileCheckpoint('main_tsx_entry'); // 标记启动时间import { startMdmRawRead } from './utils/settings/mdm/rawRead.js';startMdmRawRead(); // 立即启动 MDM 设置读取import { startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js';startKeychainPrefetch(); // 立即启动 Keychain 预取// 此时模块加载还要 ~135ms,MDM 和 Keychain 读取在这 135ms 里并行完成
时间线:
0ms 50ms 100ms 135ms 200ms│ │ │ │ │├─ import ────────────────────────────────┤├─ MDM read ──────────┤ │├─ Keychain ──────────────┤ ││ ├─ 真正开始 ─→
在模块加载的「死时间」里做并行 I/O,这种优化思路值得学习。
💡 给你的启发:用户感知的「启动速度」= 串行路径上最长的那条链。把所有无依赖的 I/O 操作提前到最早可能的时刻并行执行。
四、核心数据流:一次完整的对话
让我们追踪一次用户输入 "帮我重构 utils.ts" 的完整旅程:
用户输入 "帮我重构 utils.ts" │ ▼┌─ 1. QueryEngine.submitMessage() ─────────────────────┐│ · 组装系统 Prompt(CLAUDE.md + 项目上下文 + Git状态) ││ · 加载记忆(MEMORY.md + 嵌套记忆目录) ││ · 创建 ToolUseContext(工具上下文) │└───────────────────────┬───────────────────────────────┘ ▼┌─ 2. query() 主循环 ──────────────────────────────────┐│ ││ ┌─ 2a. 预处理 ─────────────────────────────────┐ ││ │ · Skill Discovery Prefetch(异步预取技能发现) │ ││ │ · Tool Result Budget(工具结果预算裁剪) │ ││ │ · Snip + Microcompact(历史和工具结果压缩) │ ││ │ · Context Collapse(上下文折叠) │ ││ │ · Autocompact(Token 超限全量压缩) │ ││ └───────────────────────────────────────────────┘ ││ ▼ ││ ┌─ 2b. 模型调用 ───────────────────────────────┐ ││ │ callModel() 流式调用 Claude API │ ││ │ StreamingToolExecutor 边收边执行工具 │ ││ │ → 模型返回: "我需要先读取 utils.ts" │ ││ │ → tool_use: ReadFileTool("utils.ts") │ ││ └───────────────────────────────────────────────┘ ││ ▼ ││ ┌─ 2c. 工具执行 ───────────────────────────────┐ ││ │ ReadFileTool.call("utils.ts") │ ││ │ → 返回文件内容 │ ││ └───────────────────────────────────────────────┘ ││ ▼ ││ ┌─ 2d. 附件注入 ───────────────────────────────┐ ││ │ · 文件变更通知 │ ││ │ · 记忆预取结果 │ ││ │ · 技能发现结果 │ ││ │ · 队列命令/通知 │ ││ └───────────────────────────────────────────────┘ ││ ▼ ││ needsFollowUp = true → 回到 2a 继续循环 ││ ...(可能循环 5-20 次,读文件、写文件、运行测试等) ││ ... ││ needsFollowUp = false → Stop Hooks 检查 → 退出循环 │└───────────────────────────────────────────────────────┘ ▼┌─ 3. 结果返回 ────────────────────────────────────────┐│ · 记录使用量和成本 ││ · 持久化会话记录 ││ · 更新 Git Attribution ││ · 等待用户下一次输入 │└───────────────────────────────────────────────────────┘
五、22 个可直接应用的设计模式
架构模式
-
1. 六层蛋糕架构 — 每层只依赖下层,可独立替换 -
2. AsyncGenerator 核心管道 — 流式、可取消、背压 -
3. 循环 vs 链 — 自适应任务复杂度 -
4. Feature Flag 驱动发布 — 安全地向前冲
错误处理模式
-
5. 五层韧性 — 多层恢复机制对用户透明 -
6. Tombstone 消息 — 回退时清理已发出的”脏消息”
多代理模式
-
7. 渐进式代理复杂度 — 6 个级别可混合使用 -
8. Fork + Prompt Cache 共享 — 统一占位符确保字节一致 -
9. 自动后台化 — 120秒超时自动转后台 -
10. Scratchpad 跨代理共享 — 文件系统即消息队列 -
11. Schema 动态裁剪 — 编译时防止模型误用
记忆模式
-
12. 四类记忆分类法 — 约束驱动的有效记忆 -
13. 索引+明细分层 — MEMORY.md 是索引,详细内容在子目录 -
14. 双维度限制 — 200行 AND 25KB 独立检查 -
15. 推测执行 — 在用户输入时就开始工作
状态管理
-
16. DeepImmutable + Mutable 分层 — 渲染性能 + 灵活性 -
17. Promise 记忆化 — 并发安全的一次性操作
工具系统
-
18. 工具元数据声明 — isReadOnly / isDestructive / isConcurrencySafe -
19. 阻塞限制前置检查 — 在调用 API 之前先检查 token 预算
性能优化
-
20. 启动时并行 I/O — 在模块加载的”死时间”里做并行操作 -
21. 流式一切 — AsyncGenerator 作为核心数据管道 -
22. 死代码消除 — 编译时 feature() 条件加载
六、结语
Claude Code 的出现标志着 AI 编码助手进入了一个新的阶段——从「问答式」向「自主式」转变。它的架构设计告诉我们:构建一个可靠的 Agentic 系统,需要的不仅是 LLM API,还需要:
-
• 一个健壮的核心循环 -
• 精细的上下文管理 -
• 灵活的工具框架 -
• 纵深防御的安全模型 -
• 持久化的记忆系统 -
• 以及无处不在的工程纪律
这个系列将逐层深入 Claude Code 的每一个子系统,希望能为你构建自己的 Agentic 系统提供启发。
📚 系列文章目录
本系列共7篇,系统性地拆解 Claude Code 的架构设计:
|
|
|
|
|
|---|---|---|---|
| 01 | 总览与架构哲学 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
参考资料:
-
• 项目地址:https://github.com/aga-j/claude-code-deep-dive -
• 作者:Wyman Wu (AI Product Manager) -
• 源码基础:Claude Code ~512K 行 TypeScript 源码
本系列基于开源项目源码解读,仅供学习研究使用。
引用链接
[1] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/01-architecture-overview.md[2] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/02-query-loop-and-context.md[3] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/03-tool-system.md[4] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/04-multi-agent-architecture.md[5] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/05-permissions-and-security.md[6] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/06-memory-and-state.md[7] 阅读原文: https://github.com/aga-j/claude-code-deep-dive/blob/main/07-engineering-insights.md
夜雨聆风