前言

这篇文章试图回答一个更具体的问题:

Claude Code 到底是怎么工作的?

如果只从产品表面看,Claude Code 很容易被理解成“一个会读代码、会跑命令的 Claude”。但把源码摊开后你会发现,它其实不是一个“纯聊天壳”,而是一套很完整的 agent runtime

  • 有自己的 CLI 入口和启动编排;
  • 有独立的上下文装配层;
  • 有一整套工具注册、权限判定、并发执行与回填机制;
  • 有子代理、后台任务、worktree 隔离;
  • 有 MCP、plugin、skills、bridge、remote session 等扩展层;
  • 有自动 compact、session memory、CLAUDE.md / MEMORY.md 这类长期上下文机制;
  • 最外面再包一层 React + Ink 的终端 UI。

为了更容易建立稳定的心智模型,本文会一边沿着源码主链路展开,一边拿 Codex 做对照。对照视角主要参考两类材料:

如果要先用一句话概括,我会这样总结:

Claude Code 本质上是一个用 TypeScript/Bun 写成的本地 agent 宿主。模型负责决定“下一步做什么”,而 Claude Code 负责决定“这些动作在本地如何被授权、如何执行、如何并发、如何压缩上下文、如何接入外部系统,以及如何在终端里被展示出来”。

先说可信边界:这份仓库到底是什么

先把一个很重要的前提讲清楚。

这次分析的对象不是 Anthropic 官方公开维护的完整开发仓库,而是一个源码快照。根目录的 README.md 明确说了两点:

  • 这是一个 Claude Code Source Snapshot for Security Research
  • 来源是一次公开的 source map 暴露,时间点是 2026-03-31

这意味着这份代码非常有研究价值,但也要注意三层边界:

  1. 它不是官方长期维护仓库

所以你看到的是某个时间点的实现切片,而不是完整演进历史。

  1. 它可能包含内部 feature flag 分支

源码里大量使用 feature('...') 做 dead code elimination,说明同一份代码会被编译成不同产品形态。你看到的不一定全部对外开放。

  1. 它足够解释架构,但不代表产品全部行为都能直接从快照推出

例如某些后端服务、GrowthBook 灰度、桥接基础设施、远端运行环境,源码能看见入口和协议,但未必看得到服务端完整实现。

所以,最稳妥的阅读姿势是:

  • 把它当成 Claude Code 宿主程序的高保真实现快照
  • 结合官方文档验证对外稳定能力,例如 How Claude Code worksContext windowSettingsMemory
  • 再把源码里只出现于 flag 后面的部分,视作“架构方向”和“产品内部能力痕迹”。

仓库全景:它不是一个小工具,而是一套 agent 平台

这份快照虽然只有一个 src/,但结构很完整。按目录数量看,几个最大的模块是:

目录 文件数 直觉角色
utils/ 564 公共运行时、状态辅助、权限、上下文、日志、模型辅助
components/ 389 TUI 组件层
commands/ 207 /command 命令体系
tools/ 184 模型可调用的工具层
services/ 130 API、MCP、compact、analytics 等服务层
hooks/ 104 React hooks + 权限/交互逻辑
ink/ 96 Ink 适配与终端 UI 基础设施
bridge/ 31 远程桥接、session、remote control
skills/ 20 技能系统
cli/ 19 CLI 子命令与命令行处理
tasks/ 12 后台任务/子代理任务系统

如果把这些目录抽象成一张架构图,我会画成这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
User / Terminal
      |
      v
React + Ink UI
      |
      v
main.tsx -------------- commands/
  |   \
  |    \---- AppState / settings / plugins / skills / MCP / bridge init
  v
QueryEngine
  |
  v
query.ts  <---- context.ts + queryContext.ts + systemPrompt.ts
  |
  +---- canUseTool / permissions
  |
  +---- StreamingToolExecutor / toolOrchestration
  |          |
  |          +---- Bash / Read / Edit / Write / Grep / Glob / Web / Todo
  |          +---- MCP tools
  |          +---- Agent tool / Skill tool / Task tools
  |
  +---- compact / memory / collapse / token budgeting
  |
  v
Anthropic API (claude.ts)

Side systems:
- bridge/          远程会话与 remote control
- services/mcp/    外部服务器、资源、认证
- tasks/           本地/远程 agent 任务与面板
- skills/          forked sub-agent skill execution

这个结构和 Codex 很像的一点是:模型不是系统本身,模型只是系统中的决策器

不同的是,Claude Code 比开源 Codex CLI 更“产品化”:

  • UI 更重;
  • feature flag 更多;
  • bridge / remote / assistant / kairos / companion 等产品特性痕迹更明显;
  • MCP、plugin、skills、subagent 都是统一 runtime 的一部分,而不是几个松散外挂。

一条主链路:从 main.tsx 到 agent loop

Claude Code 的真实主入口在 src/main.tsx

这个文件很大,也很“重”,说明它不是一个薄薄的参数解析层,而是整个宿主程序的启动协调器。这里面能看到几类非常重要的事情:

  • 提前做一批启动副作用,例如 profiler、keychain、managed settings;
  • 初始化 analytics、settings、plugins、skills、MCP、bridge、LSP;
  • 解析命令行参数;
  • 决定当前是正常 TUI、print 模式、SDK 模式、remote 模式还是别的运行形态;
  • 组装 QueryEngine 所需的运行时能力。

你如果把它和 Codex 的启动入口对比,会发现两者都有类似的“巨型引导层”,只是风格不同:

  • Codex 更像 Rust 的 harness/runtime 分层;
  • Claude Code 更像一个 单体 TypeScript runtime,所有入口能力最后都汇总进一个共享的 agent loop。

Claude Code 主链路里最关键的两个对象是:

  • QueryEngine
  • query.ts

QueryEngine:会话级控制器

src/QueryEngine.ts 可以看成“对话会话的总调度器”。

它持有的配置非常多,包括:

  • 当前工作目录;
  • 工具列表、命令列表、MCP client、agent definitions;
  • canUseTool 权限判定函数;
  • getAppState / setAppState
  • 文件读取状态;
  • 自定义 system prompt / append system prompt;
  • model / fallback model;
  • thinking config;
  • 最大轮数、预算、structured output schema;
  • 是否回放用户消息、是否包含 partial messages。

这说明 QueryEngine 的角色不是“发一个 API 请求”,而是:

维护一场会话级 agent 运行所需要的全部宿主依赖,并在每一轮用户输入时启动一次受控的 query loop。

query.ts:真正的 agent loop

src/query.ts 是 Claude Code 的核心中的核心。

如果你只想找“Claude Code 到底在哪里实现了多轮工具调用闭环”,这个文件就是主战场。

它做的事情大致是:

  1. 组装本轮 messages 和 system prompt;
  2. 把工具 schema 发给模型;
  3. 读取流式响应;
  4. 一边消费增量输出,一边把工具调用交给 StreamingToolExecutor
  5. 收集工具结果回填到消息流;
  6. 持续检查停止条件、轮数、预算、token 使用;
  7. 在必要时触发 compact / context collapse / reactive compact;
  8. 最终产出本轮 assistant 消息和更新后的状态。

这套设计和 Codex 的 agent loop 非常接近,但实现风格不一样:

  • Codex 的感觉更像“显式状态机 + runtime harness”;
  • Claude Code 更像“消息流 + 工具流 + UI 状态流三者同时推进”。

也正因为这样,Claude Code 源码里会明显出现“streaming tool execution”“partial messages”“in-progress tool IDs”“message appenders”“notification queue”这一整套流式系统词汇。

Claude Code 的上下文管理,核心不是“存 messages”,而是“分层装配上下文”

如果问我 Claude Code 最值得研究的部分是什么,我会选 上下文装配层

因为它不是把“历史消息原封不动发给模型”,而是显式地区分了几类上下文来源:

  • 默认 system prompt;
  • 自定义/追加 system prompt;
  • 用户工作区上下文;
  • Git 上下文;
  • 记忆系统;
  • 运行时工具和权限上下文;
  • 压缩后的会话历史;
  • 任务/子代理带回来的输出。

这部分的关键文件主要有:

1
2
3
4
5
src/context.ts
src/utils/queryContext.ts
src/utils/systemPrompt.ts
src/services/compact/*
src/memdir/memdir.ts

1. context.ts:把“环境”变成上下文

src/context.ts 里有两个很关键的函数:

  • getSystemContext()
  • getUserContext()

它们分别负责把两类环境信息注入给模型。

getSystemContext() 做的是偏“宿主视角”的上下文注入,例如:

  • 当前 Git 分支;
  • main branch;
  • git status
  • 最近 commit;
  • git user。

也就是说,Claude Code 会在会话开始时主动告诉模型:你当前不是在一个抽象目录里,而是在一个真实 git 工作区里。

getUserContext() 则更接近“用户意图层”的附加信息,里面最关键的是:

  • CLAUDE.md
  • 当前日期

这和官方文档是对上的。Anthropic 官方文档明确把 CLAUDE.md 视作 Claude Code 的用户级持久上下文载体,而源码则进一步告诉你:它不是单独的 feature,而是 query context 构造的一部分。

2. systemPrompt.ts:system prompt 不是一段字符串,而是一套优先级规则

src/utils/systemPrompt.ts 很值得读,因为它把系统提示词的拼装优先级写得非常清楚。

它处理的优先级大致是:

  1. overrideSystemPrompt 直接覆盖一切;
  2. coordinator mode 使用 coordinator prompt;
  3. 主线程 agent definition 如果存在,则使用 agent prompt;
  4. 否则使用 custom system prompt;
  5. 最后回落到 default system prompt;
  6. appendSystemPrompt 始终可以附加在末尾。

也就是说,Claude Code 的 system prompt 不是一个静态模板,而是一个 按运行模式重写的 prompt stack

这点和 Codex 很像。Codex 里也不是“只有一段 meta prompt”,而是有 platform/system/developer/user 多层;Claude Code 这里虽然最终对接的是 Anthropic 消息接口,但宿主层同样在做 prompt hierarchy。

3. queryContext.ts:会话拼装器

src/utils/queryContext.ts 的意义是把各种 prompt part 统一拉平。

它负责:

  • 获取 default system prompt;
  • 拉 user context;
  • 拉 system context;
  • 给 side question / print / SDK 等场景复用同一套装配逻辑。

这说明 Claude Code 的设计里有一个很清晰的工程观:

不同入口可以不同,但真正送给模型的上下文装配逻辑最好只有一套。

这点非常重要,因为一旦 system/user context 的注入路径散落在不同入口,产品行为就会变得不一致。

真正的“长期记忆”:CLAUDE.md + MEMORY.md + compact

很多人第一次用 Claude Code,会感觉它“好像记得我项目里的一些约定”。这背后其实不是神秘能力,而是三套机制叠加:

  1. CLAUDE.md
  2. MEMORY.md
  3. compact 之后的会话摘要 / 保留信息

CLAUDE.md:项目级持久规则

这是最直接的一层。用户把约定写进仓库里的 CLAUDE.md,Claude Code 在构造用户上下文时把它读进来。

它更像 Codex 里的 AGENTS.md / 开发者指令那一类东西:

  • 让 agent 按项目规范工作;
  • 在每轮对话前都能重新看到;
  • 不依赖模型“记住”。

MEMORY.md:显式的 session / workspace memory 入口

src/memdir/memdir.ts 非常有意思,因为它把记忆机制做成了文件系统协议。

其中几个关键信号是:

  • memory entrypoint 明确叫 MEMORY.md
  • 有尺寸和行数上限;
  • 有 index/truncation 逻辑;
  • 鼓励把记忆拆成独立文件,然后用 MEMORY.md 做入口索引。

这意味着 Claude Code 的 memory 不是“偷偷把东西藏在数据库里”,而是尽量把记忆外显为可管理文件结构。这和官方文档对 memory 的定位是吻合的,也非常工程化。

compact:不是可选优化,而是会话能跑长任务的关键

src/services/compact/compact.tssrc/services/compact/autoCompact.ts 说明了一件事:

Claude Code 从一开始就把“上下文迟早会爆”当成既定事实,所以它实现的是完整 compact pipeline,而不是一个临时裁剪函数。

从源码能看到的 compact 关键点包括:

  • auto-compact 阈值计算;
  • 根据模型上下文窗口决定触发条件;
  • 某些模式下抑制 auto-compact;
  • pre-compact / post-compact hooks;
  • 图像与附件裁剪;
  • compact 失败重试;
  • compact 后对文件、skills 等上下文的恢复预算;
  • 对连续 compact 失败的 circuit breaker。

这一点和 Codex 几乎是同一个大方向:agent 不是在无限上下文里工作,而是在有限上下文里做“保真压缩”

但 Claude Code 的实现明显更产品化,因为它需要同时处理:

  • 富文本消息;
  • 文件附件;
  • MCP 输出;
  • 子代理结果;
  • UI 状态;
  • 背景任务通知。

ToolUseContext 是 Claude Code 的宿主 ABI

如果要在 Claude Code 里找一个“最能代表 agent runtime 接口”的类型,我会选 src/Tool.ts 里的 ToolUseContext

原因很简单:这个类型决定了工具在运行时到底能拿到什么。

从源码看,ToolUseContext 里已经不是简单的“cwd + abortSignal”了,而是一整套宿主能力:

  • commands
  • tools
  • mcpClients
  • agentDefinitions
  • getAppState / setAppState
  • handleElicitation
  • messages
  • readFileState
  • addNotification
  • 文件历史
  • attribution
  • stream mode
  • conversation / agent / session 相关信息

换句话说,Claude Code 的工具不是独立函数,而是运行在一个功能非常丰富的 host context 里。

如果拿 Codex 做类比:

  • Codex 更像“runtime 为 tool handler 提供执行上下文”;
  • Claude Code 则把这个上下文抽成了明确的 TypeScript runtime contract。

工具系统:不是“工具列表”,而是“注册表 + 编排器 + 流式执行器”

Claude Code 的工具系统至少要拆成三层看:

  1. 工具注册表;
  2. 工具权限判定;
  3. 工具执行编排。

1. tools.ts:工具注册表

src/tools.ts 是工具体系的总入口。

getAllBaseTools() 基本就是 Claude Code 的“官方工具目录”。这里能看到的工具大类包括:

  • AgentTool
  • BashTool
  • FileReadTool
  • FileEditTool
  • FileWriteTool
  • GlobTool
  • GrepTool
  • WebFetchTool
  • WebSearchTool
  • TodoWriteTool
  • MCP 资源读取/列举工具
  • AskUserQuestionTool
  • SkillTool
  • plan mode / worktree / workflow / sleep / monitor 等工具

这和官方 Tools Reference 也基本一致,只是源码层面还多给你展示了很多实验性或特性开关后的能力。

2. useCanUseTool.tsx:权限不是工具内部做,而是统一前置

Claude Code 的权限体系比很多人想象中更复杂。

src/hooks/useCanUseTool.tsx 这一个文件就能看出它至少同时处理几件事:

  • 配置层的 allow / deny / ask;
  • classifier 自动放行;
  • coordinator / swarm worker 特殊判定;
  • 交互式权限对话框;
  • 中断与取消;
  • permission logging;
  • auto mode denial tracking。

这意味着工具权限不是“调用 Bash 前弹个确认框”那么简单,而是一整套 policy engine + interactive approval flow

也就是说,Claude Code 很像 Codex 一样,把“是否允许模型做这件事”放在宿主层,而不是留给模型自己克制。

3. toolOrchestration.ts + StreamingToolExecutor.ts:工具执行是流式的

这是 Claude Code 很强、也很像现代 agent runtime 的地方。

src/services/tools/toolOrchestration.ts 做的是:

  • 判断哪些工具可以并发;
  • 哪些必须串行;
  • 并发 batch 执行后再应用上下文更新;
  • 管理 in-progress 工具集合。

src/services/tools/StreamingToolExecutor.ts 则更进一步:

  • 工具调用一边从模型流出来,一边就可以开始执行;
  • 维护 queued / executing / completed / yielded 状态;
  • 管 sibling abort controller;
  • 支持用户中断;
  • 在流式失败时合成错误消息回填给模型。

这和传统“先等 assistant 完整输出所有 tool call,再统一执行”的 agent loop 很不一样。

Claude Code 的设计是:

只要模型已经明确发出足够信息,就可以让工具层先跑起来。

这会直接改善两类体验:

  • 长任务延迟更低;
  • 模型可以更快看到工具结果并继续下一轮决策。

Bash / File / Search:Claude Code 最核心的一组三件套

虽然工具很多,但 Claude Code 真正最核心的还是三类:

  • shell 执行;
  • 文件读写;
  • 代码搜索。

BashTool

src/tools/BashTool/BashTool.tsx 是一个典型的“表面简单、内部非常厚”的工具。

它不只是 spawn(command),而是额外做了很多工程处理:

  • 命令分类,区分 search / read / list / edit / destructive 倾向;
  • 权限联动;
  • 后台与前台任务处理;
  • 长输出与 UI 展示;
  • sed/edit 预览;
  • git 操作跟踪;
  • 文件历史跟踪。

这说明 Claude Code 并不是把 shell 当成一个“最后兜底的万能工具”,而是把 shell 当成一个需要 细粒度治理 的高风险工具。

FileReadTool

src/tools/FileReadTool/FileReadTool.ts 也非常能说明问题。

它支持读取的对象已经远超“纯文本文件”:

  • 文本;
  • 图片;
  • notebook;
  • PDF。

同时还有很多防御式逻辑:

  • token 限制;
  • 权限检查;
  • 特殊设备路径屏蔽;
  • 行范围读取;
  • skill 激活联动;
  • 图片/二进制处理。

也就是说,在 Claude Code 眼里,“读文件”本身已经是一个多模态输入管道,而不是简单的 cat 包装。

Grep / Glob / ToolSearch

Claude Code 很重视“先搜索,再读取,再编辑”的行为路径。

除了常规 GrepToolGlobTool,源码里还有一个很有意思的 ToolSearchTool

  • 它会搜索 deferred tools;
  • 支持直接 select:<tool_name>
  • 也支持按关键词从工具描述里检索;
  • 对 MCP 工具名有专门解析逻辑。

这说明 Claude Code 已经默认接受一个现实:

当工具很多,模型不一定总能记住全部工具名,所以宿主最好再给模型一个“找工具的工具”。

子代理不是外挂,而是 Claude Code 的一级能力

Claude Code 架构里另一个非常关键的点,是它把 subagent 做成了一等公民。

这部分最核心的文件是:

1
2
3
4
src/tools/AgentTool/AgentTool.tsx
src/tasks/LocalAgentTask/LocalAgentTask.tsx
src/Task.ts
src/tools/SkillTool/SkillTool.ts

1. Task.ts:任务系统先于具体 agent 存在

src/Task.ts 里能看到一套通用 task 类型:

  • local_bash
  • local_agent
  • remote_agent
  • in_process_teammate
  • local_workflow
  • monitor_mcp
  • dream

这说明 Claude Code 不是“只有一个主 agent,偶尔再开个子线程”,而是从抽象层上就把“系统中有很多任务并发存在”当成前提。

2. AgentTool.tsx:subagent 真正被工具化

AgentTool 的输入参数很丰富:

  • description
  • prompt
  • subagent_type
  • model
  • run_in_background
  • name
  • team_name
  • mode
  • isolation
  • cwd

这意味着 subagent 在 Claude Code 里不是隐藏机制,而是模型可以显式调用的工具,并且可以带上:

  • 类型;
  • 名字;
  • 背景运行属性;
  • worktree 隔离策略;
  • 运行目录。

再结合内置 agent 定义:

  • generalPurposeAgent
  • planAgent
  • verificationAgent
  • exploreAgent
  • claudeCodeGuideAgent

你会发现 Claude Code 已经把“让另一个 agent 去做某类任务”做成了产品语义,而不是 prompt hack。

3. LocalAgentTask.tsx:子代理状态是 UI 里的一级对象

这个文件很有代表性,因为它暴露了 Claude Code 对子代理的真实心智模型:

  • 子代理有独立 task state;
  • 会统计 tool use count 和 token count;
  • 会记录 recent activities;
  • 会支持 foreground / background;
  • 会有 pending messages;
  • 有 transcript 的磁盘侧持久化;
  • 会发送 task notification 给主模型;
  • 可以在面板里被查看。

这和很多“后台帮你调用一下子 agent”的产品很不一样。Claude Code 更像是在运行一个 agent team runtime

4. SkillTool.ts:skill 的本质是 forked agent

这点很重要。

src/tools/SkillTool/SkillTool.ts 清楚地表明:

  • skill 会在 forked sub-agent context 里执行;
  • skill 不是简单的文本模板展开;
  • 它会先准备 forked command context;
  • 再调用 runAgent()
  • 并在需要时把 progress 作为工具进度事件回推。

也就是说,在 Claude Code 里,“skill”更像 预包装的 agent workflow,而不是传统意义上的 prompt snippet。

MCP:Claude Code 的外部能力总线

如果说 shell/file 是 Claude Code 对本地环境的接口,那么 MCP 就是它对外部系统的统一接口。

src/services/mcp/client.ts 是 Claude Code 源码里最值得细读的文件之一。

从这里可以看出几件事:

1. Claude Code 是认真按 MCP 协议做的,不是自定义兼容层

它直接使用官方 MCP SDK 的多种 transport:

  • stdio
  • SSE
  • streamable HTTP
  • WebSocket

说明 MCP 在 Claude Code 里不是附属实验,而是核心扩展机制。

2. MCP 不只是工具,还有资源、prompt、认证和会话管理

这个文件里除了常见的 tool call,还能看到:

  • list resources;
  • read resources;
  • prompts;
  • OAuth token refresh;
  • 401 处理;
  • session expired 处理;
  • output truncation;
  • binary result 持久化。

所以 Claude Code 对 MCP 的理解是非常完整的,它把 MCP 当成一个“外部能力总线”,而不是只当作“再来几个工具”。

3. MCP 和 Claude Code 其他系统是深度耦合的

源码里能看到 MCP 和很多系统直接相连:

  • tool collapse 分类;
  • elicitation;
  • auth;
  • plugin;
  • skills;
  • diagnostics;
  • claude-in-chrome;
  • code indexing。

这说明 MCP 在 Claude Code 里已经不是边缘功能,而是 runtime 的一级组成部分。

Bridge / Remote Session:Claude Code 其实还能“远程存在”

对我来说,这份源码里最有意思的部分之一就是 bridge/

因为它暴露了一个很重要的事实:

Claude Code 不只是一个本地 TUI,它还能通过 bridge 机制,把会话、控制权和任务状态桥接到远端或网页侧。

src/bridge/bridgeMain.ts 里能看到很多强烈的产品信号:

  • session spawn;
  • environment registration;
  • heartbeat;
  • reconnect/backoff;
  • multi-session;
  • trusted device token;
  • remote session URL;
  • work secret;
  • session runner。

这不是一个小功能能解释的复杂度,而是一整套 remote control / remote session 基础设施。

它告诉我们两件事:

  1. Claude Code 的核心 loop 被设计成了 可搬运

只要上下文、工具、权限、session 能正确桥接,这套 agent loop 并不强依赖“必须本地终端直连人类”。

  1. Claude Code 的产品方向明显不是“终端里的一次性助手”

它已经在朝“本地 agent + 远端 companion + 持续 session”这个方向演进。

如果对标 Codex,这里的差别很明显:

  • Codex 开源实现更强调本地 harness 与执行边界;
  • Claude Code 这份快照则明显包含更多“产品在线化”和“多端接入”的痕迹。

UI 层:React + Ink 不是壳,而是状态机前端

很多人会把终端 UI 当成表面层,但 Claude Code 的 UI 其实很重要。

src/state/AppState.tsxsrc/state/AppStateStore.ts 读完之后,一个结论很明确:

Claude Code 的 UI 不是“打印日志”,而是一个真正订阅全局状态的前端。

AppState 里包含的东西非常多:

  • settings
  • 当前模型
  • expanded view / task panel / teammate panel
  • permission context
  • remote connection status
  • bridge 状态
  • tasks
  • agent name registry
  • MCP 状态
  • plugins 状态
  • file history
  • todos
  • notifications
  • elicitation
  • session hooks
  • bagel / browser 状态
  • tmux / tungsten 状态

这意味着 Claude Code 的 TUI 更像一个“终端里的单页应用”。

它和 Codex 的差别也很明显:

  • Codex 的 TUI 更偏 runtime 可视化;
  • Claude Code 的 TUI 已经承担了任务中心、权限交互、远程状态、子代理面板这些产品职能。

真正的工程重点:Claude Code 如何控制复杂性

读到这里,最容易产生的感觉是:Claude Code 代码量很大,模块很多,为什么没有彻底失控?

我觉得它主要靠五个设计把复杂度压住了。

1. 用 feature('...') 做编译期裁剪

源码里大量存在:

1
import { feature } from 'bun:bundle'

然后按 feature flag 去决定是否 require 某些模块。

这不是简单的运行时 if,而是明显在配合 Bun bundle 做 dead code elimination。也就是说,Claude Code 的单体代码库本身是“超集”,实际产物可以按 feature 裁掉很多分支。

2. 用类型把 runtime contract 钉死

几个非常关键的类型,例如:

  • ToolUseContext
  • ToolPermissionContext
  • Task
  • AppState
  • AgentDefinition

都承担了“宿主 ABI”的作用。这样即便模块很多,模块之间靠类型边界仍然能讲得清楚。

3. 用统一 query loop 吞掉不同入口

无论是正常交互、print、side question、skill fork、subagent,最后都尽量复用同一条 query path。这让 Claude Code 不至于出现很多“每个入口各跑一套 agent loop”的灾难。

4. 用 task abstraction 管并发

它没有把后台 agent、shell、workflow、monitor 都写成独立小机制,而是抽象成统一 task 系统。这一步很关键。

5. 从一开始就接受“上下文一定会溢出”

compact、collapse、memory、tool result 持久化、binary output storage,这些都说明 Claude Code 不幻想“把所有历史永远塞进上下文”。这在 agent runtime 里是非常成熟的工程取向。

对标 Codex:两者像在哪里,不像在哪里

讲到这里,就可以正面对标 Codex 了。

我先给一个结论版:

Codex 和 Claude Code 都不是“单纯的模型聊天客户端”,而是“模型决策 + 宿主编排 + 工具执行 + 权限边界 + 上下文压缩”的 agent system。差别在于,Codex 更偏 runtime/harness 视角,Claude Code 更偏产品化单体宿主视角。

下面分几项看。

1. 它们相同的地方

维度 Claude Code Codex
本质 模型驱动的 agent runtime 模型驱动的 agent runtime
核心循环 assistant 输出 -> tool call -> tool result -> 继续推理 assistant 输出 -> tool call -> tool result -> 继续推理
权限哲学 权限由宿主控制,不由模型自觉控制 权限由 harness / sandbox / approvals 控制
上下文哲学 上下文是宿主主动组装和压缩的 上下文是 ContextManager 主动维护和压缩的
长任务能力 依赖 compact / memory / task system 依赖 compact / ContextManager / turn state
多 agent 趋势 有 subagent / skill / teammate / background task 有 sub-agent / harness / agent 分工趋势

2. 它们最不一样的地方

维度 Claude Code Codex
技术栈 TypeScript + Bun + React + Ink Rust 为主,运行时边界更硬
UI 比重 很高,像终端 SPA 相对更轻,更像工程 runtime 的前端
外部扩展 MCP 是一级总线,plugin/skills/bridge 深度一体化 MCP/工具有,但整体更强调 harness/runtime 分层
远程能力 bridge / remote session 痕迹很强 开源实现更偏本地与沙箱执行
子代理表现 task/panel/background/team 视角更强 更强调 runtime orchestration
产品特性密度 很高,大量 feature flag 相对更“纯引擎”

3. 一个更直观的类比

如果让我用一句稍微口语化的话来说:

  • Codex 更像“一个被很好工程化的 agent 执行内核”
  • Claude Code 更像“一个把 agent 内核、工具总线、终端前端、远程桥接和产品实验都塞进来的单体应用平台”

这不是谁更先进的问题,而是产品重心不同:

  • Codex 更适合用来观察“agent runtime 的最小核心结构”;
  • Claude Code 更适合用来观察“一个商用 coding agent 是如何把 runtime 做成完整产品”的。

我对 Claude Code 架构的最终理解

如果把所有源码细节压缩成一个尽可能稳定的心智模型,我会这样描述 Claude Code:

1. 它的核心不是聊天,而是一个会持续迭代的 query loop

用户发来任务后,系统不是“回一句话”,而是进入:

  • 读上下文;
  • 做决策;
  • 调工具;
  • 读结果;
  • 必要时再开子代理;
  • 必要时压缩上下文;
  • 直到任务终止。

2. 它的核心资产不是某个 prompt,而是宿主 runtime

真正难写的部分不是 system prompt,而是:

  • tool runtime;
  • permission runtime;
  • context runtime;
  • task runtime;
  • MCP runtime;
  • bridge runtime。

模型很重要,但 Claude Code 的产品壁垒更多体现在这些宿主能力上。

3. 它已经不是“单 agent”

AgentToolSkillToolTaskLocalAgentTask、teammate/coordinator 这些结构来看,Claude Code 的真实架构正在走向:

一个主会话 + 多个可分工、可隔离、可后台运行的 agent/task 组合系统。

4. 它的上下文管理思路非常成熟

CLAUDE.mdMEMORY.md、Git context、compact、tool output persistence,这些东西放在一起,说明 Claude Code 已经明确接受了 agent 的三个现实:

  • 模型上下文是有限的;
  • 用户工作区本身就是上下文;
  • 长期任务必须靠宿主做记忆和压缩,而不能指望模型“自己记住”。

5. 它对外部世界的扩展方式非常统一

本地世界靠 shell / file / git,外部世界靠 MCP,远端世界靠 bridge。三者合在一起,Claude Code 就不再是“解释文本”,而是在一个广义执行环境中行动。

这篇源码阅读对我们理解 coding agent 有什么启发

最后我想把视角再抬高一点。

Claude Code 这份源码快照真正有价值的,不只是让我们知道“它有哪些工具”,而是它把 modern coding agent 的几个基本事实暴露得很清楚:

  1. 上下文管理一定是宿主层能力

不能只把消息历史 append 到数组里就完事。你必须管理:

  • 项目上下文;
  • 用户规则;
  • 会话记忆;
  • token 预算;
  • compact 和恢复。
  1. 工具系统一定是运行时系统,不是 schema 列表

真正难的是:

  • 权限;
  • 并发;
  • 中断;
  • 失败恢复;
  • UI 反馈;
  • 流式回填。
  1. 多 agent 是 runtime 问题,不是 prompt 问题

Claude Code 之所以能把 subagent 做得比较自然,是因为它有 task、state、transcript、notification、worktree isolation 这些宿主结构。

  1. 商用 agent 最终会演化成平台,而不是单点功能

这份源码最明显的感觉就是:Claude Code 已经不只是“Claude + shell”,而是在慢慢长成一个围绕 coding workflow 的本地平台。

References

后记

如果只看 Claude Code 的交互界面,你会以为它的重点是“Claude 会不会写代码”。但读完这份源码之后,更准确的判断应该是:

Claude Code 的重点并不是“让模型多会写一点代码”,而是“把模型嵌进一个能稳定完成真实工程任务的宿主系统里”。

这也是我认为它最值得对标 Codex 的地方。