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,负责工具、上下文管理与执行环境。工作机制文档把工作过程概括为收集信息、执行动作、验证结果。一次任务可能在三个阶段之间多次往返。

Claude Code 五层概念架构:入口、运行时、循环、能力与执行存储

层 关注的问题 典型内容
入口 谁提交任务、怎样显示结果 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
2
3
4
5
用户输入 → 组织模型请求 → 接收流式响应
├─ 文本 → 更新界面
├─ 工具请求 → 权限检查 → 执行 → 记录结果 ─┐
└─ 完成 → 返回最终结果 │
下一轮模型请求 ←──────────────────────────┘

工具结果是下一轮推理的输入。例如测试失败后,模型接收到退出码与错误信息,再决定读取哪个文件。若只把失败打印到终端,却没有把它写回模型可见消息,循环便失去了反馈。

AsyncGenerator 提供了什么

异步生成器把“等待输入”和“逐次产出事件”放在一个函数里。await 等待 Promise,yield 向消费者交出一个值;消费侧用 for await...of 接收事件。

1
2
3
4
5
6
7
8
9
10
async function* relay(stream) {
for await (const event of stream) {
yield event;
}
}

// stream 是上游提供的异步可迭代对象。
for await (const event of relay(stream)) {
render(event);
}

这是控制流示意,不是可独立运行的 Claude Code 客户端。它表达的价值是事件能逐个传出,消费者无需等待整轮完成。普通 async 函数也能消费流,回调和显式状态机也能实现同样的业务;生成器减少的是部分手动衔接代码,并没有消除业务状态。

取消也不能只依赖垃圾回收。网络请求、子进程、文件句柄和后台任务需要各自的取消或清理路径。一个可维护的循环应当把预算检查、终止条件与资源释放显式放在生命周期边界。

工具参数与执行时机

Messages 流式协议包含消息起止、内容块起止和增量事件。工具参数可能以 input_json_delta 分片到达,单个片段不一定是合法 JSON。

因此需要区分“正在显示模型输出”和“已经得到可执行的工具调用”。工具名、参数、调用标识完成组装与校验后,才能交给执行路径。结果还必须关联回对应调用,否则并发场景会把不同工具的反馈混在一起。

Agent SDK 的职责边界

Agent SDK把工具循环交给已有运行时管理。原始模型 API 返回工具请求后,应用通常需要自行执行并续接消息;使用 Agent SDK,则主要配置工具、权限、Hooks 和任务约束。

Python 的 query() 适合一次查询的事件消费;ClaudeSDKClient 提供持续交互入口。它们是使用方式差异,不是“实验代码”与“生产代码”的等级划分。调用时仍应检查结果状态、预算和权限配置,API 返回一个结果不代表业务目标已经完成。Python SDK 参考

提示词与缓存:复用的是请求前缀

提示词架构可以从输入稳定性理解。工具定义和通用规则变化较少,工作目录、用户消息和工具结果变化较多。把稳定内容置于较早位置,有机会让后续请求复用相同前缀。

请求由 tools、system、messages 组成:稳定前缀缓存与上下文维护分离

Prompt caching 文档说明,缓存要求标记点之前的请求片段完全匹配。它比较模型请求内容,不比较 JavaScript 数组地址。复制一个内容相同的数组不会仅因对象身份变化而破坏缓存;原地修改早期消息,却可能使后面的前缀无法命中。

缓存隔离也属于服务端契约。当前 Claude API 按 workspace 隔离,其他平台存在组织级隔离差异,不能从客户端出现“global”之类的命名推导出跨任意用户共享。观测时应结合 cache_read_input_tokens、cache_creation_input_tokens 与普通输入量,而不是只看某一项下降。

工程上可以把一次请求拆成三段:

1
2
3
稳定定义:工具 schema、通用系统指令
会话背景:项目规则、环境和已加载知识
增量历史:用户输入、模型响应、工具结果

这是缓存布局的思考方法,不是保证每次请求都按此精确排列。若频繁在最前面插入时间戳、随机字段或变化的工具定义,后面的长历史也可能失去复用机会。相反,仅在尾部追加消息有利于保持已有前缀,但仍受模型、缓存有效期和服务端隔离范围影响。

压缩改写历史后,受影响部分通常需要重新建立缓存。保持不变的工具和系统前缀仍可能命中,不能把压缩等同于整条请求的缓存全部清空。

上下文治理:裁剪、摘要与持久记录

长会话有两种不同压力:输入超过上下文容量,以及大量旧信息增加后续推理成本。扩大窗口只能缓解第一种,还需要决定哪些内容应继续随请求发送。

历史研究在 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
2
3
4
5
6
7
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "此环境禁止修改数据库结构"
}
}

字段必须按事件 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:三个互不替代的隔离边界

上下文隔离控制哪些对话材料进入主会话。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 保存可复用的任务说明和资源。它们都可能采用按需加载,但装入的内容与产生副作用的位置不同。

当前 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,可以选一个小任务,记录用户输入、模型工具请求、审批结果、实际副作用和验证输出。沿这条链检查,能比工具数量更快发现工程缺口。

场景 应观察的结果
工具被拒绝 没有副作用,拒绝原因能进入后续决策
大日志进入会话 输出有预算,原始材料仍可定位
会话发生压缩 当前目标、约束和证据引用仍能恢复
两个任务并行编辑 文件所有权明确,结果可以分别审阅
用户取消任务 网络请求和子进程确实停止或有明确后续状态

这些场景把循环、权限、上下文与隔离放到同一个任务里检验。架构图负责解释职责;最终是否可靠,要看边界条件下的实际行为。

参考资料

社区研究与版本记录:

当前接口与机制以各节链接的官方文档为准。文档会继续变化,历史实现结论应始终带上版本范围。