深度学习 Claude Code 源码:拆解 Anthropic 的迭代思路
深度拆解 Claude Code 源码:Anthropic 如何构建一个 Agentic AI 编程助手
Claude Code 是 Anthropic 推出的官方 CLI AI 编程助手。它不是一个简单的”终端聊天机器人”,而是一个能直接读写文件、执行命令、搜索代码、甚至派出多个 AI Agent 协同工作的全功能开发工具。
我对 Claude Code 的 TypeScript 源码进行了完整的逆向分析——1,905 个源文件、47 个工具模块、89 个命令、94 个 Feature Flag——试图还原 Anthropic 是如何一步步将一个 LLM 对话框架,迭代成一个成熟的 Agentic AI 工程系统的。
这篇文章不是使用教程,而是一份架构拆解与工程哲学分析。
一、技术全景
先用一张表看清全貌:
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
两个值得注意的选型决策:
为什么用 React 写终端 UI? Ink 库让 React 组件可以渲染到终端而非浏览器。Claude Code 的 UI 复杂度已经超出了传统 CLI 框架的能力——权限对话框、多 Agent 状态面板、流式 Markdown 渲染——React 的组件模型和状态管理在这里是合理选择。
为什么用 Bun 而不是 Node.js? Bun 的启动速度更快,且内置 bundler 支持 feature() 函数的编译时求值,使得 Dead Code Elimination 可以在打包阶段完成,而非运行时。这对一个有 94 个 Feature Flag 的项目至关重要。
二、架构五层模型
核心模块的代码量可以说明项目的重心分布:
• main.tsx(4,683 行)—— 启动编排,是整个系统的引导器
• QueryEngine.ts(1,295 行)—— LLM 交互循环,是运行时核心
• Tool.ts(792 行)—— 工具类型系统,是扩展性基石
接下来,我按照推导出的 9 个迭代阶段逐一拆解。
三、Phase 1:Tool Loop —— Agentic AI 的基石
传统 LLM vs Tool Use
传统 LLM 只能生成文本。Claude Code 使用 Tool Use 模式:AI 可以发出”调用工具”的结构化指令,系统执行后将结果喂回 AI,形成闭环。
这个循环就是所谓的 Query Loop,也是所有 Agentic AI 系统的基础架构。
AsyncGenerator:流式循环的实现
QueryEngine 的 submitMessage() 使用 TypeScript 的 async * 语法实现流式输出:
为什么用 AsyncGenerator 而非普通 async 函数?因为普通 async 函数要等全部完成才能返回,用户面对的是长时间空白后突然出现的大段结果。AsyncGenerator 每产生一条消息就 yield 出去,UI 立即渲染——这就是 Claude Code 实现”打字机效果”的底层机制。
工具的泛型类型系统
每个工具都是 Tool 三参数泛型:
• Input:用 Zod Schema 定义,运行时严格校验
• Output:执行结果类型,编译时检查
• Progress:进度数据类型,支持流式进度条
配合 buildTool() 函数的默认值注入(Builder Pattern 变体),工具开发者只需要定义核心逻辑,其他方法使用安全默认值:
工具排序——一个容易忽略的性能细节
工具注册表 assembleToolPool() 在合并内置工具和 MCP 工具时,会按名称排序:
原因:Claude API 支持 Prompt Caching。如果两次请求的 system prompt + tools 定义完全一致,API 复用缓存,节省计算。工具顺序不稳定会导致缓存失效——排序是为了保证幂等性。
四、Phase 2:权限系统 —— 信任是分层的
四级权限模式
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
三路由架构
权限检查不是简单的 if/else,而是根据运行模式路由到不同处理器:
Swarm Worker(多 Agent 场景中的工作者 Agent)的权限处理尤其有趣:
注意第 2 步在第 3 步之前——先注册监听,再发消息。如果顺序反过来,leader 的回复可能在注册之前到达,导致消息丢失。这是并发编程中的经典竞态条件防护。
25+ Hook 事件
系统定义了 25+ Hook 事件(PreToolUse, PostToolUse, PermissionRequest, SessionStart 等),用户和插件可以拦截几乎所有操作。比如 PreToolUse Hook 可以返回 { behavior: 'deny' } 来阻止某个工具执行——这就是自定义安全策略的入口。
五、Phase 3:五代上下文压缩 —— 最大的工程挑战
上下文管理是整个项目中工程量最大、迭代次数最多的部分。核心矛盾很简单:
压缩太激进 → AI 忘记关键信息 → 做出错误决策压缩太保守 → Token 超限 → API 报错
五代技术就是在这两个极端之间不断逼近最优解。
第一代:Full Compaction
调用一个”压缩 Agent”把整个对话历史总结为 9 段式摘要(目标/概念/文件/错误/推理/用户消息/待办/当前/下一步),然后用摘要替换原始对话。
关键设计:压缩 Agent 复用主对话的 Prompt Cache,不额外消耗完整的 system prompt 传输。
第二代:Auto-Compact
当 Token 使用量超过 85% 时自动触发压缩。加入了 Circuit Breaker(熔断器):连续 3 次失败后停止尝试,避免无限重试导致的资源浪费。
第三代:Cached Microcompact(最精妙的设计)
不修改本地消息,而是生成 cache_edits 指令告诉 API “在缓存层删除某些工具结果”:
为什么巧妙?传统压缩修改消息列表,不可逆。Cached Microcompact 只在 API 缓存层操作,本地数据完好——如果 API 调用失败需要重试,完整上下文仍然可用。这是关注点分离的典范。
只有 8 种”可压缩”工具的输出会被清理(FileRead、Shell、Grep、Glob、WebSearch、WebFetch、FileEdit、FileWrite),因为它们的输出要么可以重新获取,要么已经生效。
第四代:Time-Based Microcompact
利用 Prompt Cache 的 TTL(约 1 小时)过期时机:既然缓存已经过期、下次请求必须重发完整前缀,那就趁这个机会顺便清理旧数据。
这是一种机会主义优化——零额外成本。
第五代:Context Collapse
与 Auto-Compact 互斥运行。不是简单的”压缩全部”或”删除旧的”,而是基于语义理解哪些上下文当前有用,选择性折叠。
六、Phase 4:模型无感迁移 —— 别名 + 幂等迁移
Claude 的模型一直在更新(Opus 4.0 → 4.1 → 4.5 → 4.6),如何让用户无感升级?
别名系统:用户配置中写 'opus' 而非 'claude-opus-4-6-...',别名在运行时解析为最新版本。
幂等迁移函数:系统有 11 个迁移函数,启动时自动运行。每个都遵循相同模式:
一个微妙但关键的细节:迁移只读 userSettings 而不读合并后的 mergedSettings。如果读 merged 写 user,project 级别的设置会被意外提升到 user 级别,影响所有项目。配置系统的层级语义必须在迁移中得到尊重。
七、Phase 5:Plan Mode —— 从”直接做”到”先想再做”
Plan Mode 引入了 EnterPlanModeTool / ExitPlanModeTool / VerifyPlanExecutionTool 三个工具,实现了”规划 → 确认 → 执行 → 验证”的四步工作流。
在 Plan 权限模式下,AI 可以自由搜索和阅读代码(只读操作自动通过),但修改文件和执行命令需要等用户批准计划后才能进行。
后续还引入了 ULTRAPLAN(扩展规划)和 ULTRATHINK(深度思考),以及 Thinking Configuration 的三种模式(Adaptive / Enabled / Disabled)——让 AI 在规划阶段投入更多”思考预算”。
八、Phase 6:多 Agent 协作 —— AsyncLocalStorage 隔离
演进路径
核心挑战:同进程隔离
多个 Agent 跑在同一个 Node.js 进程内,如何隔离上下文?答案是 AsyncLocalStorage:
AsyncLocalStorage 类似 Java 的 ThreadLocal,但适用于 Node.js 的异步模型。每个异步调用链拥有独立的”局部变量空间”——Agent A 调用 readFile() 时看到的身份是 “researcher”,Agent B 同时调用 readFile() 看到的是 “coder”,互不干扰。
双路径通信
Agent 之间的通信有两条路径:
1. 内存回调(in-process agent,优先路径)——直接把权限请求放入 leader 的 React UI 确认队列
2. 文件 mailbox(跨进程 agent,退路)——写入 ~/.claude/teams/{team}/mailbox/ 目录,每 500ms 轮询
文件系统是唯一可靠的跨进程通信通道(Agent 可能跑在不同的 tmux pane 中)。
共享任务列表
团队成员通过 ~/.claude/tasks/{team-name}/ 目录共享任务。工作流:完成任务 → 标记完成 → 检查列表 → 认领下一个未阻塞的任务。
九、Phase 7:从封闭到开放 —— MCP / Skills / Plugins
MCP:AI 工具接入的 USB 标准
MCP (Model Context Protocol) 的设计灵感来自 LSP (Language Server Protocol):
在 Claude Code 中,MCP 工具和内置工具对 AI 完全等价——它们出现在同一个工具列表中,AI 自主决定调用哪个。第三方能力成为一等公民。
Skill 系统
Skill 是 Markdown 定义的可复用工作流模板。用户可以在 ~/.claude/skills/ 下创建自定义 Skill(比如 deploy-staging.md),之后用一句话触发整个流程。
压缩时 Skill 有专门的 Token 预算(每个 5K,总计 25K),确保压缩后 AI 仍然记得可用的 Skill。
十、Phase 8:全场景覆盖 —— 不止是终端
Claude Code 从纯 CLI 扩展到了多个运行环境:
|
|
|
|
|---|---|---|
|
|
|
|
|
|
BRIDGE_MODE |
|
|
|
CCR_AUTO_CONNECT |
|
|
|
DIRECT_CONNECT |
|
|
|
DAEMON |
|
|
|
SSH_REMOTE |
|
|
|
BYOC_ENVIRONMENT_RUNNER |
|
Bridge 架构使用 JWT 认证的双向消息协议,IDE 端可以发送权限请求、文件附件,CLI 端返回执行结果。
十一、Phase 9:KAIROS —— 从工具到助手
KAIROS 是目前最新的方向,标志着 Claude Code 从”被动的工具”向”主动的助手”转型:
|
|
|
|
|---|---|---|
|
|
KAIROS |
|
|
|
KAIROS_PUSH_NOTIFICATION |
|
|
|
KAIROS_GITHUB_WEBHOOKS |
|
|
|
KAIROS_CHANNELS |
|
|
|
KAIROS_BRIEF |
|
|
|
KAIROS_DREAM |
|
这不再是”你问一句我答一句”,而是一个有记忆、会主动思考、能监听外部事件的 AI 同事。
十二、启动优化 —— 毫秒级的工程较量
一个 CLI 工具的启动速度直接影响用户体验。Claude Code 在这方面做了极致优化:
Fast Path(快速退路)
claude --version 不加载 React、Ink、SDK——直接打印编译时内联的版本常量后退出,整个过程 < 10ms。
Parallel Prefetch(并行预取)
在模块 import 之前就启动 I/O 操作:
串行需要 240ms,并行只需要 135ms——I/O 被模块加载时间”吸收”了。
TLS 证书时序陷阱
一个容易被忽略的细节:Bun 使用 BoringSSL,它在第一次 TLS 握手时缓存证书库。如果自签名 CA 证书在首次连接之后才设置,BoringSSL 不会重新加载——企业内网连接直接失败。所以 applyExtraCACertsFromConfig() 必须在任何网络请求之前执行。
十三、Feature Flag —— 编译时 DCE 的工程哲学
Claude Code 有 94 个 Feature Flag,使用双层架构:
编译时(Bun DCE):feature() 在外部构建中被 stub 为 return false,Bun 的 bundler 直接消除不可达分支。代码不存在于产物中——零运行时开销、减小包体积、消除攻击面。
运行时(GrowthBook):支持 A/B 测试、按比例灰度发布、实时开关。不需要重新构建和发布。
这种双层设计解决了一个根本矛盾:编译时 Flag 效率最高但不灵活,运行时 Flag 灵活但有开销。两者结合,各取所长。
十四、状态管理 —— React 在终端中的实践
Claude Code 使用自定义的 Zustand-like Store:
内部使用 React 18 的 useSyncExternalStore,开发环境还有安全检查——如果 selector 返回整个 state 而非 slice,直接抛错。
大部分状态用 DeepImmutable 约束(递归 readonly),但 tasks 字段例外——因为它包含函数引用,不能被 readonly 约束。
十五、从源码中提炼的设计模式
最后,总结 12 个值得学习的工程模式:
|
|
|
|
|---|---|---|
| AsyncGenerator 流式输出 |
|
|
| Circuit Breaker 熔断器 |
|
|
| Builder Pattern |
|
|
| Memoize 单次执行 |
|
|
| Parallel Prefetch |
|
|
| Selector Subscription |
|
|
| AsyncLocalStorage |
|
|
| 幂等迁移 |
|
|
| 编译时 DCE |
|
|
| 机会主义优化 |
|
|
| 竞态条件防护 |
|
|
| 配置层级语义 |
|
|
结语
Claude Code 的迭代思路可以用一句话概括:
先让 AI 能动手,再让 AI 安全地动手,再让 AI 聪明地动手,再让一群 AI 一起动手,最后让 AI 成为你的长期搭档。
从 Tool Loop 到 KAIROS,Anthropic 走的每一步都遵循渐进增强的原则——用 Feature Flag 门控实验、用幂等迁移保证兼容、用编译时 DCE 控制产物体积。
没有”大爆炸式重写”,只有持续的、有意识的工程演进。
这或许才是构建一个复杂 AI 系统最值得学习的地方。
*本文基于 Claude Code TypeScript 源码(1,905 个文件)的完整逆向分析。所有代码片段均来自实际源码,经简化后用于说明架构设计。*
夜雨聆风