Skip to content
源码分析手册

Prompt Caching:一句话概括

Claude Code 并不是简单地存储对话记录,而是通过对 Anthropic API cache_control 参数的精密编排,将系统提示词、工具定义和对话历史动态地“钉”在模型服务器的 KV 缓存中,实现毫秒级的首字响应和极低的 Token 重复开销。

核心概念

在 Claude Code 的语境下,Prompt Caching 不是客户端的磁盘缓存,也不是简单的字符串匹配。

它本质上是一种服务端状态保持机制。你可以把它想象成在远程服务器上维护了一个“上下文热分区”。当你发送一个长达 30k tokens 的项目上下文时,Claude Code 通过 cache_control 标记告诉服务端:“这一段内容在接下来的几分钟甚至一小时内大概率还会被用到,请不要把它从内存中踢出去。”

它不是一种持久化的数据库存储,而是一种带有 TTL(生存时间)的、基于计算前缀匹配的临时驻留。如果你的下一次请求前缀与缓存内容一致,API 就会直接“复用”这部分计算结果,而不是重新处理一遍。

代码里的真实逻辑

Claude Code 对 Prompt Caching 的实现核心在于 src/services/api/claude.tssrc/services/api/promptCacheBreakDetection.ts 两个文件。其逻辑流程可以归纳为以下三个层面:

1. 系统提示词的“动静分离”

splitSysPromptPrefix 函数中,Claude Code 会检查系统提示词中是否存在 SYSTEM_PROMPT_DYNAMIC_BOUNDARY(动态边界标识符)。

  • 静态部分:通常包含 CLI 的基础指令、核心规则。这部分被赋予 cache_scope: 'global'org,在多个会话间甚至是跨组织共享。
  • 动态部分:包含当前文件内容、临时上下文。这部分不设缓存或采用更窄的范围。 这种设计确保了最通用的指令永远在缓存中“预热”,而不会因为你打开了一个新文件就导致整个系统提示词缓存失效。

2. 对话历史的“端点推进”

addCacheBreakpoints 函数中,Claude Code 会动态地向消息流中插入 cache_control 断点。

  • 常规模式:默认在消息列表的最后一条消息打桩。这样每次对话结束后,整个历史记录都会被标记为可缓存。
  • Fork 模式(skipCacheWrite):当 Claude Code 进行后台预测、总结或子任务时,它会设置 skipCacheWrite: true。此时,断点会被前移到倒数第二条消息。这样做能利用已有的历史缓存,但不会让这些临时生成的预测内容“污染”主对话的 KV 缓存。

3. TTL(生存时间)的智能升级

getCacheControl 中,Claude Code 会根据用户身份和 GrowthBook 配置决定缓存时长。

  • 5 分钟:默认时长。
  • 1 小时:针对高级用户或特定任务源(如 repl_main_thread)。 通过延长 TTL,Claude Code 保证了你在长达一小时的深入开发过程中,不需要反复支付庞大上下文的“入场券”Token 开销。

踩坑指南

Prompt Caching 极其强大,但它不是万能的,以下是你必须了解的物理限制:

  1. 断点限额:Anthropic API 限制单个请求最多只能有 4 个 cache_control 标记。Claude Code 通常极其节省,只在系统提示词和消息末尾使用 1-2 个,以确保最高的命中率。
  2. 前缀强一致性:缓存是基于前缀匹配的。如果你修改了系统提示词中间的一个字,或者在工具定义里多加了一个空格,该断点之后的所有缓存都会立即失效(即 Cache Break)。
  3. Token 门槛:Claude Code 的缓存失效监测(PromptCacheBreakDetection)通常只在 Token 波动超过 2000 时才会发出警告。对于微小的上下文变动,它倾向于保持沉默。
  4. Microcompact 手术式剔除:Claude Code 内部有一个 CachedMCEdits 机制。当上下文太长需要压缩时,它不会直接清空缓存,而是通过 cache_reference 指向特定的工具输出,进行精准的局部剔除。

接下来看什么

  • 阅读 src/services/api/promptCacheBreakDetection.ts:了解 Claude Code 是如何通过哈希算法(djb2 或 Bun.hash)来追踪每一个导致缓存失效的微小因素(如 Beta 头部变化、工具定义修改)。
  • 研究 src/services/api/claude.ts 中的 addCacheBreakpoints:这是操纵 cache_control 插入位置的最高机密。
  • 查阅官方 Prompt Caching 文档:理解 API 层面对 ephemeral 类型的计费差异和性能基准。

源码锚点

  • claude-code-opensource/src/services/api/claude.ts: 定义了 cache_control 的生成逻辑(getCacheControl)和消息断点设置。
📄 src/services/api/claude.ts — 定义了 `cache_control` 的生成逻辑(`getCacheControl`)和消息断点设置。L603-603 of 3420
typescript
              cache_control: getCacheControl({ querySource }),
  • claude-code-opensource/src/services/api/promptCacheBreakDetection.ts: 负责监测并记录缓存命中失败的原因,是性能调试的关键。
📄 src/services/api/promptCacheBreakDetection.ts — 负责监测并记录缓存命中失败的原因,是性能调试的关键。L19-26 of 728
typescript
function getCacheBreakDiffPath(): string {
  const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
  let suffix = ''
  for (let i = 0; i < 4; i++) {
    suffix += chars[Math.floor(Math.random() * chars.length)]
  }
  return join(getClaudeTempDir(), `cache-break-${suffix}.diff`)
}
  • claude-code-opensource/src/utils/api.ts: 包含 splitSysPromptPrefix,揭示了系统提示词如何进行动静分离。
📄 src/utils/api.ts — 包含 `splitSysPromptPrefix`,揭示了系统提示词如何进行动静分离。L321-323 of 719
typescript
export function splitSysPromptPrefix(
  systemPrompt: SystemPrompt,
  options?: { skipGlobalCacheForSystemPrompt?: boolean },

基于 Claude Code v2.1.88 开源快照的深度分析