乐于分享
好东西不私藏

Claude Code CLI 源码深度解析:从 Prompt 缓存到工业级 AI Agent 架构

Claude Code CLI 源码深度解析:从 Prompt 缓存到工业级 AI Agent 架构

最近,Claude Code CLI 的源码意外泄露。

看完源码才发现:这不是”Prompt 工程”那么简单,而是缓存架构 + 自我进化记忆 + 多模式编排的完整基础设施。

这才是为什么 Claude Code 能支持无限长对话,为什么删除几百条消息后响应速度还是那么快,为什么 Agent 能从”记录对话”进化到”学习知识”。

这不是 prompt 写得好不好的问题,是整个系统架构为长对话、自主工作、持续进化优化到了极致。


一、两层 Prompt 缓存架构

大部分 AI 应用的 prompt 是一整块发给模型的。每次对话都要重发,浪费 token 也浪费钱。

Claude Code 把 prompt 切成两层:

1.1 静态前缀(scope: ‘global’)

Anthropic 后端跨用户、跨组织共享缓存。所有人的所有会话都复用同一份。这部分内容几乎不变:

  • 身份定义(Intro)
  • 系统规则(System)
  • 任务规范(Doing Tasks)
  • 操作安全(Actions)
  • 工具使用指南(Using Tools)
  • 风格要求(Tone & Style)
  • 效率要求(Output Efficiency)

1.2 动态部分(scope: null)

每个会话独立,只在 /clear 或 /compact 时失效。包含会话特定的:

  • 会话指引(Session Guidance)
  • 记忆(Memory)
  • 环境信息(Env Info)
  • MCP 指令(MCP Instructions)
  • 语言偏好(Language)
  • 输出风格(Output Style)
  • 草稿本(Scratchpad)
  • Token 预算(Token Budget)

1.3 分界线(BOUNDARY MARKER)

这是整个设计的关键。分界线确保动态部分变化时,静态部分的缓存不会失效。两者完全解耦。

为什么这样设计?

  • 静态部分大约 5-8K token,但可以跨所有用户共享
  • 动态部分每次会话不同,通常也是 5-10K token
  • 分界线让两者解耦,动态变化不影响静态缓存
  • 节省的不是绝对数量,而是跨数百万用户共享这个乘数效应

1.4 缓存管理机制

动态部分不是每次重算,而是用 systemPromptSection() 注册 + memoize,只在 /clear 或 /compact 时清缓存。

例外: MCP Instructions 使用了 DANGEROUS_uncachedSystemPromptSection,因为 MCP 服务器会中途连接/断开,需要实时更新。

效果:

  • Claude Code 启动几乎是瞬间的
  • 新会话只需要发送动态部分
  • 长对话的成本显著降低

二、四层递进的压缩策略

Claude Code 的压缩不是单一机制,而是四层递进的压缩策略,根据上下文压力逐级升级。

上下文接近上限
    ↓
Layer 1: MicroCompact(轻量清理)
├── 缓存已冷 → 直接清空旧 tool_result
└── 缓存还热 → cache_edits 增量删除
    ↓ 不够?
Layer 2: SessionMemoryCompact(会话记忆压缩)
├── 保留最近 10K-40K token 的消息
└── 用 Session Memory 替代旧对话
    ↓ 还不够?
Layer 3: Full Compact(全量压缩)
├── Fork 一个 agent 做摘要
├── 复用主对话的 prompt cache
└── 图片替换为 [image] 标记
    ↓ 还不够?
Layer 4: PTL Retry(终极兜底)
└── 从头部砍掉最旧的消息组,最多砍 20%

2.1 Layer 1: MicroCompact + Cache Edits(黑科技)

这是 Anthropic API 的未公开功能,核心是缓存不失效的增量删除

问题场景:

长对话中,旧的 tool_result(工具调用结果)会占满上下文。一个典型的长对话可能有 100K+ token 的 tool_result。

传统做法有两种:

  1. 直接删掉旧消息 → 整个 prompt cache 失效 → 下一轮全部重发 → 费用飙升($1+ 一轮)、延迟明显(5-10 秒)
  2. 不删除 → 上下文溢出,对话中断

为什么删除旧消息会导致缓存失效?

这是理解 Cache Edits 价值的关键。

Prompt Cache 的工作原理:前缀完全匹配

假设你的消息序列是这样的:

[System Prompt] + [消息 1] + [消息 2] + [消息 3] + [消息 4] + [消息 5]

Claude 会把这整个序列缓存起来。下一轮对话,你发送:

[System Prompt] + [消息 1] + [消息 2] + [消息 3] + [消息 4] + [消息 5] + [消息 6]

Claude 发现前面的部分和缓存完全一致,直接复用缓存,只处理新的【消息 6】。

但如果你删掉了消息 2:

[System Prompt] + [消息 1] + [消息 3] + [消息 4] + [消息 5] + [消息 6]

Claude 发现从消息 1 之后,序列就对不上了,缓存失效,整个序列需要重新处理。

Cache Edits 的解法:

不修改本地消息,在 API 层发送删除指令:

{
  "type": "cache_edits",
  "edits": [
    { "type": "delete", "cache_reference": "tool_use_id_A" },
    { "type": "delete", "cache_reference": "tool_use_id_B" }
  ]
}

工作原理(3 步):

  1. 给每个块贴上 ID — 每个 tool_result 发送时带上 cache_reference,就像给缓存中的每个块贴上条形码
  2. 告诉服务端”跳过这些块” — 通过 cache_edits 发送删除指令,告诉服务端”把这些块标记为不可见”
  3. 保存标记指令,供后续请求使用 — 因为缓存里的原始数据还在(只是被标记为跳过),所以每一轮都需要重新发送 cache_edits

效果:

  • 缓存前缀没变 → 下一轮还是命中
  • 模型看到的上下文变短了 → 省 token
  • 原始数据还在服务端 → 只是被标记为”跳过”

2.2 Layer 2: SessionMemoryCompact — 外科手术式的精确切割

当 MicroCompact 清理完 tool_result 还不够时,就要动真格的了:切掉旧对话。

但这不是简单的”砍前半段”。Claude Code 的做法是外科手术式的精确切割,确保不会把一个完整的交互拆散。

核心规则:

  • 保留最近 10K-40K token 的消息(根据上下文压力动态调整)
  • 用一条 Session Memory 替代旧对话(”之前我们讨论了……”)
  • 绝不拆散 tool_use 和 tool_result — 工具调用和结果必须成对出现
  • 绝不拆散 thinking 块和同轮的 tool_use — 思考过程和执行动作必须在一起

为什么这样设计?

如果你把 tool_use 留下但删掉 tool_result,模型会以为工具调用还没执行完,陷入等待状态。如果你把 thinking 块删掉但留下 tool_use,模型会不知道为什么要调用这个工具。

所以 Claude Code 的切割逻辑是:从保留区的起点向后扩展,直到满足最小 token 数和最小消息数,并且不破坏任何完整的交互组。

这是一个”宁可多留一点,也不能拆散”的保守策略,确保上下文的语义完整性。

2.3 Layer 3: Full Compact — 复用缓存的摘要魔法

当前两层都不够时,就要祭出大招了:Fork 一个 agent 做全量摘要。

但这里有个精妙的设计:摘要 agent 的 system prompt + tools + model 和主对话完全一致

为什么?因为这样可以直接命中主对话的 prompt cache,大幅降低 token 成本。

工作流程:

主对话上下文快满了
    ↓
Fork 一个摘要 agent(复用主对话的 system prompt)
    ↓
图片清理 — 把所有 image/document 块替换为 [image]/[document] 标记
    ↓
摘要 agent 读取对话历史,生成摘要
(因为 system prompt 一致,直接命中缓存)
    ↓
摘要完成后,自动注入关键上下文:
- 最近 5 个文件的最新内容(每文件 ≤5K token,总预算 50K)
- 已激活的 Skills(每个 ≤5K token,总预算 25K)
- MCP Instructions Delta
    ↓
主对话继续,上下文变短了,但关键信息没丢

为什么要自动注入文件和 Skills?

因为压缩后,模型会忘记之前读过哪些文件、激活过哪些技能。如果不自动恢复,下一轮对话模型会说”我需要先读取 xxx 文件”,浪费一轮交互。

自动注入的逻辑是:把最近操作过的文件和技能重新喂给模型,但控制总量(文件 50K,Skills 25K),不会让上下文再次爆掉。

这是一个”压缩后恢复”的闭环设计,确保模型不会因为压缩而丢失工作记忆。

2.4 Layer 4: PTL Retry — 最后的保险机制

即使是摘要请求本身,也可能触发 prompt_too_long。

这种情况通常发生在极端场景:对话历史太长,连摘要 agent 都塞不下。

Claude Code 的兜底策略是:从头部砍掉最旧的消息组,砍到够为止

规则:

  • 每次最多砍 20%
  • 最多重试 3 次
  • 砍的单位是”消息组”(user + assistant 成对删除),不会留下孤立的消息

这是最后的保险机制,确保即使在极端情况下(比如用户一次性粘贴了几十万 token 的日志),对话也能继续。


三、Proactive 模式:从”被动响应”到”主动执行”

普通的 Claude Code 是”被动响应”的:你输入,它回复。

但当激活 PROACTIVE 或 KAIROS 模式时,会切换到一套完全不同的 System Prompt,让 Agent 变成”自主工作”模式。

3.1 终端焦点感知

这是 Proactive 模式中最前瞻的设计。Agent 通过 terminalFocus 字段知道用户是否在看终端:

Unfocused(用户离开):

  • 进入自主模式
  • 主动做决定
  • 直接提交代码
  • 不需要等待确认

Focused(用户在看):

  • 进入协作模式
  • 先问再做
  • 等待用户反馈
  • 保持交互式对话

这是一个非常聪明的设计:Agent 的自主程度不是固定的,而是根据用户的注意力动态调整。当你专注工作时,Agent 安静地在后台干活;当你回来看终端时,Agent 立刻切换到协作模式,向你汇报进展并征求意见。

3.2 Token Budget:用 token 量驱动的自主工作

用户可以指定 token 预算,Agent 会持续工作直到接近预算:

"+500k"500,000 tokens
"+2m"2,000,000 tokens
"use 1b tokens"1,000,000,000 tokens

闭环机制:

用户指定预算
    → agent 持续工作
    → 每轮检查进度
    → 接近 90%?停止,报告完成
    → 收益递减?停止(连续 3 轮增量 <500 token)
    → 未到 90%?注入 nudge 继续工作

关键设计:

  • System prompt 中的 token_budget section 是缓存的
  • 每轮对话结束后自动注入 nudge 消息
  • 收益递减检测:连续 3 轮以上,且最近两轮增量都 <500 token,自动停止

这是一个用 token 量驱动的自主工作模式 — 用户说”花 500K token”,Agent 就一直干到花完为止。


四、多 Agent 协作体系

Claude Code 的多 Agent 体系不是简单的”多开几个进程”,而是一套完整的分布式协作基础设施。

4.1 三种协作模式

1. Fork:后台执行,不污染主对话

创建一个”分身”,继承你的完整上下文(对话历史 + system prompt + 工具池),在后台干活。结果出来后,只把最终结论告诉你,中间过程不污染主对话。

关键设计:Cache 继承

Fork 不重新生成 system prompt,而是直接复制父级已经渲染好的字节

为什么?因为如果重新生成,可能会触发 Feature Flag 的重新计算。直接复制父级的字节,确保 Fork 和父级的 system prompt 完全一致,缓存继续命中。

2. Subagent:专家顾问,独立上下文

创建一个”专家顾问”,有自己的 system prompt、工具池、对话历史。它不是父级的”分身”,而是一个独立的专家。

内置的专家类型:

  • explore — 代码库探索、深度研究
  • verification — 对抗性验证(只在内部版本开放)
  • 自定义 agent — 用户可以定义自己的专家

3. Swarm:持久化团队,分布式协作

创建一个”团队”,每个成员是独立的 Agent,但通过邮箱系统通信。

核心创新:分布式权限管理

Swarm 最精妙的设计是权限冒泡

假设你有 4 个 Worker Agent 在并行工作。它们都需要删除文件、修改配置。如果每个 Agent 都在自己的终端弹出权限对话框,你需要在多个 pane 之间来回切换审批。

Swarm 的解法:

所有 Worker 的权限请求都通过邮箱系统发给 Leader,Leader 在终端展示统一的权限对话框。你只需要盯着 Leader 的终端,所有权限请求都会在那里出现。

Worker 遇到权限提示
    ↓
发送 permission_request 到 Leader 邮箱
    ↓
Leader 在终端展示对话框(用户审批)
    ↓
Leader 发送 permission_response 到 Worker 邮箱
    ↓
Worker 收到响应,继续或中止

这是一个分布式权限系统 — 多个 Agent 的权限请求全部汇总到 Leader 终端,用户不需要在多个 pane 之间切换。


五、自我进化记忆系统

Claude Code 有一个隐藏的”反思”机制:每 24 小时 + 累积 5 个会话后,自动 fork 一个后台 agent 做”反思”。

5.1 四层记忆体系

1. Auto Memory(自动记忆)

  • 路径:~/.claude/projects/<path>/memory/
  • 触发:每轮对话结束自动写入
  • 机制:extractMemories 实时提取
  • 特点:短期工作记忆,快速访问

2. Team Memory(团队记忆)

  • 路径:auto memory 的子目录
  • 作用:团队共享的知识库
  • 特点:跨用户共享,协作场景

3. Agent Memory(Agent 专属记忆)

  • 路径:自改进 agent 的专属目录
  • 作用:Agent 自己的长期知识库
  • 特点:配合 autoDream 定期整合

5.2 从短期到长期的转化

实时提取(extractMemories)
    ↓
短期工作记忆(Auto Memory)
    ↓
定期整合(autoDream)
    ↓
长期知识库(Agent Memory

这套体系让 Agent 不仅能”记住”,还能”学习”和”进化”。


六、工业级遥测体系

Claude Code 的遥测不是”能关就关”,而是有三条并行的数据通道,每条都有不同的目的。

6.1 三条数据通道

通道 1:API Header(每次必发,无法关闭)

每次 API 请求都会带上一个 x-anthropic-billing-header,注入到 system prompt 的第一块:

cc_version=1.0.0; cc_entrypoint=cli; cch=00000; cc_workload=cron

通道 2:1P Event Logging → BigQuery(主通道,可关闭)

这是最核心的遥测通道,包含五层递进的元数据收集。

通道 3:Datadog(白名单事件,可关闭)

只有 30+ 种特定事件会发到 Datadog:API 成功/失败、OAuth 流程、工具使用授权/拒绝、Compact 失败、模型 fallback、未捕获异常。

6.2 五层元数据

Layer 1: Core Metadata — 你在用什么功能

  • model、sessionId、userType、entrypoint、isInteractive
  • agentId / parentSessionId / agentType / teamName — 多 Agent 追踪
  • subscriptionType、rh(仓库指纹)

Layer 2: Env Context — 你的开发环境

  • platform / arch、terminal(20+ 种检测)
  • packageManagers、runtimes
  • deploymentEnvironment(30+ 种云平台)
  • linuxDistroId / linuxKernel

Layer 3: Process Metrics — 你的资源使用

  • uptime、rss / heapTotal / heapUsed
  • cpuUsage / cpuPercent

Layer 4: User Metadata — 你是谁

  • deviceId — randomBytes(32).hex,存在 ~/.claude/.config.json,终身不变
  • email、accountUuid / organizationUuid
  • subscriptionType / rateLimitTier

Layer 5: PII 特权字段 — 只走 BigQuery

  • skill_name、plugin_name、marketplace_name
  • 这些字段在发送到 Datadog 前会被清除,确保 PII 不会泄露

七、安全分层:20+ 道检查流水线

Bash 安全分类器(2592 行代码)不是简单的”黑名单”,而是一个 20+ 道安全检查的流水线。

核心检查:

  1. Shell 元字符检测 — $() “ {} <> 等
  2. 命令替换模式 — <() >() =() $( 等
  3. 危险变量检测 — $IFS、$PATH 等
  4. 输入/输出重定向检测
  5. 花括号展开检测 — {a, b, c} 绕过参数检查
  6. 反斜杠转义运算符 — ; | & 等
  7. 引号反同步 — “x”y” ; echo ~/.ssh/id_rsa
  8. 控制字符 + Unicode 空白字符
  9. jq 系统函数检测 — system()/exec/…
  10. /proc 文件系统访问 — /proc/self/environ 等

Zsh 专属防御:

Zsh 的攻击面比 Bash 大,源码专门维护了 ZSH_DANGEROUS_COMMANDS 集合:

  • zmodload → 加载危险模块
  • emulate → eval 等价物
  • zpty → 伪终端执行命令
  • ztcp → TCP 连接外泄数据
  • zf_rm → 绕过 rm 权限检查

八、设计哲学:Cache 感知 + 基础设施感知

Claude Code 的每个设计决策都在考虑两件事:

8.1 Cache 感知

  • Fork 直接复制父级渲染后的 system prompt 字节,避免 Feature Flag 冷→热切换导致缓存失效
  • Agent 根据缓存过期时间(5 分钟 TTL)决定 sleep 时长
  • 连 Agent 的创建方式都要考虑缓存命中率

8.2 基础设施感知

  • Agent 感知缓存 TTL、token 预算、终端焦点状态
  • 据此调整行为节奏,避免无效唤醒
  • 让 Agent 理解基础设施的约束,而不是盲目执行

九、对 AI 开发者的启示

9.1 分层思维

不要试图用单一方案解决所有问题。设计分层系统,便宜方案处理常见情况,昂贵方案处理边缘情况。

9.2 预防优于治疗

Claude Code 的设计哲学:预防上下文溢出比溢出后修复更便宜。Layer 1-3 都是预防措施。

9.3 增量维护

持续维护 session memory 比紧急总结更高效。这类似于数据库的增量备份 vs 全量备份。

9.4 Cache 是第一公民

在 LLM 应用中,prompt cache 应作为核心设计考虑,而非事后优化。

9.5 后台处理

“梦境”系统展示了后台 consolidation 的价值——用户离开时做繁重工作,用户返回时系统已准备好。


结语

Claude Code 的核心竞争力不是单个功能,而是这套缓存架构 + 自我进化记忆 + 多模式编排的完整基础设施。

特别是 cache_edits 和分层缓存,是让无限上下文对话在商业上可行的关键。没有这套机制,长对话的成本会让产品无法规模化。

这才是工业级 AI Agent 的设计哲学。


本文基于公开泄露的 Claude Code CLI 源码分析,不代表 Anthropic 官方立场。

       


       

         

感谢阅读!我是 Rowan,。如果本文对你有所启发或帮助,不妨 **推荐、分享,并关注我** ❤️。你的每一次互动,都是我持续分享优质内容的强大动力。

         

💬 欢迎在评论区留言,分享你的想法或遇到问题~