Claude Code 源码深度解析:五层架构与核心设计模式
Claude Code 的工程复杂度集中在模型调用之外:工具怎样获得执行权限,长会话怎样压缩,子任务怎样隔离,失败之后怎样恢复。2026 年 3 月暴露的源码快照让这些机制有了可研究的实现样本;随后数月的文档和版本变化,又说明其中哪些抽象保留下来、哪些接口已经改变。
本文核对资料截至 2026 年 9 月 19 日。历史实现以 v2.1.88 的公开研究为边界,当前功能对照官方文档与当天发布的 v2.1.278。五层架构是一种阅读视图,不代表 Anthropic 对外承诺的固定模块划分。
从泄露快照到持续演进的产品
3 月 31 日的 source map 事件暴露了 Claude Code CLI 应用的大量 TypeScript 源码。它提供了客户端运行时的研究材料,不能等同于模型权重、训练系统或全部云端服务的开源。文件数和行数受生成文件、依赖及统计口径影响,分析架构时更有用的是固定版本与调用关系。
社区研究已经从罗列隐藏开关,转向解释执行系统。论文 Dive into Claude Code 于 4 月 14 日提交,7 月 2 日更新 v2;其研究对象仍是 v2.1.88。ThreeFish-AI 提供逆向研究材料,Piebald-AI 持续整理不同版本的提示词与工具描述。前者帮助理解实现,后者适合观察模型可见接口的变化,都不能代替当前运行版本的验证。
阅读这些材料需要区分三类结论:
| 证据 | 能回答什么 | 不能据此断言什么 |
|---|---|---|
| 固定版本的源码研究 | 当时的函数、数据结构和控制流 | 今天仍沿用同一实现 |
| 当前官方文档与发布记录 | 已公开功能、配置及适用范围 | 所有内部实验都已上线 |
| 架构归纳 | 怎样组织和比较机制 | 某种分层是官方唯一设计 |
9 月的更新尤其值得关注三处。普通子智能体与 fork 已有不同的上下文继承方式;团队创建改成隐式生命周期;权限分类器的部署位置也发生变化。这些都是跨边界的调整,不能只在旧工具列表后面追加几个名字。
五层视图:模型调用发生在运行时之内
官方把 Claude Code 定义为模型周围的 agentic harness,负责工具、上下文管理与执行环境。工作机制文档把工作过程概括为收集信息、执行动作、验证结果。一次任务可能在三个阶段之间多次往返。
| 层 | 关注的问题 | 典型内容 |
|---|---|---|
| 入口 | 谁提交任务、怎样显示结果 | CLI、IDE、桌面、Web、SDK |
| 运行时 | 当前会话如何受控地推进 | 会话状态、取消、Hooks、权限 |
| Agent 循环 | 下一次推理需要什么输入 | 模型请求、流式响应、工具结果、压缩 |
| 能力 | 可以读取或改变什么 | 文件工具、Bash、MCP、Skills、子智能体 |
| 执行与存储 | 动作在哪里执行、记录在哪里保存 | sandbox、worktree、transcript、认证、遥测 |
这些层不是一条单向调用链。例如权限检查会介入工具执行,压缩会改变下一轮模型输入,工具结果又回到会话状态。分层的用途是定位责任:模型请求了动作,仍要由运行时决定是否执行;工具返回成功,也仍需结合目标检查结果。
入口与执行位置同样要分开。Remote Control 可以让浏览器控制本机上的会话,云端会话则有自己的执行环境。浏览器是交互入口,并不意味着文件和命令必然在云端运行。
共享循环与 QueryEngine 的区别
论文 v2 的第 3.4 节专门澄清了 QueryEngine:在被研究的快照中,它包装非交互入口的会话状态与单轮生命周期;共享路径位于 query() 及其内部 queryLoop(),交互式 CLI 可以直接调用这条路径。因而不能把整个产品描述成由一个全局 QueryEngine 单例驱动。
这个区别会影响架构判断。包装对象负责“这一段会话有哪些状态”,循环负责“怎样把一次模型响应转化为下一步动作”。把两者分开,才能说明多入口复用的是执行过程,而不必共享同一个内存对象。
Agent 循环:流式输出与工具反馈
可以用下面的概念流程表示一次任务。它省略了重试和具体协议,不是泄露源码的逐行转写:
1 | |
工具结果是下一轮推理的输入。例如测试失败后,模型接收到退出码与错误信息,再决定读取哪个文件。若只把失败打印到终端,却没有把它写回模型可见消息,循环便失去了反馈。
AsyncGenerator 提供了什么
异步生成器把“等待输入”和“逐次产出事件”放在一个函数里。await 等待 Promise,yield 向消费者交出一个值;消费侧用 for await...of 接收事件。
1 | |
这是控制流示意,不是可独立运行的 Claude Code 客户端。它表达的价值是事件能逐个传出,消费者无需等待整轮完成。普通 async 函数也能消费流,回调和显式状态机也能实现同样的业务;生成器减少的是部分手动衔接代码,并没有消除业务状态。
取消也不能只依赖垃圾回收。网络请求、子进程、文件句柄和后台任务需要各自的取消或清理路径。一个可维护的循环应当把预算检查、终止条件与资源释放显式放在生命周期边界。
工具参数与执行时机
Messages 流式协议包含消息起止、内容块起止和增量事件。工具参数可能以 input_json_delta 分片到达,单个片段不一定是合法 JSON。
因此需要区分“正在显示模型输出”和“已经得到可执行的工具调用”。工具名、参数、调用标识完成组装与校验后,才能交给执行路径。结果还必须关联回对应调用,否则并发场景会把不同工具的反馈混在一起。
Agent SDK 的职责边界
Agent SDK把工具循环交给已有运行时管理。原始模型 API 返回工具请求后,应用通常需要自行执行并续接消息;使用 Agent SDK,则主要配置工具、权限、Hooks 和任务约束。
Python 的 query() 适合一次查询的事件消费;ClaudeSDKClient 提供持续交互入口。它们是使用方式差异,不是“实验代码”与“生产代码”的等级划分。调用时仍应检查结果状态、预算和权限配置,API 返回一个结果不代表业务目标已经完成。Python SDK 参考
提示词与缓存:复用的是请求前缀
提示词架构可以从输入稳定性理解。工具定义和通用规则变化较少,工作目录、用户消息和工具结果变化较多。把稳定内容置于较早位置,有机会让后续请求复用相同前缀。
Prompt caching 文档说明,缓存要求标记点之前的请求片段完全匹配。它比较模型请求内容,不比较 JavaScript 数组地址。复制一个内容相同的数组不会仅因对象身份变化而破坏缓存;原地修改早期消息,却可能使后面的前缀无法命中。
缓存隔离也属于服务端契约。当前 Claude API 按 workspace 隔离,其他平台存在组织级隔离差异,不能从客户端出现“global”之类的命名推导出跨任意用户共享。观测时应结合 cache_read_input_tokens、cache_creation_input_tokens 与普通输入量,而不是只看某一项下降。
工程上可以把一次请求拆成三段:
1 | |
这是缓存布局的思考方法,不是保证每次请求都按此精确排列。若频繁在最前面插入时间戳、随机字段或变化的工具定义,后面的长历史也可能失去复用机会。相反,仅在尾部追加消息有利于保持已有前缀,但仍受模型、缓存有效期和服务端隔离范围影响。
压缩改写历史后,受影响部分通常需要重新建立缓存。保持不变的工具和系统前缀仍可能命中,不能把压缩等同于整条请求的缓存全部清空。
上下文治理:裁剪、摘要与持久记录
长会话有两种不同压力:输入超过上下文容量,以及大量旧信息增加后续推理成本。扩大窗口只能缓解第一种,还需要决定哪些内容应继续随请求发送。
历史研究在 v2.1.88 中归纳了工具结果预算、snip、microcompact、context collapse 和 auto-compact 等机制,其中部分受特性开关控制。这是一组有条件的处理路径,不能当作每轮都完整执行的固定五级流水线。论文第 4.3 节
当前公开行为可以概括为先清理较旧的工具输出,再按需摘要。持续的大输入仍可能导致反复压缩失败,运行时会停止并报错,而不是无限重试。上下文管理
| 手段 | 主要降低什么 | 代价 |
|---|---|---|
| 限制工具输出 | 单次日志、搜索结果的体积 | 可能漏掉远处的相关行 |
| 按需加载 | 暂时用不到的 schema 或文档 | 需要额外检索步骤 |
| 裁剪历史 | 重复、陈旧的工作材料 | 后续可能重新读取 |
| 摘要 | 多轮对话的输入体积 | 细节与约束可能丢失 |
| 外部持久记录 | 反复把全部历史放进上下文的需求 | 必须设计检索与更新机制 |
以一次排障为例,十万行日志适合保存在文件中,主上下文只保留错误模式、时间范围和检索路径。这样后续可以重新定位证据。若只留下“问题已修复”五个字,摘要虽短,却丢掉了判断依据。
压缩摘要、transcript 与持久记忆承担不同职责。摘要是后续推理的工作材料;transcript 是会话记录;记忆用于跨会话复用知识。压缩当前输入不等于删除磁盘上的历史,也不能据此认定历史会永久保留,应另外检查保留策略。
Hooks:事件触发与决策必须分别理解
Hook 的触发由运行时生命周期控制。它适合把格式化、检查和外部记录接到工具或会话边界。触发时机确定,不意味着处理结果确定:shell 规则可以做确定性检查,prompt 和 agent handler 则会调用模型。
当前公开 handler 包括 command、HTTP、MCP tool、prompt 和 agent。匹配同一事件的 Hooks 并行运行,不能依赖注册顺序传递中间状态。需要先归档再通知时,应把有依赖的步骤放入同一个处理程序。Hooks 参考
控制动作、补充上下文、提示用户
这三个用途应分别表达:
| 输出 | 用途 |
|---|---|
hookSpecificOutput.permissionDecision |
PreToolUse 等相应事件的权限决策 |
hookSpecificOutput.additionalContext |
在支持此字段的事件中补充模型上下文 |
systemMessage |
通常提示用户;具体投递行为取决于事件及同步、异步方式 |
下面是 PreToolUse 的结构化拒绝示例,不涉及具体文件路径匹配逻辑:
1 | |
字段必须按事件 schema 使用。不能把 systemMessage 当作通用的模型上下文回灌接口,也不能把某个事件的输出复制给所有事件。
压缩和失败的边界
PreCompact 位于压缩之前,PostCompact 位于压缩之后。前者可阻止压缩,后者可读取生成的摘要并更新外部记录,不能回滚已经完成的压缩。两者会丢弃 systemMessage 与 continue,因此归档后的状态不能依赖这两个字段注入。
对支持阻断的事件,退出码 2 可以表达阻止动作;但有些事件已经发生,只能报告错误。当前协议还会解析结构化 JSON,不能笼统认为所有非零退出都忽略 JSON。实现前应核对具体事件的决策表,检查超时和脚本无法启动时会怎样处理。事件控制与退出码
对部署审批这类关键限制,Hook 的价值在于提供检查入口,但仍要验证失败行为。脚本没跑起来、超时或返回无效 JSON,和明确拒绝是不同状态。
多智能体:新上下文、fork 与团队
子任务能减少主会话需要携带的探索材料,但其模型调用仍然消耗配额或费用。收益来自控制信息流与并行工作,不是子任务结束后计算成本消失。
普通 subagent 与 fork
当前文档明确区分两条路径:普通 subagent 从任务说明与自身定义建立新上下文;fork 从主会话当前状态派生,继承历史、系统提示、工具和模型。相同请求前缀让 fork 能复用父会话缓存。子智能体文档
model: inherit 只表达模型选择,不能单独证明继承完整对话。交互模式从 v2.1.232 起默认启用 fork mode,-p 和 SDK 的默认值不同。/subtask 可手动发起 fork;配置和入口必须一起判断。
对于独立搜索任务,新上下文通常足够:明确问题、目录和返回格式即可。对于依赖长篇讨论背景的方案尝试,fork 可以减少重新整理前情的成本。无论哪种路径,多开任务都可能增加输出和协调开销,不能用“共享缓存”推导出五个任务只花一个任务的费用。
三种隔离解决不同问题
上下文隔离控制哪些对话材料进入主会话。Git worktree 为任务提供独立检出目录,减少并行修改互相覆盖。Sandbox 约束受管进程能访问的文件或网络资源。
三者不能互相代替:两个子智能体即使拥有不同消息历史,仍可能编辑同一个文件;两个 worktree 也仍可能访问同一数据库或监听同一个端口。涉及共同外部资源时,任务分工还需要资源所有权、幂等操作或协调机制。
Team 的生命周期已经变化
当前团队文档说明,自 v2.1.178 起不再使用 TeamCreate / TeamDelete,启用实验功能后的会话拥有隐式团队,退出时自动清理。团队仍默认关闭,-p 与 SDK 不产生 teammates。
团队更适合需要独立会话、共享任务与互相发消息的协作。它增加了协调成本:任务认领、结果冲突、成员退出都需要处理。固定且独立的几项检索,用普通子任务往往更直接。
工具与权限:可请求、获准、执行是三个状态
工具 schema 告诉模型怎样提出动作,权限系统判断动作是否获准,执行环境才真正产生副作用。三者的日志应该能够关联。只记录“模型请求了 Bash”无法证明命令实际运行;只看到工具成功返回,也无法证明用户目标达成。
MCP 的 readOnlyHint、destructiveHint 等 annotation 是工具描述提示。规范要求客户端不要把不可信服务器的声明当成可靠事实,因此不能用 readOnlyHint: true 作为无副作用或允许并行的安全证明。MCP 工具规范
规则检查与 sandbox
当前权限规则按 deny → ask → allow 评估。宽泛 deny 不会被更具体的 allow 覆盖,PreToolUse 的 allow 也不能绕过显式 deny 或 ask。规则中的路径锚点仍需区分:// 表示文件系统绝对路径,~/ 是用户目录,/ 相对项目根,其余路径相对工作目录。权限文档
Sandbox 为受管 Bash 进程及其子进程限制文件系统和网络访问。它与工具审批互补,不应被理解为全部 MCP 服务或所有宿主活动自动进入同一个隔离环境。Sandbox 文档
例如允许一次 shell 调用,并不代表需要授予整个用户目录的写权限。反过来,某条命令能够在 sandbox 内运行,也不能替代外部系统对账号、数据库和发布权限的控制。
9 月 19 日:auto mode 分类器的服务端迁移
v2.1.278 发布记录把 Claude API、Enterprise,以及 Bedrock、Vertex、Foundry、网关用户的 auto mode 默认分类路径调整为服务端分类器,并在 /status 增加运行位置说明。服务端分类器不收取分类开销,计费回退会有提示;部分平台可以通过环境变量退出该默认行为。
这改变了权限架构的部署视图:执行动作仍要经过运行时控制,但某项安全判断可以请求服务端完成。分析延迟、计费和故障时,应区分分类请求与任务本身的模型请求。auto 仍然是审核模式,不能和跳过审批等同。
MCP 与 Skills:按需加载不同内容
MCP 接入外部工具与数据,Skills 保存可复用的任务说明和资源。它们都可能采用按需加载,但装入的内容与产生副作用的位置不同。
MCP Tool Search
当前 Claude Code 默认延迟加载 MCP 工具定义,通过 Tool Search 获取需要的 schema。这样服务器多时,不必一开始把全部详细参数塞入模型上下文。连接可使用本地 stdio 或远程 HTTP;旧 SSE transport 已被标为弃用。MCP 文档
延迟加载解决的是发现和上下文开销,不会自动解决认证与权限。检索到了一个工具,只能说明调用描述可用,不能证明当前身份具有执行权限。
Agent SDK 还支持把自定义工具注册到进程内 MCP server,减少为本地函数另启服务的部署负担。工具仍应按协议返回内容和错误信息,而不是把函数签名直接等同于完整执行契约。SDK 自定义工具
Skills 的渐进披露
Skills 用 SKILL.md 描述适用任务、操作步骤和关联资源。元数据负责发现,正文在使用时加载,附带文件再按需读取。用户也能显式调用;disable-model-invocation: true 可以限制模型自行选择。旧 .claude/commands/ 路径仍有兼容支持。Skills 文档
| 机制 | 适合保存什么 | 执行责任在哪里 |
|---|---|---|
| 项目规则 | 构建命令、约定、长期约束 | 模型读取后遵循,硬限制另设权限 |
| Skill | 一类任务的方法和资源 | 调用后的任务执行过程 |
| Hook | 生命周期上的检查或副作用 | 运行时触发 handler |
| MCP 工具 | 对外系统的可调用操作 | 客户端授权与服务器执行 |
若一个工作流程总在固定事件后运行,Hook 比仅写入 Skill 更直接;若流程需要根据任务选择,Skill 更适合承载说明。工具权限仍应独立配置,避免把文字指令当成访问控制。
持久记忆与实验模块
当前 auto memory 默认位于 ~/.claude/projects/<project>/memory/,同一仓库的 worktree 共享该记忆目录。启动时加载 MEMORY.md 的前 200 行或 25KB,以先到的限制为准,主题文件按需读取。它与人工维护的 CLAUDE.md 都是上下文,不能强制授予或禁止权限。记忆文档
因此应把易变化的环境事实和长期项目规则分开。端口、部署版本、临时分支属于需要重新核验的信息;构建入口与模块职责可以持久保存,但也应随仓库演进更新。记忆的存在不保证其内容仍然成立。
历史讨论中的 KAIROS、DreamTask、BUDDY 或反蒸馏相关标志,应回到对应快照与功能开关阅读。一个模块名既不能证明功能向全部用户开放,也不能证明其计费方式、生产流量或当前开关状态。对架构研究有价值的是它要求怎样的调度与持久化机制,产品可用性则需要另找发布证据。
把架构判断落到可观察的结果
评估一套 coding agent,可以选一个小任务,记录用户输入、模型工具请求、审批结果、实际副作用和验证输出。沿这条链检查,能比工具数量更快发现工程缺口。
| 场景 | 应观察的结果 |
|---|---|
| 工具被拒绝 | 没有副作用,拒绝原因能进入后续决策 |
| 大日志进入会话 | 输出有预算,原始材料仍可定位 |
| 会话发生压缩 | 当前目标、约束和证据引用仍能恢复 |
| 两个任务并行编辑 | 文件所有权明确,结果可以分别审阅 |
| 用户取消任务 | 网络请求和子进程确实停止或有明确后续状态 |
这些场景把循环、权限、上下文与隔离放到同一个任务里检验。架构图负责解释职责;最终是否可靠,要看边界条件下的实际行为。
参考资料
社区研究与版本记录:
- Dive into Claude Code,arXiv 版本记录:首版 2026-04-14,v2 为 2026-07-02。
- 论文 v2 全文:研究对象
v2.1.88,包含 QueryEngine 澄清与上下文处理分析。 - ThreeFish-AI 逆向研究与 Piebald-AI 提示词记录:两种不同层面的社区观察材料。
- Claude Code v2.1.278:2026-09-19 的 auto mode 分类器更新。
当前接口与机制以各节链接的官方文档为准。文档会继续变化,历史实现结论应始终带上版本范围。


