claude code 源码泄露这事儿大家应该知道了吧?不知道的我再说一次。 2026 年 3 月 31 日,@anthropic-ai/claude-code npm 包的 source map 意外暴露了 R2 存储桶中的未混淆 TypeScript 源码,由 Chaofan Shou (@Fried_rice) 率先发现。这次泄露让我们有机会一窥这个企业级产品的完整架构设计。
Claude Code 项目全景总览
项目定位
这个仓库并不是一个普通的 Node CLI,而是一套把 CLI+AgentRuntime+ToolRuntime+TUI+扩展平台 压在同一个进程模型里的企业级 Agent 产品内核。
从前端工程师视角看,它最值得学习的不是某个单点功能,而是它如何把下面几件事工程化地拼成一个整体:
- 把用户输入组织成多轮可恢复的会话
- 把模型调用包装成可插拔的"查询引擎"
- 把文件、命令、MCP、插件、技能都抽象成统一能力面
- 把上下文压缩、子 agent 并行、权限控制、UI 反馈放进同一条主链路
- 在大量动态能力存在时,仍然尽量保持 prompt cache 稳定
建议分层
结合源码结构,我建议按下面 8 层学习:
- 启动与入口层
- 命令解析与路由层
- 会话状态与 UI 编排层
- 上下文构建与查询引擎层
- 工具系统与调度层
- Agent 与并行协作层
- 扩展机制与外部能力接入层
- 构建发布与工程化基础设施层
全局架构图

为什么这套分层对前端工程师有价值
- 它本质上是在做"复杂交互状态机",只是 UI 不在浏览器而在终端
- 它把"页面动作"升级成了"模型 + 工具 + 状态 + 权限"的统一事务
- 它的插件、技能、MCP,分别对应前端常见的微前端、配置中心、第三方能力接入
- 它对 prompt cache 稳定性的追求,很像前端里对渲染稳定性、缓存命中率、依赖边界的追求
关键源码入口
src/entrypoints/cli.tsx:轻量启动分流,先判断是否走 fast pathsrc/main.tsx:完整 CLI 启动编排中心src/entrypoints/init.ts:初始化环境、配置、遥测、网络能力src/commands.ts:命令注册中心src/tools.ts:工具注册与工具池组装中心src/screens/REPL.tsx:交互式产品主界面src/query.ts:核心查询循环src/QueryEngine.ts:面向 SDK / Headless 的会话引擎封装
用户输入到结果输出的主路径

建议阅读顺序
- 先读启动层,搞清楚它如何"分流"不同运行模式
- 再读命令层,理解为什么命令、技能、插件命令能并存
- 再读状态/UI 层,理解 REPL 为什么能承载这么多异步事件
- 再读查询与上下文层,理解真正的 Agent 主循环
- 再读工具与 Agent 层,理解并行、权限、共享缓存
- 最后读扩展层和工程化层,理解它为什么可以持续增长
设计亮点
entrypoints/cli.tsx用 fast path 把很多非主链路命令提前短路,减少冷启动成本main.tsx在顶层就开始并行预取多个昂贵资源,例如 keychain、MDM、GrowthBooktools.ts和commands.ts都是"统一注册中心",但保留 feature flag 与动态扩展入口query.ts不是简单 API 调用,而是多轮消息驱动的状态循环utils/forkedAgent.ts和tools/AgentTool/forkSubagent.ts把 prompt cache 设计显式化了
对前端工程师的启发
- 复杂产品先找"统一事件主循环",再看局部组件
- 学架构不要被目录数量吓到,先抓注册中心和主链路
- 好架构不是文件少,而是入口清晰、职责清晰、扩展清晰
可借鉴到业务系统的方式
- 把"页面行为"抽象成统一动作协议,而不是散落在页面组件里
- 把动态能力收敛到注册中心,避免在各个业务模块里偷偷扩展
- 把"主流程"和"扩展流程"隔离开,让复杂性可控增长
我建议重点学习的 3 个设计点
- 启动分流:先做 fast path,再做完整初始化
- 统一能力注册:命令、工具、插件、技能都走统一入口
- 主循环思维:把模型调用、工具执行、上下文压缩做成可恢复的循环
如果要做一个简化版
- 第一步只做
CLI入口+query loop+3个工具 - 第二步加
AppState+简单TUI - 第三步再加
插件/MCP/子agent``
01-启动与入口层
本层定位
这一层解决的问题不是"怎么跑起来",而是"如何用最小成本进入正确运行模式"。Claude Code 启动层明显分成两段:
- 轻量入口:
src/entrypoints/cli.tsx - 完整启动编排:
src/main.tsx与src/entrypoints/init.ts
这是一种很典型的企业级产品写法:先做早期分流,再做昂贵初始化。
核心职责
- 识别命令行参数并走 fast path
- 在真正加载大模块前做最便宜的预判
- 预取高成本依赖,缩短完整启动链路
- 初始化配置、遥测、网络、证书、远程设置、LSP 等全局能力
- 最终把命令、工具、状态、REPL、非交互模式拼装起来
关键模块
src/entrypoints/cli.tsx- 负责早期参数分流
- 对
--version、bridge、daemon、bg session、特殊 MCP server 等路径做短路 src/main.tsx- 真正的产品入口
- 负责启动时并行预取、命令/工具/插件/技能装配、模式切换
src/entrypoints/init.ts- 初始化配置系统、环境变量、遥测、代理、mTLS、清理逻辑
src/setup.ts- 偏会话级 setup,例如 cwd、hooks snapshot、worktree、tmux、session memory
关键调用链

代表性源码文件
src/entrypoints/cli.tsxsrc/main.tsxsrc/entrypoints/init.tssrc/setup.ts
为什么这样分层
如果一开始就无脑加载整个 CLI,会有几个问题:
- 简单命令也要承担全量模块冷启动成本
- 某些模式根本不需要 React/Ink、命令树、工具树
- 容易在顶层 import 过早触发配置、证书、鉴权、副作用
所以它把入口拆成:
cli.tsx:只做"识别路径"main.tsx:只做"产品装配"init.ts:只做"基础设施初始化"setup.ts:只做"会话级环境进入"
这就是典型的分层启动设计。
核心要素流程图

设计亮点
main.tsx在顶层注释里明确写了为什么要提前并行读 keychain / MDM,这说明它不是"经验主义优化",而是被真实启动耗时驱动出来的优化entrypoints/cli.tsx通过动态 import 把不同运行模式隔离,减少非必要模块求值init.ts把"安全环境变量"和"完整环境变量"分阶段处理,说明它兼顾了信任边界setup.ts先捕获 hooks 配置快照,再进入会话,这是一种很强的可审计意识
对前端工程师的启发
- 首屏优化不只是前端页面问题,CLI 也同样需要"启动路径拆分"
- 顶层 import 是架构行为,不只是语法行为
- 初始化必须分层,否则安全、性能、调试成本会混在一起
可借鉴到业务系统的方式
- Web 应用可以把"登录前壳层"和"完整应用壳层"拆开
- BFF / Node 服务可以把"配置初始化"和"请求级上下文初始化"拆开
- 中后台项目可把"路由命中前的数据预取"和"进入页面后的完整装配"拆开
我应该重点学习的 3 个设计点
- Fast path 设计:让轻量命令不承担完整产品冷启动
- 并行预取:把多个昂贵初始化前置并重叠执行
- 初始化分层:基础设施初始化与会话初始化分离
这些设计点对应前端什么能力
- Fast path 对应首屏路由拆分与懒加载能力
- 并行预取对应资源调度与 waterfall 优化能力
- 初始化分层对应应用壳层架构能力
如果要自己实现一个简化版
- 先做
cli.tsx->main.tsx两层入口 - 再把配置初始化独立成
init.ts - 最后再单独补
setup.ts去承接工作区、会话、权限等逻辑
02-命令解析与路由层
本层定位
这一层负责把"用户意图"映射成"可执行能力"。它不是简单的命令表,而是一个可动态合并的路由系统:
- 内建命令
- feature flag 控制的命令
- 技能命令
- 插件命令
- 工作流命令
核心职责
- 注册内建 slash command
- 处理不同账号形态、不同 provider、不同 feature flag 下的命令可见性
- 动态合并技能、插件、工作流生成的命令
- 在交互式 REPL 和非交互模式中提供一致的命令源
关键模块
src/commands.ts- 内建命令注册中心
meetsAvailabilityRequirement()负责 provider 级过滤loadAllCommands()负责合并技能、插件、工作流命令src/skills/loadSkillsDir.ts- 把 markdown 技能转为命令对象
src/utils/plugins/loadPluginCommands.ts- 从插件里读取命令与技能
src/plugins/builtinPlugins.ts- 注册内置插件带来的技能命令
关键调用链

代表性源码文件
src/commands.tssrc/skills/loadSkillsDir.tssrc/plugins/builtinPlugins.tssrc/utils/plugins/loadPluginCommands.ts
为什么它不是"一个大数组"那么简单
因为命令系统要同时满足几件事:
- 某些命令只在特定 build / feature 下出现
- 某些命令只有 claude.ai 用户或 console 用户可用
- 插件和技能会在运行时增减
- 同一能力既可能是 slash command,也可能在 Tool/Skill 体系里被模型间接调用
所以这里真正的设计重点是:
- 注册中心统一
- 过滤逻辑集中
- 动态来源可合并
核心要素流程图

设计亮点
commands.ts既是注册中心,也是策略中心- 命令的 availability 先于
isEnabled(),说明作者把"能力资格"和"功能开关"区分开了 - 技能被转成命令对象这一做法非常重要,它让"配置文件描述能力"也能进入统一路由层
对前端工程师的启发
- 路由系统不该只管页面跳转,它也可以是产品能力注册中心
- 当系统有静态能力和动态能力时,关键不是分开管理,而是统一出口
- "权限、可见性、来源"要在路由层集中表达,不能散在页面里
可借鉴到业务系统的方式
- 后台系统的操作菜单、页面入口、批处理能力都可以统一建模
- 低代码平台可把"内建动作 + 插件动作 + 配置动作"统一成 Action Registry
- SaaS 产品可把租户能力、套餐能力、实验能力统一放到路由决策层
我应该重点学习的 3 个设计点
- 统一命令描述模型
- 动态命令合并机制
- availability 与 enabled 分层判断
这些设计点在前端领域分别对应什么能力
- 统一命令模型对应统一路由协议设计能力
- 动态合并对应插件化与运行时装配能力
- 分层判断对应权限与实验系统设计能力
如果我要自己实现一个简化版
-
先做一个
Command类型和静态命令表 -
再加 markdown 配置驱动命令
-
最后加插件来源和可见性过滤
03-会话状态与 UI 编排层
本层定位
这一层决定 Claude Code 为什么不是"命令执行器",而是"可持续交互的产品"。它把:
- AppState
- Store
- REPL
- 各种 hook
- 远程事件、权限事件、MCP 事件、任务事件
全部编排进一个终端 UI runtime。
核心职责
- 持有整场会话的全局状态
- 让 TUI 对消息流、任务流、权限流持续响应
- 承接交互模式和 Headless 模式之间的状态差异
- 管理 foreground / background task、agent 视图、bridge、MCP、插件、通知
关键模块
src/state/AppStateStore.ts- 定义了巨大的
AppState结构 - 能看到系统真正关心的产品域模型:任务、MCP、插件、通知、bridge、agent registry、permission context
src/state/store.ts- 一个极简 store,实现
getState/setState/subscribe src/state/onChangeAppState.ts- 把状态变化转成副作用,比如同步权限模式、保存配置、应用 env
src/screens/REPL.tsx- 交互式产品主舞台
- 汇聚输入、消息渲染、任务导航、MCP 对话框、权限对话框、远程会话等
关键调用链

代表性源码文件
src/state/AppStateStore.tssrc/state/store.tssrc/state/onChangeAppState.tssrc/screens/REPL.tsx
为什么值得前端工程师重点看
因为它非常像一个超复杂前端应用,只是渲染容器换成了终端:
REPL.tsx类似单页应用的根容器AppState类似全局应用状态模型- 大量 hook 类似浏览器里的事件订阅与副作用系统
- message / task / permission / mcp 都是在做跨模块状态编排
它最强的一点是:UI 不是业务逻辑的边角料,而是 Agent runtime 的第一等公民。
核心要素流程图

设计亮点
store.ts非常小,但把复杂度让给领域状态与订阅系统,而不是框架本身onChangeAppState.ts是一个很好的"副作用闸口",避免状态变化副作用散落AppState中mcp/plugins/tasks/agentNameRegistry/remoteConnectionStatus这些字段,基本就把产品域模型画出来了
对前端工程师的启发
- 大状态不是坏事,坏的是没有域边界和副作用出口
- 根组件不是只渲染页面,它也应该是运行时编排器
- 状态变更后的副作用一定要集中,而不是哪里改状态哪里顺手做副作用
可借鉴到业务系统的方式
- 把复杂中后台的消息、通知、任务、权限弹窗统一放到应用级状态机里
- 把副作用归并到单一出口,例如 store middleware 或 domain event 层
- 把"页面应用"改造成"事件驱动应用",尤其适合 AI Copilot、IM、工作台产品
我应该重点学习的 3 个设计点
- AppState 的领域建模方式
- Store + onChange 的轻量状态架构
- REPL 作为运行时容器的设计方式
这些设计点在前端领域分别对应什么能力
- 领域建模对应复杂业务抽象能力
- 轻量状态架构对应全局状态设计能力
- 运行时容器对应应用壳层与事件编排能力
如果我要自己实现一个简化版
- 先做一个只包含
messages/tasks/permissions的 store - 再做 REPL 主容器或 Web 根容器
- 最后加 onChange 副作用层,统一处理持久化和外部同步
04-上下文构建与查询引擎层
本层定位
这一层是 Claude Code 的心脏。它解决的不是"发一次模型请求",而是:
- 如何组织 system prompt、user context、system context、messages
- 如何在多轮工具调用中持续推进
- 如何在上下文过大时做 compact / auto-compact / snip
- 如何把 REPL 与 Headless/SDK 共用同一套查询能力
核心职责
- 构建 cache-critical 的 prompt 前缀
- 驱动多轮 query loop
- 处理中断、恢复、token budget、fallback model
- 在需要时触发上下文压缩与恢复
- 为子 agent 暴露 cache-safe params,提升并行调用的缓存命中率
关键模块
src/query.ts- 真实的 query loop
- 负责 tooluse -> toolresult -> 下一轮 assistant 的循环
src/QueryEngine.ts- 对 query loop 的面向对象封装
- 更适合 SDK / headless 场景
src/utils/queryContext.ts- 负责 system prompt、user context、system context 的统一获取
src/services/compact/autoCompact.ts- 自动 compact 门槛与策略
src/services/compact/compact.ts- 真正的 compact 执行逻辑
src/utils/forkedAgent.ts- 管理 cache-safe params,支持 forked agent 共享 prompt cache
关键调用链

代表性源码文件
src/query.tssrc/QueryEngine.tssrc/utils/queryContext.tssrc/services/compact/autoCompact.tssrc/services/compact/compact.tssrc/utils/forkedAgent.ts
为什么这层最值得深挖
很多 AI 工程只停留在:
- 拼 prompt
- 调 API
- 回显结果
Claude Code 则把"上下文"当成一个长期运行的系统对象来经营。这里至少有四个重点:
- 查询循环不是一次请求,而是一个带状态的多轮状态机
- system prompt 与 tools 顺序会影响 prompt cache,因此它非常重视前缀稳定性
- 上下文过长不是异常情况,而是正常产品场景,所以 compact 被内建进主流程
- 子 agent 不只是并行运行,更要尽量复用父上下文的缓存前缀
核心要素流程图
1. 一次 query 的主循环

2. cache-safe params 与 fork 共享缓存

设计亮点
utils/queryContext.ts很明确地把"影响 cache key 的前缀"单独抽出来,这是高水平设计autoCompact.ts把上下文窗口控制做成正式策略,不是出错后补救compact.ts不只做摘要,还会处理附件、技能、文件恢复等"后 compact 修复"QueryEngine.ts说明团队在把 REPL runtime 和 SDK runtime 抽象到统一模型
对前端工程师的启发
- 上下文管理很像前端里的"状态归档 + 局部恢复 + 缓存命中"
- 真正复杂的系统不是一个大函数,而是一条可恢复的循环
- 当产品开始有"长会话"特征时,压缩、降采样、摘要不是优化项,而是架构项
可借鉴到业务系统的方式
- 聊天产品可做"历史摘要 + 最近消息热区"
- 工作台产品可做"长任务上下文压缩 + 最近操作重建"
- Web Copilot 可做"主线程上下文 + 子任务共享前缀"
我应该重点学习的 3 个设计点
- Query loop 的状态机写法
- Compact 作为主流程而不是补丁功能
- CacheSafeParams 的显式建模
这些设计点在前端领域分别对应什么能力
- 状态机写法对应复杂异步流程编排能力
- Compact 对应长生命周期数据治理能力
- CacheSafeParams 对应缓存边界与依赖稳定性设计能力
如果我要自己实现一个简化版
- 先做
messages->model->tool->model的最小循环 - 再加 token 统计与超限前压缩
- 最后做父任务与子任务共享上下文前缀
05-工具系统与调度层
本层定位
这一层决定了 Claude Code 为什么能从"聊天"变成"执行"。模型本身只会生成 tool_use,真正让产品有行动能力的是这一层:
- 统一工具描述协议
- 工具池组装
- 权限检查
- 并发与串行调度
- 流式工具执行
核心职责
- 注册内建工具与 feature-gated 工具
- 组装 built-in tools 与 MCP tools 的统一工具池
- 根据权限上下文和模式过滤工具
- 判断工具是否可并发执行
- 在流式输出中尽早启动工具,同时保证结果回填顺序可控
关键模块
src/tools.ts- 内建工具注册中心
assembleToolPool()是 built-in 与 MCP 工具池合并的统一入口src/Tool.ts- 工具基础类型、工具匹配与工具上下文
src/services/tools/toolOrchestration.ts- 批处理执行工具
- 先分 batch,再决定并发或串行
src/services/tools/StreamingToolExecutor.ts- 流式工具执行器
- 支持一边收到 tool_use,一边提前运行
关键调用链
工具池组装流程(assembleToolPool)

代表性源码文件
src/tools.tssrc/Tool.tssrc/services/tools/toolOrchestration.tssrc/services/tools/StreamingToolExecutor.ts
核心问题:为什么一定要有"工具池组装层"
因为这个系统的工具并不是静态的:
- 内建工具会受 feature flag、平台、模式影响
- MCP tools 是动态接入的
- REPL mode 会隐藏某些 primitive tools
- simple mode、coordinator mode、worktree mode 都会影响工具集合
所以它不能让各处自己拼工具,而是必须有 tools.ts 作为单一事实来源。
核心要素流程图
1. 工具池组装流程
核心函数 : assembleToolPool(permissionContext,mcpTools)
这是工具系统的单一事实来源,负责将 built-in 工具和 MCP 工具合并成最终的工具池。

代码示例 - 工具池组装的核心逻辑:
css
// src/tools.ts:345-367export function assembleToolPool( permissionContext: ToolPermissionContext, mcpTools: Tools,): Tools { // 1. 获取 built-in 工具(已按 mode/feature/deny 过滤) const builtInTools = getTools(permissionContext) // 2. 过滤 MCP 工具 const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext) // 3. 分别排序后合并(保证 prompt cache 稳定性) // built-in 工具作为连续前缀,避免 MCP 工具插入破坏缓存 const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name) return uniqBy( [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)), 'name', // 去重:built-in 优先 )}
关键设计点:
- 缓存稳定性:工具排序确保 prompt cache 可复用,避免 MCP 工具插入导致缓存失效
- 权限一致性:built-in 和 MCP 工具使用相同的 deny 规则过滤
- 去重策略:built-in 工具优先,避免 MCP 工具覆盖内建功能
2. 工具调度执行流程
核心函数 : runTools() - 编排工具的并发/串行执行
这个调度器通过 isConcurrencySafe 判断工具是否可以并发执行,优化响应速度同时保证安全性。

代码示例 - 工具并发安全判定:
ruby
// src/Tool.ts:402 - 工具接口定义export type Tool={// ... 其他属性 isConcurrencySafe(input: z.infer<Input>):boolean isReadOnly(input: z.infer<Input>):boolean isDestructive?(input: z.infer<Input>):boolean}
// src/tools/BashTool/BashTool.ts - Bash 工具示例isConcurrencySafe(input){// Bash 命令默认不安全,可能有副作用returnfalse}
// src/tools/FileReadTool/FileReadTool.ts - Read 工具示例isConcurrencySafe(input){// Read 是只读操作,可以安全并发returntrue}
// src/tools/FileEditTool/FileEditTool.ts - Edit 工具示例isConcurrencySafe(input){// Edit 是写操作,需要串行执行returnfalse}
执行策略对比:
| 工具类型 | isConcurrencySafe | 执行策略 | 典型工具 |
|---|---|---|---|
| 只读查询 | ✅ true | 并发执行 | Read, Grep, Glob, WebFetch |
| 写入操作 | ❌ false | 串行执行 | Edit, Write, Bash |
| 危险操作 | ❌ false | 串行执行 | Bash(rm), Write(覆盖) |
关键设计点:
- 抽象层次 :用
isConcurrencySafe()而非硬编码工具名,支持自定义工具 - 安全优先:解析失败或异常时默认不安全,采用保守策略
- 上下文传递:非并发工具支持 contextModifier,允许修改后续工具的上下文
设计亮点
assembleToolPool()明确强调 prompt cache 稳定性,因此 built-in 与 MCP tool 的排序不是随便写的toolOrchestration.ts用isConcurrencySafe而不是"按工具名硬编码并发规则",抽象层次更高StreamingToolExecutor.ts把"流式收到工具调用"和"安全调度执行"结合起来,说明团队在优化响应延迟
对前端工程师的启发
- 当能力越来越多时,一定要有统一注册中心
- 并发不是 Promise.all 就完了,关键是定义什么可以并发
- 真正好的 runtime 会同时考虑吞吐、延迟和结果顺序
可借鉴到业务系统的方式
- 低代码平台可把 action 执行分成只读 action 和写 action
- 工作流平台可按副作用级别做并发编排
- 前端埋点、同步、草稿保存等异步动作也能做统一调度中心
进阶:流式工具执行器详解
3. StreamingToolExecutor 工作流程
核心类 : StreamingToolExecutor
这是 Claude Code 在性能优化上的关键设计,实现了"边接收边执行"的能力,显著降低端到端延迟。

内建工具分类表
根据源码分析,Claude Code 的 60+ 内建工具按并发安全性分为以下几类:
| 分类 | 并发安全 | 典型工具 | 说明 |
|---|---|---|---|
| 文件读取类 | ✅ Safe | Read, Glob, Grep | 只读操作,无副作用 |
| 网络请求类 | ✅ Safe | WebFetch, WebSearch | 独立的 HTTP 请求 |
| 查询类 | ✅ Safe | TaskOutput, TaskList, TaskGet | 只读查询状态 |
| MCP 资源类 | ✅ Safe | ListMcpResources, ReadMcpResource | 只读 MCP 资源 |
| 工具搜索类 | ✅ Safe | ToolSearch | 元数据查询 |
| 文件写入类 | ❌ Unsafe | Edit, Write, NotebookEdit | 有写操作,需串行 |
| Shell 执行类 | ❌ Unsafe | Bash, PowerShell | 可能有副作用 |
| 任务管理类 | ❌ Unsafe | TaskCreate, TaskUpdate, TaskStop | 修改任务状态 |
| 模式切换类 | ❌ Unsafe | EnterPlanMode, ExitPlanMode | 改变执行模式 |
| 用户交互类 | ❌ Unsafe | AskUserQuestion | 需要用户响应 |
| Agent 类 | ❌ Unsafe | Agent | 启动子 Agent |
| 调度类 | ❌ Unsafe | CronCreate, CronDelete | 修改定时任务 |
06-Agent 与并行协作层
本层定位
这一层是 Claude Code 最有"产品壁垒"的部分之一。它并不满足于单线程主 agent,而是把:
- 子 agent
- teammate / swarm
- agent-specific MCP
- fork subagent
- prompt cache 共享
做成了一套相对完整的并行协作体系。
核心职责
- 创建和运行子 agent
- 继承或裁剪父上下文
- 维持 agent 的工具、权限、模型、MCP 组合
- 在并行执行时尽可能共享 prompt cache
- 为主线程提供任务视图和消息回传能力
关键模块
src/tools/AgentTool/runAgent.ts- 子 agent 主执行入口
- 会处理 agent-specific MCP、工具解析、上下文构建、消息记录
src/tools/AgentTool/forkSubagent.ts- 为 fork child 构造 byte-identical 的上下文前缀
- 非常关键,直接体现"共享缓存降低并行成本"
src/tools/shared/spawnMultiAgent.ts- 更偏 teammate/swarm 侧的多 agent 启动
src/utils/forkedAgent.ts- CacheSafeParams 与 createSubagentContext 的基础设施
关键调用链

代表性源码文件
src/tools/AgentTool/runAgent.tssrc/tools/AgentTool/forkSubagent.tssrc/tools/shared/spawnMultiAgent.tssrc/utils/forkedAgent.ts
子 agent 设计里最值得学的点
1. 并行不只是"多开几个任务"
很多系统做并行只是:
- 复制 prompt
- 启多个请求
- 等待结果
Claude Code 更进一步:
- 子 agent 会继承父上下文的 cache-safe 部分
- 会尽量让多个 fork child 共享相同前缀
- 会让工具结果占位文本保持一致,提升缓存命中
这说明他们不是只追求并行,而是追求"低成本并行"。
2. 子 agent 不是完全孤立
runAgent.ts 里能看到:
- agent-specific MCP server 可以附加到父客户端集合上
- 共享的客户端不会被误清理
- 子 agent 有自己独立的 transcript / metadata / file state 副本
这是一种"控制性继承"。
核心要素流程图
1. Fork Child 如何共享缓存
核心问题:当父 agent 需要并行派发多个子任务时,如何让它们共享 prompt cache 以降低成本?
解决方案 :通过 buildForkedMessages() 构造字节完全相同的消息前缀。

2. 子 agent 生命周期

设计亮点
forkSubagent.ts里对 prompt cache 的处理非常"显式",这说明团队把成本优化放进了架构层runAgent.ts把 agent-specific MCP、transcript、工具上下文都纳入统一生命周期spawnMultiAgent.ts说明系统支持多种协作后端,不局限于当前 UI 形态
对前端工程师的启发
- 并行体系设计最重要的是"共享什么,隔离什么"
- 降低并行成本常常靠缓存与前缀稳定,而不是单纯靠更高并发
- 当子任务越来越多时,必须先定义 lineage、上下文继承和清理边界
可借鉴到业务系统的方式
- 多 tab 协作、后台任务编排、低代码多 worker 执行都适合这套思路
- Web Copilot 可用主线程上下文 + 子任务共享前缀降低成本
- 工作流引擎可借鉴"共享客户端、隔离本地状态"的设计
我应该重点学习的 3 个设计点
- CacheSafeParams 设计
- 子 agent 的控制性继承
- 并行运行的生命周期管理
这些设计点在前端领域分别对应什么能力
- CacheSafeParams 对应缓存稳定性与依赖控制能力
- 控制性继承对应上下文隔离与共享设计能力
- 生命周期管理对应复杂异步任务治理能力
如果我要自己实现一个简化版
- 先做一个只能继承父 prompt 的子 agent
- 再做共享文件状态缓存和独立消息流
- 最后再做多 agent 并行与缓存前缀稳定优化
07-扩展机制与外部能力接入层
本层定位
这一层决定 Claude Code 是否能从"一个产品"成长成"一个平台"。它的扩展面至少包括三类:
- 技能系统
- 插件系统
- MCP 系统
其中:
- 技能更像 prompt / command / workflow 的轻量扩展
- 插件更像带生命周期和分发渠道的产品扩展
- MCP 更像外部能力总线
核心职责
- 从磁盘或市场加载插件与技能
- 把外部能力转成命令、工具、资源
- 统一接入本地与远程 MCP server
- 管理 OAuth、连接状态、动态重连、资源读取
- 维持扩展能力与主系统之间的安全和边界
关键模块
src/skills/loadSkillsDir.ts- 从 markdown/frontmatter 装载技能
- 可解析工具限制、参数、hooks、agent、effort、context
src/utils/plugins/pluginLoader.ts- 插件发现、缓存、版本化、安装与加载
src/plugins/builtinPlugins.ts- 内置插件注册中心
src/services/mcp/client.ts- MCP 连接、工具/资源/提示能力拉取、认证与错误处理
关键调用链

代表性源码文件
src/skills/loadSkillsDir.tssrc/utils/plugins/pluginLoader.tssrc/plugins/builtinPlugins.tssrc/services/mcp/client.ts
三类扩展各自像什么
1. 技能系统
更像"配置驱动的命令与提示模板系统",优点是:
- 扩展门槛低
- 更适合 prompt 复用
- 可以附带工具限制、agent 选择、hooks
2. 插件系统
更像"正式扩展包系统",优点是:
- 能提供多组件能力
- 有 marketplace / 版本 / 缓存 / 启停状态
- 支持更稳定的分发与治理
3. MCP 系统
更像"外部能力协议层",优点是:
- 不需要把第三方能力硬编码进主仓库
- 外部 server 可以提供 tools、resources、prompts
- 主系统只需要统一协议和安全边界
核心要素流程图
1. 扩展接入总览
三种扩展类型的统一接入流程

2. MCP 接入路径
从配置到可用的完整生命周期

传输协议对比:
| 协议 | 适用场景 | 优势 | 示例 |
|---|---|---|---|
| stdio | 本地命令行工具 | 简单、低延迟 | 数据库工具、文件系统工具 |
| SSE | 实时推送服务 | 双向通信、实时更新 | 浏览器自动化、实时监控 |
| HTTP | REST API 服务 | 广泛兼容、易于代理 | 外部 API、云服务 |
错误处理机制:
McpAuthError- 认证失败,需要用户重新授权McpSessionExpiredError- 会话过期,需要重新连接McpToolCallError- 工具调用失败,包含错误信息
3.技能系统加载流程
Frontmatter 配置示例:
css
---name: commitdescription:Create a git commit with auto-generated messagetools:-Bash(git status)-Bash(git diff)-Bash(git add)-Bash(git commit)hooks:PreToolUse:-if: tool =="Bash" command:"echo 'About to run: {{input.command}}'"effort: highagent:Plan---
## 使用说明
执行智能提交,自动分析变更并生成 commit message。
关键设计点:
- 延迟加载 - 只解析 frontmatter,完整内容在调用时加载
- 工具限制 - 通过
tools字段限制可用的工具范围 - Hooks 注入 - 支持生命周期钩子,实现自动化流程
- Agent 指定 - 可指定特定 agent 执行(如 Plan agent)
设计亮点
loadSkillsDir.ts让 markdown 成为正式扩展协议,这很适合知识型产品pluginLoader.ts明显考虑了 marketplace、版本缓存、seed cache,说明它不是玩具插件系统mcp/client.ts支持多 transport、认证、资源截断、结果持久化,是很完整的协议适配层
对前端工程师的启发
- 平台化能力的前提不是"先做插件市场",而是先统一能力协议
- 扩展系统至少要区分轻扩展与重扩展
- 接第三方能力时,不要直接耦合业务代码,先做协议适配层
可借鉴到业务系统的方式
- 低代码平台可把模板、插件、外部连接器分层设计
- 企业工作台可把 AI 技能、组织插件、第三方工具统一收口
- Web 应用可借鉴"轻扩展 markdown + 重扩展 plugin + 协议扩展 connector"
我应该重点学习的 3 个设计点
- 扩展协议分层
- 插件缓存与版本化
- MCP 这种协议适配层的组织方式
这些设计点在前端领域分别对应什么能力
- 协议分层对应平台型产品设计能力
- 版本化对应工程治理与发布能力
- 协议适配对应第三方生态接入能力
如果我要自己实现一个简化版
- 先做 markdown 技能系统
- 再做本地目录插件系统
- 最后再做统一 connector/MCP 适配层
08-构建发布与工程化基础设施层
本层定位
这层在当前仓库里不是最完整的一层,因为这里主要是泄露出的 src/,并不包含完整的构建

能看出很清晰的工程化取向。
这里我会明确区分:
- 已能从源码确认的设计
- 根据源码高度推断的设计
核心职责
- 基于 feature flag 做 build-time 剪裁
- 控制启动性能与模块装载成本
- 为不同运行模式复用同一套核心逻辑
- 管理遥测、清理、证书、代理、远程配置、策略限制等基础设施
- 保证插件、MCP、bridge、daemon 等子系统可以渐进扩展
关键模块
src/main.tsx- 顶层并行预取与延迟加载
src/entrypoints/cli.tsx- 运行模式分流
src/entrypoints/init.ts- 网络、遥测、清理、配置、策略能力初始化
src/tools.ts与src/commands.ts- 大量
feature('...')分支,明显依赖构建期能力裁剪 src/services/analytics/growthbook.ts- 运行时实验开关与配置刷新
关键调用链

代表性源码文件
src/entrypoints/cli.tsxsrc/main.tsxsrc/entrypoints/init.tssrc/tools.tssrc/commands.tssrc/services/analytics/growthbook.ts
已确认的工程化特征
1. 强依赖 build-time feature flag
源码里大量使用 feature('...') 与条件 require():
COORDINATOR_MODEKAIROSBRIDGE_MODEVOICE_MODEWORKFLOW_SCRIPTSAGENT_TRIGGERS
这意味着他们不是只做运行时 if,而是在编译时就裁剪产品切面。
2. 冷启动优化是系统性设计
可以确认的手段包括:
- 顶层预取 keychain / MDM
- 早期 fast path
- 延迟 import 重模块
- 多模式分流
3. 基础设施初始化被独立管理
init.ts 里能看到:
- 遥测初始化
- mTLS / proxy
- CA cert
- graceful shutdown
- remote managed settings
- policy limits
说明他们把"产品能力"和"基础设施能力"明确分层。
推测但大概率成立的部分
- 推测使用 Bun 相关构建链做 feature-based bundle 裁剪
- 推测内部与外部 build 产物存在明显差异,因为很多 ant-only 逻辑被条件消除
- 推测发布时会输出多个运行形态,例如普通 CLI、bridge/daemon 相关模式
这些是推测,因为当前仓库没有完整构建脚本与 CI 配置。
核心要素流程图

设计亮点
- 运行模式多,但入口收敛
- 产品能力多,但通过 feature flag 保持 build 结果可裁剪
- 运行时实验与构建时裁剪并存,形成"两层开关体系"
对前端工程师的启发
- 真正的工程化不是"接了多少工具链",而是架构是否允许产品被安全裁剪
- build-time 和 runtime 的开关职责不同,不能混用
- 大型应用必须把基础设施初始化当作独立层
可借鉴到业务系统的方式
- Web 多租户平台可做 build-time 剪裁 + runtime 实验双层控制
- 中台可把"基础设施接入"独立出业务层
- 桌面端或 Electron 产品很适合借鉴这种多入口、多模式分流
我应该重点学习的 3 个设计点
- build-time 与 runtime 开关分层
- 启动性能工程化
- 基础设施初始化独立化
这些设计点在前端领域分别对应什么能力
- 开关分层对应产品形态治理能力
- 启动性能对应首屏与 bundle 体积治理能力
- 初始化独立化对应平台底座设计能力
如果我要自己实现一个简化版
- 先只做 runtime feature flag
- 再把重模块改成 lazy import
- 最后再引入 build-time 裁剪,把不同产品形态分包
总结与启示
整体架构总结
Claude Code 的核心不是"一个能调模型的 CLI",而是一个把以下能力统一进同一 runtime 的系统:
- CLI 启动分流
- 命令与工具注册
- REPL/TUI 产品壳
- Query loop 主循环
- 上下文压缩与恢复
- Agent 并行协作
- 插件、技能、MCP 扩展平台
- 权限、策略、遥测、远程配置等基础设施
如果用一句话概括:
它把 AI Agent 产品做成了一套"有前端壳层思维的终端应用平台"。
关键节点总流程图
完整架构运行流程

核心数据流

这个项目最厉害的工程设计点
1. 主循环思维非常完整
它不是简单的 "prompt in, text out",而是一个完整的状态机循环:
标准 Agent 循环:
输入→上下文构建→模型请求→工具执行→状态更新→压缩恢复→下一轮
Claude Code 的增强版循环:
ini
// src/query.ts 核心循环伪代码asyncfunction* query(){while(true){// 1. 上下文构建const messages =await normalizeMessagesForAPI(conversationHistory)
// 2. Token 预算管理const budget = calculateTokenBudget(messages, tools)
// 3. 模型请求(流式)const stream = streamMessages({ messages, tools, budget })
// 4. 解析响应forawait(constevent of stream){if(event.type ==='tool_use'){// 5. 工具执行(并发/串行调度)const results =await runTools(toolUseBlocks)
// 6. 结果回填yield* results
// 7. 上下文更新 conversationHistory.push(...results)}}
// 8. 上下文压缩(如果需要)if(shouldCompact(conversationHistory)){ conversationHistory =await compact(conversationHistory)}}}
这才是企业级 Agent runtime 的样子------每个环节都有明确职责和优化空间。
2. Prompt Cache 稳定性被提升到架构层
最打动人的地方不是有子 agent,而是团队把 API 成本优化 做成了架构级能力:
关键设计决策:
| 设计点 | 传统做法 | Claude Code 做法 | 收益 |
|---|---|---|---|
| 工具排序 | 随意顺序 | 按名称排序,built-in 作为稳定前缀 | 缓存命中率提升 |
| Fork Child | 复制完整上下文 | 构造 byte-identical 前缀 + 不同 directive | 多个子 agent 共享缓存 |
| MCP 工具 | 动态加载 | 固定占位符,延迟加载详情 | 避免缓存失效 |
| 上下文压缩 | 简单截断 | 智能保留关键信息,维护缓存稳定 | 减少重复请求 |
代码示例 - 工具池排序:
css
// src/tools.ts:assembleToolPool()exportfunction assembleToolPool(permissionContext, mcpTools){const builtInTools = getTools(permissionContext)const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext)
// 关键:分别排序后合并,built-in 作为稳定前缀const byName =(a, b)=> a.name.localeCompare(b.name)return uniqBy([...builtInTools].sort(byName)// built-in 先排序.concat(allowedMcpTools.sort(byName)),// MCP 后排序'name')}
代码示例 - Fork Child 缓存共享:
css
// src/tools/AgentTool/forkSubagent.ts:buildForkedMessages()const FORK_PLACEHOLDER_RESULT ='Fork started --- processing in background'
// 所有 fork children 使用相同的占位符const toolResultBlocks = toolUseBlocks.map(block =>({ type:'tool_result', tool_use_id: block.id, content:[{ type:'text', text: FORK_PLACEHOLDER_RESULT }]// 完全相同!}))
// 只有最后的 directive 不同const directive = buildChildMessage(uniqueDirective)
成本收益估算:
- 假设前缀 10K tokens,10 个 fork children
- 缓存命中:10K × 10 = 100K tokens 节省
- 按 <math xmlns="http://www.w3.org/1998/Math/MathML"> 15 / 1 M t o k e n s 计算, ∗ ∗ 节省 15/1M tokens 计算,**节省 </math>15/1Mtokens计算,∗∗节省1.5**(单次并发)
这说明团队已经从"功能实现"进化到"成本结构设计"。
3. 扩展体系不是后补的
技能、插件、MCP 不是零散外挂,而是从一开始就被纳入核心架构:
扩展接入统一接口:
arduino
// 所有扩展都通过统一的注册中心接入exportinterfaceExtension{// 命令扩展 commands?:Command[]
// 工具扩展 tools?:Tool[]
// Hooks 扩展 hooks?:HooksSettings
// Agent 扩展 agents?:AgentDefinition[]
// MCP 扩展 mcpServers?:McpServerConfig[]}
统一装配流程:
ini
// src/setup.ts 核心装配逻辑asyncfunction setup(){// 1. 加载技能const skills =await loadSkillsDir('.claude/skills')
// 2. 加载插件const plugins =await loadPlugins()
// 3. 连接 MCPconst mcpClients =await connectMcpServers(config.mcpServers)
// 4. 统一装配到命令/工具/状态系统const commands =[...builtinCommands,...skills,...plugins.commands]const tools = assembleToolPool(permissionContext, mcpClients.tools)const hooks =[...builtinHooks,...plugins.hooks]
// 5. 更新全局状态 updateAppState({ commands, tools, hooks, mcpClients })}
这使它具备平台化潜力------任何扩展都遵循相同的生命周期和权限边界。
产品层面的启示
1. AI 产品最终会回到"操作系统式产品"
用户真正需要的不是一个回答问题的模型,而是一个能:
- 接收任务 - 理解意图,分解目标
- 调用工具 - 执行操作,访问外部系统
- 记住上下文 - 维护会话状态,累积知识
- 并行处理 - 多任务并发,提高效率
- 接入外部系统 - MCP 协议,插件生态
的工作操作台。
Claude Code 展示的"操作系统式"特征:
sql
┌─────────────────────────────────────────┐│ClaudeCodeRuntime│├─────────────────────────────────────────┤│┌─────────┐┌─────────┐┌─────────┐│││ REPL UI ││Command││Tool││││Layer││System││System│││└─────────┘└─────────┘└─────────┘││┌─────────┐┌─────────┐┌─────────┐│││State││Agent││Extended││││Management││Runtime││Platform│││└─────────┘└─────────┘└─────────┘││┌────────────────────────────────────┐│││InfrastructureLayer││││Auth/Telemetry/ MCP /Policy│││└────────────────────────────────────┘│└─────────────────────────────────────────┘↕ MCP Protocol↕┌─────────────────────┐│ExternalWorld││APIs/Files/Git│└─────────────────────┘
2. 长会话是第一性问题
只要产品允许连续协作,就一定会遇到:
| 问题 | Claude Code 的解决方案 | 设计思想 |
|---|---|---|
| 上下文膨胀 | 智能压缩算法 | 保留关键信息,丢弃冗余 |
| 成本膨胀 | Prompt Cache + 工具排序稳定 | 架构级成本控制 |
| 历史污染 | 消息标准化 + Tombstone | 可恢复的错误处理 |
| 子任务分流 | Fork Subagent + CacheSafeParams | 低成本并行 |
| 状态一致性 | 单一 AppState + Immer updates | 可预测的状态管理 |
代码示例 - 长会话治理:
ruby
// src/query.ts 长会话处理伪代码asyncfunction manageLongConversation(){// 1. 检查上下文长度if(getTokenCount(messages)> MAX_TOKENS){// 2. 智能压缩 messages =await compact({ messages, strategy:'preserve-important',// 保留关键信息 cacheSafePrefix: getCacheSafeParams()// 维护缓存稳定})}
// 3. 检查子任务需求if(needsParallelExecution(tasks)){// 4. 低成本 forkconst children =awaitPromise.all( tasks.map(task => forkSubagent({ directive: task, cacheSafePrefix: buildForkedMessages()})))}
// 5. 状态恢复if(needsRecovery(state)){await recoverFromCheckpoint(state)}}
3. 用户体验不只是 UI 漂亮
它的体验来自深层的工程优化:
启动性能优化:
ruby
// 顶层并行预取(在 import 之前)startMdmRawRead()// ~40ms 并行startKeychainPrefetch()// ~65ms 并行
// Fast path 零模块加载if(args[0]==='--version'){ console.log(MACRO.VERSION)// MACRO 内联return// 直接退出}
响应流畅性优化:
python
// 流式工具执行forawait(const progress of runTool(toolUse)){// 立即返回进度yield{ type:'progress', data: progress }}
// 并发安全工具同时执行const results =awaitPromise.all([ readFile('a.ts'), readFile('b.ts'), grepPattern('pattern')])
可恢复性设计:
vbnet
// 会话持久化await writeTranscript(messages, sessionId)
// 崩溃恢复const lastState =await readTranscript(sessionId)if(lastState.incomplete){ promptUser('Resume from last session?')}
这些比"回答更像人"更重要------可靠性 > 智能。
成功要素
1. 明确的单一事实来源 (Single Source of Truth)
每个核心能力都有唯一的注册中心:
ruby
// 命令注册中心 - src/commands.tsexportfunction getCommands():Command[]{return[...builtinCommands,...skillCommands,...pluginCommands]}
// 工具注册中心 - src/tools.tsexportfunction assembleToolPool(permissionContext, mcpTools):Tools{const builtInTools = getTools(permissionContext)return uniqBy([...builtInTools,...mcpTools].sort(byName),'name')}
// 查询引擎 - src/query.tsexportasyncfunction* query({ messages, tools, systemPrompt }){// 唯一的主循环入口}
// 状态管理 - src/state/AppState.tsxexport type AppState={ messages:Message[] toolPermissionContext:ToolPermissionContext mcp:{ clients:MCPServerConnection[]}// ... 全局状态}
优势:
- 复杂但不混乱
- 易于追踪和调试
- 便于扩展和维护
2. 清晰的层间边界 (Layer Boundaries)
每层只关心自己的职责:
ruby
// 入口层 - 只关心分流// src/entrypoints/cli.tsxif(args[0]==='--version')return printVersion()if(args[0]==='--daemon-worker')return runDaemonWorker()// 分流后不干预后续逻辑
// 查询层 - 只关心主循环// src/query.tsasyncfunction* query(){while(true){const messages =await normalizeInput()const response =await callModel()const results =await executeTools()yield* results// 不关心具体工具实现}}
// 工具层 - 只关心能力执行// src/services/tools/toolOrchestration.tsasyncfunction runTools(toolUseBlocks){const batches = partitionToolCalls(toolUseBlocks)for(const batch of batches){if(batch.isConcurrencySafe){await runConcurrently(batch)}else{await runSerially(batch)}}// 不关心业务语义}
// 扩展层 - 只关心能力装配// src/tools.ts + src/commands.tsexportfunction assembleToolPool(){// 过滤、排序、去重// 不关心工具如何执行}
设计原则:
- 单向依赖,避免循环
- 接口清晰,职责单一
- 易于测试和替换
3. 以成本和性能为导向的架构细节
启动性能工程化:
ruby
// 阶段 1: 并行预取(import 之前)startMdmRawRead()// ~40ms 并行startKeychainPrefetch()// ~65ms 并行// 收益:启动时间从 ~200ms 降到 ~100ms
// 阶段 2: Fast Path(零模块加载)if(args[0]==='--version'){ console.log(MACRO.VERSION)// 构建时内联return}// 收益:version 命令 <5ms
// 阶段 3: 延迟加载const heavy =awaitimport('heavy-module')// 收益:避免不必要的模块加载
Prompt Cache 稳定性:
ini
// 策略 1: 工具排序稳定const tools =[...builtInTools].sort(byName).concat([...mcpTools].sort(byName))
// 策略 2: Fork Child 字节级相同const FORK_PLACEHOLDER ='Fork started --- processing in background'
// 策略 3: CacheSafeParams 显式建模export type CacheSafeParams={ systemPrompt:SystemPrompt userContext:UserContext systemContext:SystemContext}
并发调度优化:
lua
// 工具分级调度function partitionToolCalls(toolUseBlocks){return toolUseBlocks.reduce((batches, block)=>{const isSafe = tool.isConcurrencySafe(input)
// 相邻的并发安全工具合并到一个 batchif(isSafe &&last(batches).isConcurrencySafe){last(batches).blocks.push(block)}else{ batches.push({ isConcurrencySafe: isSafe, blocks:[block]})}
return batches},[])}
4. 可持续增长的扩展架构
三级扩展体系:

扩展能力对比:
| 扩展类型 | 适用场景 | 开发成本 | 分发方式 | 示例 |
|---|---|---|---|---|
| 技能 | 个人模板、快速复用 | 极低(写 Markdown) | 目录共享 | commit/push/review |
| 插件 | 产品化能力、团队共享 | 中等(需配置) | Marketplace | chrome-integration |
| MCP | 外部系统接入 | 高(需服务端) | 协议标准 | database-api、browser-control |
对前端工程师最值得迁移的能力模型
1. 系统思维升级
从"页面集合"到"运行时系统" :
css
传统前端思维:┌─────────────┐│Page A │→独立状态├─────────────┤│Page B │→独立状态├─────────────┤│Page C │→独立状态└─────────────┘
ClaudeCode思维:┌─────────────────────────────┐│UnifiedRuntime││┌───────┐┌───────┐│││ REPL ││Query││││ UI ││Loop│││└───────┘└───────┘││┌───────────────────────┐│││StateManagement││││(AppState)│││└───────────────────────┘││┌───────────────────────┐│││CapabilityRegistry││││Commands/Tools/ MCP│││└───────────────────────┘│└─────────────────────────────┘
迁移路径:
lua
// 阶段 1: 统一状态管理const appState ={ user:UserState, data:DataState, ui:UIState}
// 阶段 2: 能力注册中心const capabilities ={ commands:newMap<string,Command>(), tools:newMap<string,Tool>(), extensions:newMap<string,Extension>()}
// 阶段 3: 运行时循环function* runtimeLoop(){while(true){const input =yield'ready'const result =await processInput(input) updateState(result)}}
2. 状态管理升级
从"组件局部变量"到"领域模型" :
css
// 传统做法:状态分散在组件functionComponent(){const[data, setData]= useState()const[loading, setLoading]= useState()const[error, setError]= useState()}
// Claude Code 做法:统一领域状态type AppState={// 会话状态 conversation:{ messages:Message[] turnCount: number lastActivity: number}
// 工具状态 tools:{ inProgressToolUseIDs:Set<string> lastToolResults:Map<string,ToolResult>}
// 扩展状态 extensions:{ mcp:{ clients:MCPServerConnection[]} plugins:Map<string,Plugin> skills:Map<string,Skill>}
// 权限状态 permissions:{ mode:PermissionMode context:ToolPermissionContext}}
// 统一更新机制function updateAppState(updater:(prev:AppState)=>AppState){const newState = immer.produce(currentState, updater) notifySubscribers(newState)}
3. 能力治理升级
从"散落的能力"到"统一注册中心" :
lua
// 传统做法:能力散落在代码各处asyncfunction handleAction(type:string){if(type ==='save'){/* ... */}elseif(type ==='load'){/* ... */}elseif(type ==='export'){/* ... */}}
// Claude Code 做法:统一注册classCapabilityRegistry{private commands =newMap<string,Command>()private tools =newMap<string,Tool>()
register(command:Command){this.validate(command)this.commands.set(command.name, command)}
get(name:string):Command|undefined{returnthis.commands.get(name)}
// 统一过滤、权限检查 filter(predicate:(cmd:Command)=>boolean):Command[]{returnArray.from(this.commands.values()).filter(predicate)}}
4. 长生命周期治理
从"短期会话"到"可持续运行" :
vbnet
// 短期思维:页面关闭 = 状态丢失functionPage(){ useEffect(()=>{return()=>{// 清理}},[])}
// 长期思维:会话可能持续数天classLongRunningSession{// 1. 持久化async persist(){await writeTranscript(this.messages)await writeCheckpoint(this.state)}
// 2. 恢复async restore(sessionId:string){const transcript =await readTranscript(sessionId)const checkpoint =await readCheckpoint(sessionId)this.rebuildState(transcript, checkpoint)}
// 3. 压缩async compact(){// 保留关键信息,丢弃冗余this.messages =await intelligentCompact(this.messages)}
// 4. 快照async snapshot(){// 定期快照,便于回溯await writeSnapshot({ timestamp:Date.now(), state:this.state, messages:this.messages})}}
5. 缓存稳定性思维
从"功能优先"到"成本结构优先" :
css
// 传统思维:功能实现了就行const tools =[...tools1,...tools2]
// Claude Code 思维:缓存稳定性优先const tools = assembleToolPool({ builtIn: getBuiltInTools(), mcp: getMcpTools(),// 排序保证缓存命中 sortStrategy:'stable-prefix',// 去重保证一致性 dedupStrategy:'built-in-first'})
实际收益:
| 优化点 | 传统做法 | Claude Code 做法 | 收益 |
|---|---|---|---|
| 工具排序 | 随意顺序 | 固定前缀排序 | 缓存命中率 +40% |
| Fork Child | 完整复制 | 字节级相同前缀 | 并行成本 -60% |
| 上下文压缩 | 简单截断 | 智能保留 | Token 成本 -50% |
| 消息标准化 | 原样保留 | 规范化 + Tombstone | 恢复可靠性 +100% |
如果我要仿照这个项目做一个自己的 AI Coding Agent,最小可行架构应该怎么设计
最小版本(MVP)- 核心循环
目标:实现最基本的 AI Agent 循环
文件结构:
bash
src/├── index.ts # 入口├── query.ts # 主循环├── tools.ts # 工具注册├── commands.ts # 命令注册├── tools/│├──ReadTool.ts # 文件读取│├──EditTool.ts # 文件编辑│├──BashTool.ts # 命令执行└── state.ts # 状态管理
核心代码:
vbnet
// src/query.ts - 主循环asyncfunction* query(messages:Message[], tools:Tool[]){while(true){// 1. 调用模型const response =await callModel({ messages, tools })
// 2. 解析工具调用const toolCalls = parseToolCalls(response)
// 3. 执行工具for(const toolCall of toolCalls){const tool = tools.find(t => t.name === toolCall.name)const result =await tool.call(toolCall.input)
// 4. 返回结果yield{ type:'tool_result', content: result }
// 5. 更新消息 messages.push({ role:'assistant', content: response}) messages.push({ role:'user', content:[{ type:'tool_result',...result }]})}
// 6. 检查是否结束if(shouldEnd(response))break}}
工具定义:
css
// src/tools.tsexportinterfaceTool{ name:string description:string inputSchema:ZodSchema call(input: any):Promise<string>}
// src/tools/ReadTool.tsexportconstReadTool:Tool={ name:'Read', description:'Read file contents', inputSchema: z.object({ file_path: z.string()}),async call({ file_path }){returnawait fs.readFile(file_path,'utf-8')}}
MVP 特性:
- ✅ 最基本的主循环
- ✅ 3-5 个核心工具
- ✅ 简单的状态管理
- ❌ 无权限系统
- ❌ 无压缩恢复
- ❌ 无扩展机制
第二阶段 - 用户体验层
新增功能:
bash
src/├── repl.ts # REPL 界面├── auth.ts # 权限系统├── compact.ts # 上下文压缩├── storage.ts # 持久化存储└── tools/├──GlobTool.ts # 文件搜索├──GrepTool.ts # 内容搜索└──WriteTool.ts # 文件写入
权限系统:
python
// src/auth.tsexportasyncfunction checkPermission( tool:string, input: any, context:PermissionContext):Promise<'allow'|'deny'|'ask'>{// 1. 检查规则const rule = matchRule(context.rules, tool, input)if(rule)return rule.behavior
// 2. 默认询问return'ask'}
// 在工具执行前检查asyncfunction runTool(tool:Tool, input: any){const decision =await checkPermission(tool.name, input, context)
if(decision ==='allow'){returnawait tool.call(input)}elseif(decision ==='deny'){thrownewError('Permission denied')}else{// 弹出对话框询问const userChoice =await askUser(tool.name, input)return userChoice ==='allow'?await tool.call(input):thrownewError('User denied')}}
上下文压缩:
ini
// src/compact.tsexportasyncfunction compact(messages:Message[]):Promise<Message[]>{// 1. 计算当前 token 数const tokens = countTokens(messages)
if(tokens < MAX_TOKENS)return messages
// 2. 简单压缩:保留系统消息 + 最近 N 条const systemMessages = messages.filter(m => m.role ==='system')const recentMessages = messages.slice(-20)
// 3. 添加压缩摘要const summary =await generateSummary(messages)
return[...systemMessages,{ role:'system', content:`Previous context: ${summary}`},...recentMessages]}
会话持久化:
javascript
// src/storage.tsexportasyncfunction saveSession(messages:Message[], sessionId:string){const transcript = JSON.stringify(messages)await fs.writeFile(`.sessions/${sessionId}.json`, transcript)}
exportasyncfunction loadSession(sessionId:string):Promise<Message[]>{const transcript =await fs.readFile(`.sessions/${sessionId}.json`,'utf-8')return JSON.parse(transcript)}
阶段 2 特性:
- ✅ REPL 或 Web UI
- ✅ 权限系统
- ✅ 上下文压缩
- ✅ 会话持久化
- ✅ 更多工具
- ❌ 无子 agent
- ❌ 无扩展机制
第三阶段 - 企业级特性
新增架构:
bash
src/├── agent/│├── runAgent.ts # 子 agent 执行│└── forkSubagent.ts # Fork 机制├── cache/│└── cacheSafeParams.ts # 缓存参数├── plugins/│├── loader.ts # 插件加载│└── registry.ts # 插件注册├── mcp/│├── client.ts # MCP 客户端│└── protocol.ts # MCP 协议└── build/└── featureFlags.ts # Feature flags
子 Agent 机制:
css
// src/agent/runAgent.tsexportasyncfunction* runAgent( prompt:string, context:AgentContext):AsyncGenerator<Message>{// 1. 创建隔离上下文const agentContext = createSubagentContext(context)
// 2. 继承缓存安全参数const cacheSafeParams = getCacheSafeParams(context)
// 3. 执行独立查询循环forawait(const message of query({ messages:[{ role:'user', content: prompt }], tools: filterTools(context.tools), systemPrompt: agentContext.systemPrompt})){yield message}}
// src/agent/forkSubagent.tsexportfunction buildForkedMessages( directive:string, assistantMessage:Message):Message[]{// 构造字节级相同的前缀const placeholder ='Fork started --- processing in background'
const toolResults = assistantMessage.content.filter(block => block.type ==='tool_use').map(block =>({ type:'tool_result', tool_use_id: block.id, content: placeholder}))
return[ assistantMessage,{ role:'user', content:[...toolResults,{ type:'text', text: directive }]}]}
插件系统:
vbnet
// src/plugins/loader.tsexportasyncfunction loadPlugin(pluginPath:string):Promise<Plugin>{// 1. 读取 plugin.jsonconst manifest =await readJson(join(pluginPath,'plugin.json'))
// 2. 加载命令const commands =await loadCommands(join(pluginPath,'commands'))
// 3. 加载工具const tools =await loadTools(join(pluginPath,'tools'))
// 4. 加载 Hooksconst hooks =await loadHooks(join(pluginPath,'hooks'))
return{ manifest, commands, tools, hooks }}
// src/plugins/registry.tsclassPluginRegistry{private plugins =newMap<string,Plugin>()
register(plugin:Plugin){// 注册到命令系统for(const cmd of plugin.commands){ commandRegistry.register(cmd)}
// 注册到工具系统for(const tool of plugin.tools){ toolRegistry.register(tool)}
// 注册 Hooks hookRegistry.register(plugin.hooks)
this.plugins.set(plugin.manifest.name, plugin)}}
MCP 协议支持:
vbnet
// src/mcp/client.tsexportasyncfunction connectMcpServer( config:McpServerConfig):Promise<McpClient>{// 1. 建立传输层const transport = createTransport(config)
// 2. 初始化连接await transport.initialize()
// 3. 拉取能力列表const tools =await transport.listTools()const resources =await transport.listResources()
return{ transport, tools: tools.map(t =>newMCPTool(t, transport)), resources}}
// src/mcp/MCPTool.tsclassMCPToolimplementsTool{ constructor(private definition:McpToolDefinition,private transport:Transport){}
async call(input: any):Promise<string>{const result =awaitthis.transport.callTool(this.definition.name, input)
return result.content}}
Feature Flags:
ruby
// src/build/featureFlags.ts// 构建时替换exportfunction feature(name:string):boolean{// 在构建时被替换为 true/falsereturnfalse}
// 使用示例if(feature('ENABLE_AGENT')){const agent =awaitimport('./agent/runAgent')// 整个模块在未启用时被裁剪}
阶段 3 特性:
- ✅ 子 agent 并行协作
- ✅ Cache-safe context 复用
- ✅ 插件 / 技能扩展
- ✅ MCP 协议接入
- ✅ Build-time 裁剪
- ✅ 完整的工程化体系
架构演进路径
less
graph LR A[MVP<br/>核心循环]--> B[阶段2<br/>用户体验] B --> C[阶段3<br/>企业级]
A --> A1[✅基础工具<br/>✅简单状态<br/>❌无权限]
B --> B1[✅ REPL UI<br/>✅权限系统<br/>✅压缩恢复]
C --> C1[✅子Agent<br/>✅扩展机制<br/>✅工程化]
style A fill:#c8e6c9 style B fill:#fff9c4 style C fill:#e1f5ff
对这个项目的最终判断
从前端工程师的学习角度,这个项目最值得借鉴的不是"终端 UI 怎么写",而是它展示了一个先进的产品事实:
AI Agent 产品的核心竞争力,不在单个 prompt,而在整套工程体系:
核心能力清单

能力分层总结
| 层级 | 核心职责 | 关键模块 | 学习价值 |
|---|---|---|---|
| 入口层 | 模式分流 | cli.tsx / main.tsx | Fast path 设计、启动优化 |
| 查询层 | 主循环编排 | query.ts | Agent runtime 设计 |
| 工具层 | 能力执行 | toolOrchestration.ts | 并发调度、权限控制 |
| Agent 层 | 并行协作 | runAgent.ts / forkSubagent.ts | 低成本并行、缓存共享 |
| 扩展层 | 能力装配 | tools.ts / commands.ts | 插件架构、注册中心 |
| 基础设施层 | 工程化支撑 | init.ts / growthbook.ts | 性能优化、成本控制 |
从"功能实现"到"工程体系"的进化
传统 AI 产品思维:
用户输入→Prompt→模型响应→显示结果
Claude Code 的工程体系思维:
arduino
┌────────────────────────────────────────────┐│产品设计决策层││-多模式支持:CLI / REPL /Bridge/ BG ││-扩展策略:技能/插件/ MCP 三级体系││-成本控制:PromptCache+并发调度│└────────────────────────────────────────────┘↓┌────────────────────────────────────────────┐│架构设计层││-单一事实来源:commands / tools / state ││-清晰边界:入口/查询/工具/扩展││-统一接口:Command/Tool/Extension│└────────────────────────────────────────────┘↓┌────────────────────────────────────────────┐│工程实现层││-启动优化:并行预取/FastPath││-运行时优化:流式执行/并发调度││-成本优化:Cache稳定/上下文压缩│└────────────────────────────────────────────┘↓┌────────────────────────────────────────────┐│可观测性层││-遥测系统:Telemetry/Analytics││-性能监控:StartupProfiler││-成本监控:Token计数/Cache命中率│└────────────────────────────────────────────┘
关键启示
1. 产品思维
- AI Agent 是"操作系统式产品",不是"对话式问答"
- 长会话是第一性问题,需要架构级解决方案
- 用户体验来自可靠性,而不仅仅是智能程度
2. 架构思维
- 单一事实来源是复杂度管理的关键
- 清晰的层间边界是可维护性的基础
- 扩展性要前置设计,不能后补
3. 工程思维
- 成本结构设计比功能实现更重要
- 启动性能和运行时性能都需要系统性优化
- Build-time 和 Runtime 开关要分层设计
4. 产品化思维
- 三级扩展体系(技能/插件/MCP)满足不同场景
- 权限系统是产品化的基础设施
- 可观测性是企业级产品的必备能力
最终评价
Claude Code 是一个教科书级的 AI Agent 工程实践,它展示了:
✅ 完整的 Agent Runtime - 从输入到输出的完整生命周期管理
✅ 企业级的工程化 - 性能、成本、可观测性的系统性设计
✅ 平台化的扩展性 - 三级扩展体系 + 统一注册中心
✅ 产品级的用户体验 - 快速启动、可恢复、可并行、有边界
对前端工程师的价值:
- 学会"运行时思维",而不仅是"页面思维"
- 学会"成本结构设计",而不仅是"功能实现"
- 学会"平台化架构",而不仅是"单点应用"
这也是它能成为优秀企业级 Agent / CLI 产品的真正原因------不是模型更强,而是工程体系更完整。
声明
- 源码版权:归 Anthropic 所有
- 研究用途:本分析仅供技术研究与学习使用
- 非官方:本文档非 Anthropic 官方文档
- 删除请求:如有侵权,请联系删除
泄露事件时间线:
- 2026-03-31 - Chaofan Shou (@Fried_rice) 在 X (Twitter) 公开发现
- 2026-03-31 - GitHub 社区恢复版发布