基于 Claude Code v2.1.88 还原源码分析
前言
如果你把市面上的 AI 编程工具都用一遍,会很快发现一个区别:
有些工具第一回合看起来很惊艳,但任务一复杂、会话一拉长、工具一并发,就开始变钝、变乱、变得需要你不停接管。
Claude Code 不一样。
它不一定总是“最会炫技”的那个,但往往是那个最稳、最顺、最像能长期干活的系统。
我最近把 Claude Code v2.1.88 的还原源码完整过了一遍。看完之后,我的判断比以前更明确了:
Claude Code 的优势,并不主要来自模型本身,而来自它把 Agent 做成了一套完整 runtime。
这篇文章我不再停留在“它很好用”的层面,而是回答三个更具体的问题:
- 它到底在哪些源码层面做得更好?
- 这些设计为什么会直接影响日常使用体验?
- 如果你也在做自己的 Agent,到底该抄它哪些作业?
先说结论:Claude Code 强在“运行时”,不是强在某个单点功能
很多人总结 Claude Code,会列一串功能:
- 工具多
- 命令多
- 权限做得细
- 会话更稳
- 支持多代理
这些都对,但还不够准确。
更准确的说法是:
Claude Code 真正强的,是它把“工具调用、上下文治理、权限控制、异常恢复、多代理隔离”都收敛进了同一套运行时。
换句话说,它不是“模型 + 一堆工具”的拼装,而是已经很接近一个 Agent OS。

如果你只盯着某个文件,比如最大的 query.ts,很容易看得很累。
更有效的阅读方式,是先建立这张脑内地图:
main.tsx负责启动、预热、装配QueryEngine.ts负责 session lifecyclequery.ts负责主循环services/tools/*负责工具调度和执行services/compact/*负责上下文治理useCanUseTool.tsx和 permissions 相关服务负责权限链路AgentTool/coordinator/*/remote/*负责多代理和隔离执行
这也是 Claude Code 和很多 Agent 工具最大的差别:
后者往往是“哪里需要逻辑就往哪里塞”,而它更像一套职责明确的系统。
如果你今天就要开始读这份源码,我建议不要一上来啃最大的文件,而是按“启动装配 -> 会话生命周期 -> 主循环 -> 工具执行 -> 治理链路”这条主线往下走。

更具体一点:
- 先看
main.tsx,搞清楚启动阶段到底装了哪些能力 - 再看
QueryEngine.ts,理解一次 session 是怎么被收口成统一入口的 - 然后进
query.ts,看真正的 agent loop 怎样处理流式输出、工具调用和恢复逻辑 - 接着顺着
StreamingToolExecutor/toolOrchestration.ts往下,理解它为什么能在复杂任务里保持工具执行秩序 - 最后再看
compact和 permissions 相关代码,理解它为什么不会越聊越乱、越跑越危险
这个阅读顺序的好处是,你会先建立系统模型,再去看实现细节。否则很容易一头扎进 query.ts 以后,只看到大量分支,却看不清这些分支为什么存在。
一、它不是把模型塞进终端,而是把会话生命周期从 UI 里抽了出来
这是 Claude Code 最值得学的一点。
很多 Agent 工具一开始就把自己写死在 UI 或 CLI 交互层里:
- 输入框状态在组件里
- 工具执行在某个回调里
- 权限确认是个弹窗逻辑
- 会话恢复是另一个补丁
短期能跑,但一旦你要支持:
- Headless 模式
- SDK 接入
- 子代理
- 远程执行
- 自动恢复
整个结构就会开始打架。
Claude Code 的解法是:先定义运行时,再定义交互层。
源码里最关键的分工就是:
main.tsx负责启动阶段的装配和预热QueryEngine.ts负责把一次会话的生命周期串起来query.ts负责进入真正的 agent loop
这背后的实际价值非常大。
因为它意味着:
- UI 不承担核心业务状态
- REPL 和 SDK 复用同一套执行路径
- 工具调用、消息回写、权限拒绝、压缩、fallback 都能挂在统一 runtime 上
从工程上说,这几乎就是一句话:
不要把 Agent 写成“聊天界面调模型”,要把它写成“交互界面调用运行时”。
如果你自己在做 Agent,这是一条优先级极高的建议。
因为一旦这层没抽干净,后面加再多能力都会越来越乱。
二、Claude Code 真正拉开差距的,不是工具数量,而是工具调度系统
很多 Agent 工具都会宣传:
- 能读文件
- 能改文件
- 能执行命令
- 能联网
但真正复杂任务里的问题从来不是“有没有工具”,而是:
- 哪些工具能并发?
- 哪些工具必须串行?
- 某个工具失败后,兄弟任务还要不要继续?
- 用户打断后,是取消还是等待安全收尾?
- 工具结果该不该立刻写回上下文?
Claude Code 在这件事上做得非常重。

1. 批次级调度,不是简单顺序执行
在 services/tools/toolOrchestration.ts 里,你能看到一个很关键的思想:
工具调用先被按语义分批,再决定执行顺序。
核心判断是 isConcurrencySafe。
这意味着 Claude Code 并不是默认:
- 全部串行
- 或全部并发
而是会先看工具的副作用语义:
- 读操作、查询操作,更适合并发
- 写操作、有状态修改的操作,更适合串行
很多工具做不稳,不是因为模型笨,而是因为它们把“调度”问题错误地交给了模型自己解决。
Claude Code 没这么做。
它把“工具是不是并发安全”上升成了运行时知识。
2. StreamingToolExecutor 处理的是“执行中的世界”,不是“执行前的想象”
更关键的一层,在 StreamingToolExecutor。
这里真正处理的是一个更现实的问题:
模型的输出是流式的,工具调用也是动态到达的。
所以运行时要处理的不是“拿到一组完整工具调用后怎么执行”,而是:
- 工具边到达边进入队列
- 有的已经开始执行
- 有的还在等待
- 有的可能需要取消
- 有的已经完成但不一定该立即暴露
源码里能看到它明确区分这些状态:
queuedexecutingcompletedyielded
同时还处理:
- sibling abort
- interrupt behavior
- fallback 时丢弃未完成结果
- 某个工具失败时如何传播取消
这已经不是“tool calling”,而是接近一个 action scheduler。
如果你自己做 Agent,我会建议你优先补两层:
- 工具语义声明
- 运行时调度器
不要幻想 prompt 自己就能把这件事解决掉。
三、长会话为什么不容易崩:它把上下文当成受控资源来治理
很多 Agent 第一次用都不错,真正拉开差距的是第十次、第二十次回合以后。
这时会暴露两个问题:
- 上下文越来越乱
- 错误恢复越来越差
Claude Code 在这块下了很重的功夫,而且不是简单一句“超长了就总结一下”。

在 services/compact/* 相关代码里,你能看到它至少做了这些事情:
- 估算有效上下文窗口
- 为 compact summary 预留 token
- 设置 warning threshold / error threshold / blocking limit
- 区分 reactive compact、micro compact、trim compact 等不同处理方式
- 在连续失败时熔断,避免 session 无限重试
这套设计最值得学的地方在于:
它把上下文溢出视为系统故障,而不是普通异常。
这和很多 Agent 工具的思路完全不同。
后者的逻辑通常是:
“超长了?那就随便总结一下吧。”
Claude Code 的逻辑更像:
“上下文是受控资源,需要像内存一样治理。”
这也直接解释了为什么它在长会话里更稳。
不是因为模型突然更聪明,而是因为运行时在持续控制上下文债务。
四、权限系统为什么“既不放任,也不烦人”
很多 Agent 工具在权限这件事上会走两个极端:
- 全放开,结果很危险
- 全询问,结果很烦
Claude Code 比较成熟的地方在于,它没有把权限做成一个 UI 弹窗功能,而是做成一条运行时链路。
源码里至少可以看到这些层:
- allow / deny / ask 规则
- 工具粒度匹配
- MCP server 级匹配
- sandbox override
- working directory 限制
- hooks 决策
- classifier 辅助决策
useCanUseTool.tsx 则把这整套链路真正串起来,并根据运行模式区分:
- 交互式会话怎么处理
- 子代理怎么处理
- swarm / coordinator worker 怎么处理
- headless / SDK 怎么处理
这背后的启发很明确:
权限不是“操作前问一句”,而是“运行时是否允许进入下一步”的统一判定。
如果你把权限只做成弹窗,后面一旦要支持后台代理、远程代理、多代理协作,整个权限体系很容易立刻崩掉。
五、最有指导意义的一点:它已经在为“真正的软件代理”做地基
如果你继续往下看,就会发现 Claude Code 早就不只是一个本地 CLI。
源码里能看到非常多信号:
AgentTool支持 background / teammate / remote / worktree 等不同执行后端coordinator/*在处理多代理协作remote/*在处理远程运行plugins/skills/MCP形成分层扩展体系main.tsx启动阶段已经在预取远程托管配置和能力注册信息
最关键的是,它并不是把这些能力都塞进一个“大插件系统”。
它的做法更像分层装配:
commands解决用户入口tools解决能力执行skills解决可复用工作流plugins解决本地扩展MCP解决外部能力接入
这套分层有两个直接好处:
- 用户入口清晰,不会所有能力都混成一种东西
- 不同能力类型可以走不同的安全与权限策略
这也是为什么我会说:Claude Code 不是在做一个“更强的聊天助手”,而是在做一个软件代理基础设施。
六、如果你也在做 Agent,最值得先抄哪几块作业?
这是看完源码以后,我觉得最有现实意义的部分。
因为大多数团队并不需要一次性做出 Claude Code 的全部能力,但非常值得先抄这四块。

1. 先把运行时和交互层分开
第一优先级,不是多加一个工具,而是把:
- 会话生命周期
- 工具调用
- 消息回写
- 权限判定
- 上下文治理
从 UI 里抽出来。
如果这一步不做,后面一旦加 Headless、自动化、子代理,代码会迅速失控。
2. 给工具加“语义”,不要只加 schema
很多团队做工具时,只定义:
- 名称
- 入参
- 出参
但真正决定稳定性的,是工具的运行时语义,比如:
- 是否可并发
- 是否有副作用
- 是否可以打断
- 失败后是否影响兄弟任务
Claude Code 强就强在这里。
它不是只知道“怎么调用工具”,而是知道“该怎么运行工具”。
3. 把上下文当成资源管理问题
不要把上下文管理理解成“写个总结 prompt”。
更稳的做法是:
- 先算预算
- 再设阈值
- 然后定义 warning / blocking / compact 策略
- 最后加失败熔断
这会极大改善长会话稳定性。
4. 权限一定要走统一判定链
如果未来要支持:
- 子代理
- 远程执行
- 自动化 worker
那权限系统必须是一条统一 runtime 链路,而不是各处散落的 if / confirm。
这件事越晚补,成本越高。
七、隐藏功能值得看,但它们不是 Claude Code 最核心的竞争力
源码里确实还能看到很多有意思的东西:
- Undercover mode
- Buddy 宠物系统
- KAIROS 自主代理模式
- 语音模式
- 远程托管设置
- 大量 feature flags
这些都说明 Claude Code 的产品边界远比表面看起来大。
但如果只把注意力放在这些“彩蛋”上,会错过真正重要的部分:
Claude Code 最核心的竞争力,不是它藏了多少功能,而是它已经把 Agent 的基本盘做对了。
也就是:
- runtime 抽象
- 工具调度
- 上下文治理
- 权限链路
- 多代理隔离
这些才是决定它为什么能长期稳定工作的东西。
最后一句
如果你今天还把 Agent 理解成“一个会聊天、会写代码的大模型外壳”,那你看到的还是第一阶段。
Claude Code 源码真正让我确认的一件事是:
下一代 Agent 的竞争,拼的已经不是“谁更会回答”,而是:
- 谁更像一个运行时
- 谁更像一个系统
- 谁更能在复杂任务里保持稳定、可控、可扩展
Claude Code 之所以更好用,不是因为它更会说,
而是因为它更会工作。
源码信息
- 分析版本:Claude Code v2.1.88
- 代码量:约 163,000 行 TypeScript
- 文件数:1,884 个文件
- 主要语言:TypeScript 6.0+ / Bun
- 终端 UI:React + Ink
源码仓库:
注:本文仅供学术研究与教育目的。使用者应自行遵守相关法律法规及服务条款。