Skip to content

A10: Prompt Caching(提示缓存) ​

相关章节:第 3 章 上下文窗口管理、第 13 章 Agent SDK、A07 Token 经济学

Prompt Caching(提示缓存)是一种优化机制,让 API 跳过对已经处理过的 Token 的重新处理。当你的提示开头与最近缓存的提示匹配时,这些 Token 以 90% 的折扣从缓存中提供,并且延迟显著降低。对于 Claude Code 而言,系统提示和 CLAUDE.md 内容在每个回合中都相同,因此缓存自动带来了可观的节省。

Prompt Caching 的工作原理 ​

前缀匹配 ​

Prompt Caching 使用精确前缀匹配。API 将当前提示的开头与最近缓存的提示进行比较。如果一个连续的前缀完全匹配(逐字节),这些 Token 就从缓存中提供。

Turn 1 (cache miss — nothing cached yet):
┌─────────────────────────────────────────────────┐
│ System prompt       [3,000 tokens]  PROCESSED   │
│ CLAUDE.md           [2,000 tokens]  PROCESSED   │
│ Tool definitions    [1,500 tokens]  PROCESSED   │
│ User message        [  200 tokens]  PROCESSED   │
└─────────────────────────────────────────────────┘
  Total: 6,700 tokens processed at full price
  Cache: prefix of 6,500 tokens stored

Turn 2 (cache hit on prefix):
┌─────────────────────────────────────────────────┐
│ System prompt       [3,000 tokens]  CACHED ✓    │
│ CLAUDE.md           [2,000 tokens]  CACHED ✓    │
│ Tool definitions    [1,500 tokens]  CACHED ✓    │
│ Turn 1 history      [  600 tokens]  PROCESSED   │
│ User message        [  150 tokens]  PROCESSED   │
└─────────────────────────────────────────────────┘
  Cached: 6,500 tokens at 90% discount
  Processed: 750 tokens at full price

关键洞察:缓存仅对前缀有效。如果前缀中间的任何字节不同,缓存就在该点断开,之后的所有内容都按全价处理。

缓存了什么 ​

缓存存储的是匹配前缀的中间计算状态(内部的键值注意力缓存)。这意味着模型不需要重新对这些 Token 进行注意力计算——它可以直接跳到处理新的 Token。

Without caching:
  Prompt [A B C D E] → Process A, then B, then C, then D, then E

With caching (prefix A B C is cached):
  Prompt [A B C D E] → Load cached state for A B C, process only D and E

最小前缀长度 ​

缓存需要最小前缀长度才能生效。Anthropic 要求缓存前缀至少需要 1,024 个 Token 才能创建缓存条目。低于此阈值的短提示不会从缓存中受益。

在实践中,Claude Code 会话总是超过此阈值,因为仅系统提示就约有 3,000 个 Token。

缓存命中的条件 ​

要发生缓存命中,以下条件必须完全匹配:

1. 相同的模型 ​

缓存条目是按模型区分的。为 claude-sonnet-4-20250514 缓存的提示不会在 claude-opus-4-20250514 上产生命中。

2. 精确的前缀字节 ​

前缀中的每个字节都必须匹配。这包括:

  • 系统提示内容
  • CLAUDE.md 内容(任何编辑都会使缓存失效)
  • 工具定义(如果工具变化,缓存失效)
  • 消息排序和内容
  • 空白、换行和格式
# These are DIFFERENT prefixes (no cache hit):

"System: You are a helpful assistant."    # period
"System: You are a helpful assistant"     # no period

"Use pnpm for packages"                  # original
"Use pnpm  for packages"                 # extra space (!)

3. 相同的 API 参数(某些字段) ​

部分 API 参数是缓存键的一部分。如果你更改 model,缓存会失效。像 max_tokens 和 temperature 这样的参数不影响缓存键。

什么会使缓存失效 ​

导致缓存前缀失效的常见情况:

Cache-breaking changes:
├── Editing CLAUDE.md (any character change)
├── Adding/removing MCP servers (changes tool definitions)
├── Switching models mid-session
├── Different system prompt versions (Claude Code updates)
└── Modifying the message history (editing a past message)

Non-breaking changes (cache preserved):
├── New user messages (appended to the end)
├── New assistant messages (appended to the end)
├── Tool results (appended to the end)
├── Changing max_tokens
└── Changing temperature

对延迟和成本的影响 ​

首个 Token 时间(TTFT) ​

缓存显著减少了首个 Token 时间,因为模型跳过了对缓存前缀的处理。改善幅度随前缀长度增加:

Prefix size    │  TTFT (no cache)  │  TTFT (cached)  │  Speedup
──────────────┼──────────────────┼────────────────┼──────────
5,000 tokens   │  ~1.2 seconds    │  ~0.3 seconds  │  4x
20,000 tokens  │  ~3.5 seconds    │  ~0.5 seconds  │  7x
50,000 tokens  │  ~7.0 seconds    │  ~0.8 seconds  │  9x
100,000 tokens │  ~12.0 seconds   │  ~1.0 seconds  │  12x

对于对话历史增长到 50K+ Token 的长 Claude Code 会话,缓存使得工具的响应感觉很灵敏,而不是迟缓。

成本降低 ​

缓存的输入 Token 定价为标准输入费率的 10%:

模型标准输入(每 1M)缓存输入(每 1M)节省
Claude Opus 4$15.00$1.5090%
Claude Sonnet 4$3.00$0.3090%
Claude Haiku 3.5$0.80$0.0890%

注意:创建新缓存条目时还有少量缓存写入成本(首次请求有 25% 的附加费)。这在后续缓存命中中被摊销。

Example: 20-turn session with 5,000-token stable prefix (Sonnet)

Without caching:
  20 turns × 5,000 prefix tokens × $3.00/1M = $0.30

With caching:
  Turn 1 (cache write): 5,000 × $3.75/1M = $0.01875
  Turns 2-20 (cache hit): 19 × 5,000 × $0.30/1M = $0.0285
  Total: $0.047  (84% savings on the prefix)

5 分钟 TTL ​

工作方式 ​

缓存条目有 5 分钟的生存时间(TTL)。如果 5 分钟内没有匹配的请求,缓存条目过期,下一个请求将是缓存未命中(全价处理 + 缓存写入)。

Timeline:
  0:00  Request 1 → Cache miss (cache created)
  0:30  Request 2 → Cache hit ✓ (TTL resets to 5 min)
  1:00  Request 3 → Cache hit ✓ (TTL resets)
  
  ... 6 minutes of inactivity ...
  
  7:00  Request 4 → Cache miss (expired, new cache created)
  7:15  Request 5 → Cache hit ✓ (TTL resets)

每次缓存命中都会重置 TTL。只要你在 5 分钟内持续发送请求,缓存就保持活跃。

对 Claude Code 使用的影响 ​

  • 活跃会话受益最大:如果你正在积极编码(每隔几分钟发送提示),缓存在整个会话期间保持活跃。
  • 超过 5 分钟的休息会导致下一个请求的缓存未命中。休息后的第一个请求更慢更贵,但后续请求又会受益。
  • 闲置会话:如果你去吃午饭,预期回来后的第一个请求是缓存未命中。
  • CI/CD 流水线:如果流水线运行间隔超过 5 分钟,每次运行都是冷启动。对于频繁的流水线(例如每次提交都运行),缓存有显著帮助。

保持缓存活跃的策略 ​

对于缓存活跃度很重要的场景(例如共享的基于 API 的工具):

python
# Ping approach: send a minimal request to refresh the TTL
# (Only useful for API integrations, not for Claude Code CLI)
import time
import threading

def keep_cache_warm(client, system_prompt, interval=240):
    """Send a minimal request every 4 minutes to keep cache alive."""
    def ping():
        while True:
            client.messages.create(
                model="claude-sonnet-4-20250514",
                max_tokens=1,
                system=system_prompt,
                messages=[{"role": "user", "content": "ping"}]
            )
            time.sleep(interval)
    thread = threading.Thread(target=ping, daemon=True)
    thread.start()

这在交互式 Claude Code 使用中很少需要,但对于系统提示很大且重新处理成本高的基于 API 的集成可能很有价值。

如何构建提示以最大化缓存命中 ​

原则:稳定内容在前,可变内容在后 ​

由于缓存基于前缀工作,请将最稳定的内容放在上下文的开头:

Optimal ordering (maximizes cache prefix):
┌────────────────────────────────────────┐
│ 1. System prompt        (never changes) │  ← Always cached
│ 2. CLAUDE.md            (rarely changes)│  ← Almost always cached
│ 3. Tool definitions     (rarely changes)│  ← Almost always cached
│ 4. Conversation history (grows linearly)│  ← Partially cached
│ 5. Current user message (always new)    │  ← Never cached
└────────────────────────────────────────┘

Poor ordering (breaks cache early):
┌────────────────────────────────────────┐
│ 1. Current timestamp    (always changes)│  ← Breaks cache!
│ 2. System prompt                        │  ← Not cached
│ 3. Everything else                      │  ← Not cached
└────────────────────────────────────────┘

Claude Code 已经按照这种方式构建其提示。你会自动受益。

CLAUDE.md 的稳定性很重要 ​

由于 CLAUDE.md 是缓存前缀的一部分,在会话中途编辑它会使缓存失效:

Session timeline:
  Turn 1-5:  Cache building, prefix grows, cost decreasing
  Turn 6:    User edits CLAUDE.md (adds one line)
  Turn 7:    Cache MISS on the entire prefix (CLAUDE.md changed)
  Turn 8+:   New cache builds from the updated prefix

实用建议:在会话之间编辑 CLAUDE.md,而不是在会话期间。如果必须在会话中编辑,请尽早进行以减少缓存浪费。

对话历史与缓存 ​

随着对话增长,缓存前缀也随之增长:

Turn 1:  Prefix = system + CLAUDE.md + tools (5,000 tk) → miss
Turn 2:  Prefix = above + turn 1 (6,000 tk) → 5,000 tk cached
Turn 5:  Prefix = above + turns 2-4 (12,000 tk) → 11,000 tk cached
Turn 10: Prefix = above + turns 5-9 (25,000 tk) → 24,000 tk cached
Turn 20: Prefix = above + turns 10-19 (50,000 tk) → 49,000 tk cached

更长的会话意味着更多的缓存命中,因为不断增长的对话历史始终是下一个请求的前缀。

然而,当 Claude Code 执行压缩(摘要化旧消息以适应上下文窗口)时,压缩后的内容与原始内容不同,会使被压缩部分的缓存失效。这是上下文窗口管理与缓存效率之间不可避免的权衡。

CLAUDE.md 与 Prompt Caching 的协同效应 ​

CLAUDE.md 非常适合 Prompt Caching,因为:

  1. 它被注入到提示的早期位置(系统提示前缀的一部分)
  2. 它在会话期间很少变化
  3. 它在所有回合中都完全相同
  4. 它应用于每个请求(没有条件性包含)

这意味着精心编写的 CLAUDE.md 在第一个回合之后实际上是"免费"的。一个 3,000 Token 的 CLAUDE.md 花费:

Turn 1 (cache write):  3,000 tokens × $3.75/1M = $0.011  (Sonnet)
Turn 2+ (cache read):  3,000 tokens × $0.30/1M = $0.0009 per turn

Over a 20-turn session:
  Without caching: 20 × 3,000 × $3.00/1M = $0.18
  With caching:    $0.011 + 19 × $0.0009  = $0.028
  Savings: 84%

不要为了"节省 Token"而缩减 CLAUDE.md 的内容。缓存系统意味着 CLAUDE.md 内容在第一个回合之后几乎是免费的。投入精力编写详尽的 CLAUDE.md——它通过更好的 Claude 行为来回报你,同时成本影响极小。

实际测量:检测缓存命中 ​

API 响应头 ​

直接使用 Anthropic API 时,缓存信息在响应中返回:

json
{
  "usage": {
    "input_tokens": 2500,
    "output_tokens": 800,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 6500
  }
}

关键字段:

  • cache_creation_input_tokens:写入缓存的 Token(首次请求或缓存未命中后)。按标准输入费率的 1.25 倍计费。
  • cache_read_input_tokens:从缓存提供的 Token。按标准输入费率的 0.1 倍计费。
  • input_tokens:正常处理的 Token(未缓存)。

计算你的缓存命中率 ​

python
# From API response usage data
def cache_hit_rate(usage):
    total_input = (
        usage["input_tokens"] + 
        usage["cache_creation_input_tokens"] + 
        usage["cache_read_input_tokens"]
    )
    if total_input == 0:
        return 0
    return usage["cache_read_input_tokens"] / total_input

# Example
usage = {
    "input_tokens": 2500,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 6500
}
print(f"Cache hit rate: {cache_hit_rate(usage):.1%}")  # 72.2%

良好缓存率的参考 ​

Scenario                          │  Expected Cache Hit Rate
──────────────────────────────────┼─────────────────────────
Active multi-turn session         │  60-85%
Long session (20+ turns)          │  75-90%
Session after 5+ min break        │  0% (first turn), then 60%+
CI/CD with frequent runs (<5 min) │  50-70%
CI/CD with infrequent runs        │  0-10%
First turn of any session         │  0% (always a miss)

Claude Code CLI 的观察 ​

在 Claude Code 中,你不会在 UI 中直接看到缓存指标。但你可以间接观察缓存效果:

  • 会话的第一个回合明显比后续回合慢
  • 编辑 CLAUDE.md 后,下一个回复更慢(缓存重建)
  • 长时间休息后,第一个回复更慢(缓存过期)
  • 状态栏中报告的 Token 用量反映的是有效的(缓存后)成本

与 Extended Thinking 的交互 ​

Extended Thinking(参见 A09)生成输出 Token,这些永远不会被缓存。只有输入 Token 能从 Prompt Caching 中受益。这意味着:

With thinking enabled:
  Input tokens  → Can be cached (90% savings possible)
  Thinking tokens → Output, never cached, always full price
  Response tokens → Output, never cached, always full price

缓存和思考是互补的优化,针对不同的成本组成部分:

  • 缓存降低了处理上下文的成本(输入 Token)
  • 思考预算管理降低了推理的成本(输出 Token)

参考:Anthropic 文档 ​

关于 Prompt Caching 的最新详情,请参阅:

  • Anthropic 文档:Prompt Caching——包含当前定价和 API 详情的官方文档
  • Anthropic Cookbook:Python 和 TypeScript SDK 的 Prompt Caching 示例
  • API 参考:Messages API 响应中的 usage 对象

缓存行为和定价可能会变化。请始终查阅官方文档获取最新信息。

核心要点 ​

  1. Prompt Caching 将匹配已缓存前缀的输入成本降低 90%。Claude Code 自动受益,因为系统提示和 CLAUDE.md 形成了稳定的前缀。
  2. 缓存条目在 5 分钟无活动后过期。活跃的会话保持缓存活跃;超过 5 分钟的休息会导致下一个请求的冷启动。
  3. 稳定的前缀最大化缓存。将不变的内容(系统提示、CLAUDE.md)放在开头。避免在会话中途编辑 CLAUDE.md。
  4. 详尽的 CLAUDE.md 在第一个回合后几乎是免费的。不要为了节省 Token 而缩减项目说明——缓存使每回合的成本可以忽略不计。
  5. 活跃多回合会话的缓存命中率通常为 60-85%。第一个回合始终是缓存未命中。
  6. 缓存和 Extended Thinking 是互补的:缓存降低输入成本,思考预算管理降低输出成本。

参见:A07 Token 经济学 了解完整的成本分析,第 3 章 上下文窗口管理 了解上下文策略,以及 A09 Extended Thinking 了解输出 Token 优化。

基于 MIT 许可发布