A10: Prompt Caching(提示缓存)
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.50 | 90% |
| Claude Sonnet 4 | $3.00 | $0.30 | 90% |
| Claude Haiku 3.5 | $0.80 | $0.08 | 90% |
注意:创建新缓存条目时还有少量缓存写入成本(首次请求有 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 的工具):
# 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,因为:
- 它被注入到提示的早期位置(系统提示前缀的一部分)
- 它在会话期间很少变化
- 它在所有回合中都完全相同
- 它应用于每个请求(没有条件性包含)
这意味着精心编写的 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 时,缓存信息在响应中返回:
{
"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(未缓存)。
计算你的缓存命中率
# 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对象
缓存行为和定价可能会变化。请始终查阅官方文档获取最新信息。
核心要点
- Prompt Caching 将匹配已缓存前缀的输入成本降低 90%。Claude Code 自动受益,因为系统提示和 CLAUDE.md 形成了稳定的前缀。
- 缓存条目在 5 分钟无活动后过期。活跃的会话保持缓存活跃;超过 5 分钟的休息会导致下一个请求的冷启动。
- 稳定的前缀最大化缓存。将不变的内容(系统提示、CLAUDE.md)放在开头。避免在会话中途编辑 CLAUDE.md。
- 详尽的 CLAUDE.md 在第一个回合后几乎是免费的。不要为了节省 Token 而缩减项目说明——缓存使每回合的成本可以忽略不计。
- 活跃多回合会话的缓存命中率通常为 60-85%。第一个回合始终是缓存未命中。
- 缓存和 Extended Thinking 是互补的:缓存降低输入成本,思考预算管理降低输出成本。
参见:A07 Token 经济学 了解完整的成本分析,第 3 章 上下文窗口管理 了解上下文策略,以及 A09 Extended Thinking 了解输出 Token 优化。