Claude Code 不是一个更聪明的模型,而是一层包在模型外面的 agentic harness。官方文档对它的定义只有一句话:agentic loop 由两个部件驱动——负责推理的模型,和负责行动的工具;Claude Code 就是围绕模型提供工具、并管理模型能看到什么的那一层。把这句话拆开,也就把整个系统拆开了。

模型这一侧只有 Messages API:一次无状态的 HTTP 调用,输入 system、工具定义和完整对话历史,输出 text / thinking / tool_use 内容块。它不能读文件,也不能跑命令。所谓“调用工具”,实际动作是模型写出一段结构化 JSON、把 stop_reason 置为 tool_use,然后停下等待。真正让这件事发生的是 harness:它做权限检查、执行工具、把结果以 tool_result 回填,再把完整上下文重新发过去。

分工带来的后果很直接:能力上限由工具集合决定,可靠性下限由权限、沙箱与检查点决定,而成本与效果落在上下文工程上。Claude Code 在第三件事上花的心思最多——它不试图把整个仓库塞进窗口,而是让模型在需要时自己检索、把大范围调研隔离给子代理、在接近上限时压缩并从磁盘重新注入关键信息。

一、协议与循环:模型只写出调用,Harness 负责执行

Messages API 是无状态的:每轮都要重新携带 system prompt、工具定义和全部历史。Agent 跑 50 轮,同样的前缀就发送 50 次,这是缓存与压缩机制存在的前提。tools[] 由 name、description、JSON Schema 组成,描述质量直接决定模型会不会用、用得对不对;Anthropic 把这称为 ACI 设计,要求像设计人机界面一样设计工具。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 请求:无状态,每轮都带全量前缀
{ "system": "...", "tools": [...], "messages": [...] }

// 响应:模型只"写出"调用,并不执行
{ "content": [
{ "type": "thinking", "thinking": "先跑测试看失败信息…", "signature": "EqQB…" },
{ "type": "tool_use", "id": "toolu_01", "name": "Bash",
"input": { "command": "npm test -- auth" } } ],
"stop_reason": "tool_use" }

// 回填:harness 执行完,把结果作为 user 消息追加
{ "role": "user", "content": [
{ "type": "tool_result", "tool_use_id": "toolu_01",
"content": "3 failed, 9 passed", "is_error": false } ] }

一次“输出调用 → 执行 → 结果回填”的往返就是一个 turn。两个细节值得注意:被权限拒绝也只返回一条 tool_result,模型会换一条路而不是异常中断;thinking 块带服务端签名,多轮必须原样回传,模型才能在工具结果之后接着原来的思路继续(interleaved thinking)。

循环本身极简,剥开就是一个 while:

1
2
3
4
5
6
7
8
while (true) {
if (nearContextLimit()) await compact() // PreCompact → 清理 → 摘要 → compact_boundary
const resp = await claude.messages.stream({ system, tools, messages: history })
if (!resp.content.some(b => b.type === "tool_use")) break // 纯文本 → end_turn
const results = await runTools(resp.content) // 权限 → PreToolUse → 执行 → PostToolUse
history.push(assistant(resp), user(results))
if (++turns >= maxTurns || cost >= maxBudgetUsd) return error
}

以“修复 auth 失败测试”为例:Bash 跑测试拿到 3 个失败 → 派 Explore 子代理定位问题 → 并行 Read 两个文件 → Edit 精确替换 → 再跑测试全部通过 → 输出纯文本 end_turn。按 Esc 立即取消当前工具,直接输入回车则是排队消息,在当前工具结束后被读取。

二、六层架构:一个循环,多个外壳

从官方文档披露的行为,可以把 Claude Code 拆成六层:

层 关键组件 职责
① 交互界面 TUI、VS Code / JetBrains、Desktop、claude.ai/code、Slack、headless -p、Agent SDK 外壳
② 会话与配置 Session JSONL、resume / fork、settings 分层 managed › CLI › local › project › user 状态与策略
③ Agentic Loop gather → act → verify、流式解析、工具调度、中断与排队、maxTurns · maxBudgetUsd 驱动循环
④ 上下文引擎 system prompt、CLAUDE.md、Auto memory、Tool search、auto-compaction、prompt caching 决定模型看到什么
⑤ 工具与安全 Read / Edit / Write、Glob / Grep、Bash(+Sandbox)、Web、Agent / Skill / Task、权限引擎、Checkpoints 执行并受限
⑥ 扩展层 Skills、MCP、Hooks、Subagents、Plugins、LSP 按需扩展

关键在于第 ③ 层只有一份实现。Agent SDK 不是重新实现 Agent,而是捆绑原生二进制、以子进程驱动:stdin/stdout 走 stream-json,另有一条控制通道(initialize、interrupt、set_permission_mode、set_model、rewind_files,反向的 can_use_tool、hook_callback、mcp_message),所以终端、IDE、CI 与 SDK 跑的是同一个循环。

取舍也很明确:单循环加强工具,而不是预设的工作流图。编码任务的子任务不可预知,适合让模型依据环境反馈(测试、报错)自己决定下一步;检索用即时查询而非预建索引,始终看到最新代码,也不必维护向量库。

三、权限、沙箱与检查点

Claude Code 的安全分三层:permissions 决定什么能不经询问就执行,sandbox 限制执行后能碰到什么,checkpoints 让文件改动可以撤销。

权限规则的求值顺序固定:

  1. PreToolUse hook:exit code 2 的阻断型 hook 先于规则生效,能拦下即便被 allow 的调用;
  2. deny:任何层级(managed / CLI / project / user)命中 deny,其他层级都无法放开;
  3. ask:命中必须询问,即便 hook 返回 allow;
  4. allow:如 Bash(npm test *) 这类模式;
  5. 模式兜底:都没命中时按当前权限模式决定。

权限模式用 Shift+Tab 切换,共六种:auto(后台分类器审阅多数动作,交互式终端默认)、default(改动前询问)、acceptEdits、plan(只出方案)、dontAsk、bypassPermissions(仅在隔离环境用)。“Yes, and don’t ask again” 对 Bash 与 WebFetch 会写进 .claude/settings.local.json,文件修改授权只在本会话有效。

开启 sandbox 后 Bash 运行在 OS 级隔离中:macOS 用 Seatbelt、Linux / WSL2 用 bubblewrap,写入限制在工作目录,网络按域名白名单走代理。权限与沙箱回答两个不同问题:要不要执行,以及执行了能碰到什么。

checkpoints 处理“改错了怎么办”:每个新 prompt 建一个检查点,编辑前快照受影响文件,保留最近 100 个,Esc Esc 或 /rewind 可回退代码和/或对话。它的边界也清楚:Bash 造成的改动、子代理编辑、数据库与部署这类远程副作用不在快照内,只能靠权限控制。

四、上下文工程与扩展点

上下文是有限的注意力预算:token 越多,模型对每条信息的召回越弱。Claude Code 让模型看到最小但足够的高信号集合,手段有五种:

  • JIT 检索:只留路径引用,靠 Glob / Grep / Read 需要时拉取;
  • 延迟加载:Skills 只放描述、MCP 只放工具名,正文与 schema 用到才加载;
  • 子代理隔离:子代理独立上下文读几十个文件,只交回摘要;
  • 自动压缩:接近上限时先清旧工具输出,再摘要历史;
  • 结构化笔记:CLAUDE.md、MEMORY.md、Task 清单落盘,压缩后重新注入。

压缩后保留什么有明确清单:system prompt 与 output style 照常生效;项目根 CLAUDE.md、无路径限定的 rules、Auto memory、plan 与 skill 描述从磁盘重新注入;最近修改的至多 5 个文件重新读取,超过 5000 tokens 只留路径;已调用 skill 正文按单个 5000、总计 25000 tokens 注入。早期对话的细节指令会被摘要、可能丢失——所以“必须每次都遵守”的规则要写进 CLAUDE.md;过大的工具输出还可能触发 thrashing 保护,停止自动压缩。

跨会话记忆也只靠落盘文件:managed policy、~/.claude/CLAUDE.md、./CLAUDE.md、CLAUDE.local.md 与 MEMORY.md。加载从根目录到当前目录逐级拼接而非覆盖,越近越后读;MEMORY.md 只加载前 200 行或 25KB。

扩展层统一按需进入上下文:Skills 开局只暴露 name 与 description,调用后才加载正文;MCP 工具以 mcp__<server>__<tool> 命名,schema 延迟加载;Hooks 在外部运行、不占上下文,却能在固定点保证“每次都发生”,exit 2 可以阻断一次工具调用,格式化与危险命令拦截该放在这里;Subagents 有独立上下文与 system prompt,Fork 复制当前对话作为起点,对应 orchestrator-workers 模式:主代理拆任务、只汇总摘要。

三种读法

同一个主题准备了三个层次的交互页面,可以按需要深入:

架构详解是完整参考,逐节讲清模型原理、Agent 运行流程与六层架构:Claude 原理与 Claude Code 代码架构详解。

动画以“修复 auth 模块失败的测试”为场景,逐步骤演示从启动会话到结果持久化的每个环节,包括每轮的 messages 结构、权限决策、上下文增长与检查点变化,可单步、播放和调速。下面直接嵌入:

在新窗口打开完整页面 →

演示文稿是幻灯片形式,覆盖训练三段式、Constitutional AI、工具调用协议、六层架构、安全机制与上下文工程,适合快速过一遍或当作分享材料。下面直接嵌入:

在新窗口打开完整页面 →

参考与延伸

同系列的另外两篇: