Claude Code 深度拆解:上下文里有什么——Prompt Cache 机制

Hi,大家好,欢迎来到维元码簿。

本文属于 《Claude Code 源码 Deep Dive》 系列,专注于上下文工程中的 Prompt Cache 板块。如果你想了解整个系列,可以先看系列开篇 | Claude Code 源码架构概览:51万行代码的模块地图

本文聚焦一件事:缓存系统作为一个完整的工程闭环------怎么检测断裂、怎么修复、怎么在压缩和缓存之间找到平衡。

读完全文,你将了解缓存从建立到降级的完整生命周期:

  • 缓存是什么?为什么一次对话能省 10 倍成本?
  • 怎么建立缓存?三层标记 + 四大保障怎么保持前缀稳定?
  • 缓存断了怎么发现?省钱不破坏缓存的 cache_edits 怎么工作?
  • 缓存冷了怎么办?

缓存是什么?为什么一次对话能省 10 倍成本?

前三篇姊妹篇讲了上下文的三大板块------System Prompt、Messages、Tools。这篇讲"怎么省着用"。

Prompt Caching 是 Anthropic API 提供的 KV cache 机制:如果连续请求的 prompt 前缀相同,可以跳过前缀的处理,直接从缓存读取。Claude Code 的一个典型 System Prompt 约 10K-15K tokens,一次对话可能有 50-100 个 turn。没有缓存时,每个 turn 都重新处理整个 System Prompt,总成本是 15K × 100 = 150 万 tokens 。有了缓存,只有第一个 turn 处理完整 System Prompt,后续 turn 只处理变化部分,成本降到原来的 10-20%

但缓存不是客户端单方面能完成的------它是一个两端协作的工程:客户端负责"在哪里标记缓存断点",服务端负责"在哪里执行 KV cache 读写"。

但有一个关键约束:缓存是"前缀匹配"------一旦前缀发生变化(哪怕一个字符),从变化点开始的所有缓存全部失效。这就是为什么 Claude Code 花了那么多精力来保持前缀稳定。

上图展示了 KV cache 的工作原理:前缀相同时(绿色部分)直接复用缓存的 KV 向量,只计算新增部分(橙色部分)。一旦前缀变化(红色部分),从变化点开始的缓存全部失效,需要重新计算。

怎么建立缓存?三层缓存标记

Claude Code 在 prompt 的三个位置标记缓存断点,由 splitSysPromptPrefix()src/utils/api.ts)处理:

System Prompt 缓存 :设计目标很明确------尽可能让静态区命中最高级别的缓存共享,同时避免不同用户的动态内容互相干扰。

核心思路一句话:能共享就共享,不能共享就降级。

  • 有 MCP 工具时------全部降级为 org。不同用户配置的 MCP 工具不同,工具 Schema 会影响 System Prompt 内容,全局缓存不可用,所有内容降级为组织级
  • 无 MCP 工具 + 有 boundary marker------静态区命中 global。7 个静态模块对所有用户完全相同,可以跨组织、跨用户共享缓存
  • 第三方 API 提供商------回退到 org。Bedrock、Vertex 等平台可能不支持全局缓存

Messages 缓存 :在每条消息上标记一个 ephemeral 断点。新消息追加时,之前的消息从缓存读取------每轮只处理新增的那一条。

Tools 缓存 :工具 Schema 通过会话级缓存锁定(toolSchemaCache.ts,27 行),即使 feature flag 中途变化也不影响序列化结果。

怎么保持缓存?四大稳定性保障

保持前缀稳定是缓存命中的前提。Claude Code 实现了四个机制,核心逻辑一致:会话内一旦确定,不再变化。

保障机制 保护对象 核心思路
Section Memoization 动态区模块 计算一次,缓存到 /clear/compact
Tool Schema Cache 工具 JSON Schema 会话级锁定,feature flag 变化不影响
Beta Header 锁存 API 请求头 首次发送后锁定,防止缓存策略中途切换
MCP Instructions Delta MCP 使用说明 从"每 turn 重算"改为"仅在连接/断开时注入增量"

其中 MCP Instructions Delta 的收益最直观:50 个 turn 的会话,只有 2 次 MCP 变化。如果用 DANGEROUS_uncached,50 次重写 × 10K tokens = 500K tokens 浪费;改为 delta 后,只有 2 次注入,成本接近零。

缓存还有两种 TTL:5min (默认)和 1h (长间隔对话)。should1hCacheTTL() 通过两层关卡(用户资格 + 来源白名单)决定是否使用 1h TTL。关键决策:资格在首次判断后锁存 到会话状态,即使中途超量也不降级------因为 TTL 切换会改变 cache_control 字段,导致前缀变化,整个缓存失效。锁存策略避免了"好心办坏事"。

缓存断了怎么办?两阶段断裂检测

缓存断裂的直接后果是:这一轮响应变慢、API 费用突增。但断裂的原因可能有很多------system prompt 变了、工具 Schema 变了、模型换了、TTL 过期了......如果只看到"cache_read_tokens 骤降",无从排查。

Claude Code 实现了一个精密的两阶段检测系统src/services/api/promptCacheBreakDetection.ts,651 行),核心思路:调用前拍快照、调用后对账、归因到具体维度。

Phase 1:预调用状态快照。 每次 API 调用前,recordPromptState() 记录 12 个可能影响缓存前缀的维度(systemHash、toolsHash、model、betas、cacheControlHash 等)。如果调用前发现某个维度变了,就记录到 pendingChanges------但此时还不能确认缓存真的断裂了。一个重要细节:defer_loading 的工具被排除在哈希计算之外,避免 MCP 工具延迟加载状态变化制造"假阳性"告警。

Phase 2:后调用 token 分析。 API 返回后,checkResponseForCacheBreak() 检查 cache_read_input_tokens 是否骤降(下降 > 5% 且绝对下降 > 2,000 tokens)。如果两个条件都满足,用 Phase 1 的 pendingChanges 来归因。如果 12 个维度都没变但缓存还是断了,系统根据时间间隔推断原因(> 1h → TTL 过期,< 5min → 服务端路由/驱逐)。

这个检测系统的价值不仅在于排查,更在于迭代。 团队可以量化每次变更对缓存命中率的影响:MCP Delta 改造后断裂率降低了多少,Beta Header 锁存上线后"betas changed"类断裂是否归零。

怎么省钱不破坏缓存?cache_edits

对话越久,旧的工具结果(Read 文件内容、Grep 搜索结果等)越占空间。传统的"微压缩"是在本地删除旧工具结果,但这会修改消息数组,导致 prompt 前缀变化,缓存全部失效------省下的 token 还没补回来,缓存失效增加的成本就已经抵消了收益。

Claude Code 用了一个完全不同的思路:cache_edits------不在本地修改消息,而是通过 API 的 Cache Editing 功能在服务端透明删除。本地消息数组不变,prompt 前缀完全一致,缓存完美命中。

完整生命周期分五步:

  1. 注册cachedMicrocompactPath() 扫描消息中所有 tool_result,把可压缩工具(Read、Edit、Write、Glob、Grep)的 tool_use_id 注册到 cachedMCState
  2. 决策getToolResultsToDelete() 根据配置(keepRecent 保留最近 N 个,triggerThreshold 超过阈值才触发)决定哪些需要删除
  3. 构建createCacheEditsBlock() 生成 JSON 结构,每个 cache_reference 对应一个待删除的 tool_use_id
  4. 插入addCacheBreakpoints() 从末尾往前找到最后一条 role: 'user' 的消息,把 cache_edits block 插入到它的 content 数组中
  5. Pin/Replay :cache_edits 不是"发一次就永久生效"------它是每 turn 都需要重新发送的指令 。因为服务端 KV cache 是"前缀匹配",下一 turn 不包含同样的 block,服务端会认为前缀不同,缓存失效。getPinnedCacheEdits() 在每 turn 回放之前所有 pin 的 edits

为了让服务端知道哪些 tool_result 可以被删除,位于 cache_control 标记之前的所有 tool_result block 都会被添加一个 cache_reference 字段,值等于 tool_use_id

缓存冷了怎么办?双路径压缩与 Compact 复用

Microcompact(微压缩)有两条路径,选择哪条取决于缓存状态:

缓存热路径(距离上次调用时间短,服务端缓存仍然有效):走 cache-editing 路径。不修改本地消息,通过 cache_edits 在服务端透明删除------"无损"操作。

缓存冷路径 (距离上次调用时间超过阈值,服务端缓存已过期):maybeTimeBasedMicrocompact() 改为直接在本地替换旧工具结果的内容。缓存已经失效了,修改消息不会再造成额外损失。

这个设计体现了"因地制宜"的工程原则:缓存热的时候保护缓存,缓存冷的时候直接改。

Compact 的缓存复用 遵循同样的思路。Compact(完整压缩)需要调用模型生成摘要,这是一次额外的 API 调用。Claude Code 让 Compact 通过 runForkedAgent() 复用主线程的缓存身份 ,传入 cacheSafeParams(包含了主线程最近一次调用的 system prompt、user context、messages 前缀)。Fork 的请求和主线程共享相同的缓存前缀,可以命中 KV cache。

一个微妙的设计决策:Compact fork 不设置 maxOutputTokens------因为设置后会通过 Math.min(budget, maxOutputTokens-1) 影响 thinking config,导致与主线程的缓存不匹配。

总结

跟着缓存的"人生"走了一遍,我们把本文的核心机制串起来:

阶段 机制 核心设计
建立 三层缓存标记 能共享就共享(global),不能共享就降级(org/ephemeral)
保持 四大稳定性保障 + TTL 锁存 会话内一旦确定,不再变化
检测 两阶段断裂检测 12 维度预追踪 + token 骤降归因
修复 cache_edits pin/replay 不动本地消息,服务端透明删除,每 turn 重发
降级 双路径微压缩 缓存热走 editing,缓存冷走本地替换
复用 Compact 缓存复用 fork 共享 cacheSafeParams,"蹭"主线程缓存

每个机制都不是孤立存在的------TTL 锁存防止策略切换导致断裂,cache_reference 为 cache_edits 提供定位依据,双路径切换依赖时间间隔判断缓存冷热,Compact 复用要求 maxOutputTokens 不干扰 thinking config。

缓存工程的终极目标不是"让缓存永远命中",而是"让缓存尽可能多地命中,命中不了时有优雅的降级策略"。

如果你读到这里觉得缓存这块比较难懂------这是正常的。缓存机制不像 System Prompt 或 Messages 那样集中在一个模块,它散布在整个子系统的各个角落:组装时标记缓存断点、调用前拍快照、返回后对账、构建请求时插入 cache_edits、Compact 时复用缓存身份......每一个环节都和上下文的其他板块(System Prompt、Messages、Tools)紧密耦合。

建议搭配姊妹篇一起读:先看 [System Prompt 工程](./02-Claude Code深度拆解-上下文里有什么-System Prompt工程.md) 和 [消息上下文管理](./02-Claude Code深度拆解-上下文里有什么-消息上下文管理.md) 了解缓存断点是怎么标记的,再看本文,会有更完整的理解。


系列导航

本文属于 《Claude Code 源码 Deep Dive》 系列中「上下文组成与缓存」命题的子篇章,专注于上下文缓存的工程闭环

本文是完整版《Claude Code 源码深度解析:拆解上下文的组成与缓存》的子命题之一。如果你想了解上下文编排的全景(System Prompt + Messages + Tools + 缓存工程),推荐阅读完整版。

姊妹篇(可独立阅读):


如果这篇文章对你有帮助,欢迎点赞收藏 支持一下。如果你对 Claude Code 源码感兴趣,欢迎关注本系列 后续更新。有任何想法或疑问,欢迎评论区留言讨论 👋

相关推荐
维元码簿2 小时前
Claude Code 深度拆解:上下文里有什么——System Prompt 工程
ai·agent·claude code·ai coding
xiami_world2 小时前
Claude Design vs. 博思AIPPT深度对比:从架构、交互、数据处理看垂直AI PPT工具的优势
人工智能·ai·信息可视化·powerpoint·思维导图·ppt
刘佬GEO2 小时前
GEO 效果看什么指标:从提及、引用到推荐的判断框架
前端·网络·人工智能·搜索引擎·ai
张忠琳11 小时前
【vllm】vLLM v1 KV Offload — 模块超深度逐行分析之一(七)
ai·架构·vllm
j_xxx404_11 小时前
大语言模型 (LLM) 零基础入门:核心原理、训练机制与能力全解
人工智能·ai·transformer
汪汪88888811 小时前
测试智能平台落地实践:五大核心能力破解传统测试痛点
ai
张忠琳13 小时前
【vllm】vLLM v1 Attention — 系统级架构深度分析(五)
ai·架构·vllm
薛定谔的猫36913 小时前
探索 MCP 协议:构建跨平台的 Agent 自动化生态
ai·自动化·agent·技术趋势·mcp
DavidSoCool13 小时前
win11系统使用DockerDesktop搭建Dify实现知识库问答
ai·大模型·知识库·dify