Prompt Caching:LLM 调用成本降 90% 的底层机制与实战策略
同一条 System Prompt 被发送上万次,每次都从零计算------Prompt Caching 通过复用 KV Cache 中间状态,将重复前缀的输入成本降至 1/10,首 Token 延迟降低 50%-85%。本文从 Transformer 推理机制出发,拆解三大厂商的实现差异,并给出从 0% 到 80%+ 缓存命中率的实战步骤。
一、引子:同样的 System Prompt,为什么要算一万遍?
一个典型的 RAG 应用,每次请求都会携带 10K token 的 System Prompt + 检索文档上下文。如果每天处理 10,000 次请求,其中 System Prompt 完全相同------这意味着同一段文本被 Prefill 计算了 10,000 次。
这不是假设。阿里云通义团队的生产数据(KVCache in the Wild,2025)揭示了两个关键数字:
- to-B 场景下,97% 的缓存命中来自 System Prompt 共享------几乎所有重复计算都集中在固定指令上
- KV Cache 的 P99 生命周期仅 97 秒------缓存天然短命,但请求密集时复用率极高
Agent 时代放大了这个问题。Claude Code 的 200K context window 中,每次会话都会重新发送完整的 CLAUDE.md、System Prompt 和对话历史。Claude Code 团队工程师 Thariq Shafi 明确表示:他们围绕 Prompt Caching 构建了整个工程体系,cache hit rate 下降会触发生产事故级别的告警。
换言之,对 Agent 应用而言,Prompt Caching 不是优化项,而是基础设施。
那么问题来了:LLM 推理到底在重复计算什么?缓存又是如何跳过这些计算的?
二、原理与基础:从 KV Cache 到 Prompt Cache
2.1 LLM 推理的两阶段:Prefill 与 Decode
LLM 推理分为两个阶段,它们的计算特征截然不同。
Prefill 阶段 :模型一次性处理所有输入 token,为每个 token 计算自注意力机制中的 Key 和 Value 张量。这些张量构成 KV Cache ------模型对输入语境的数学表示。Prefill 是 compute-bound 的,计算量与输入长度正相关,长 Prompt 的 Prefill 可以耗时数百毫秒。
Decode 阶段 :模型基于 KV Cache 逐 token 自回归生成输出。每生成一个新 token,需要读取之前所有 token 的 KV Cache 来计算注意力。Decode 是 memory-bound 的,瓶颈在于显存带宽而非算力。
用 Mooncake 论文中 LLaMA2-70B 的数据来说明差异:
| 阶段 | 瓶颈类型 | 计算量与序列长度关系 | 延迟特征 |
|---|---|---|---|
| Prefill | compute-bound | 超线性增长 | 长输入时显著 |
| Decode | memory-bound | 线性增长 | 每步稳定 |
关键洞察:Prefill 是成本和延迟的主要来源,而 Prompt Caching 正是针对 Prefill 的优化。
2.2 KV Cache:注意力状态的中间结果
理解 KV Cache 是理解 Prompt Caching 的前提。
Transformer 的自注意力机制中,每个 token 会生成三个向量:Query(Q)、Key(K)、Value(V)。在 Prefill 阶段,模型为所有输入 token 计算完整的 K 和 V 矩阵,存储在 KV Cache 中。Decode 阶段只需计算新 token 的 Q,然后与已有的 K、V 做注意力计算即可。
传统 API 调用的问题:请求结束后,KV Cache 立即被销毁。下一次请求即使携带完全相同的前缀,也要重新执行一遍 Prefill。这就是成本浪费的根源。
注意:KV Cache 的大小与序列长度和模型维度成正比。对于 LLaMA2-70B,一个 token 的 KV Cache 约占 1.28 MB 显存。10K token 的 System Prompt 对应约 12.8 GB 的 KV Cache------每次请求都重新计算这些数据,代价不低。
2.3 Prompt Cache 的核心机制:前缀匹配 + 状态复用
Prompt Caching 的核心思路:把 Prefill 阶段产出的 KV Cache 持久化存储,后续请求命中相同前缀时直接复用,跳过 Prefill 计算。
工作流程分三步:
-
Step 1:前缀哈希 (目的:快速判断是否命中缓存)
系统对请求的 token 前缀做连续哈希计算。前缀的构成顺序是固定的:Tools → System → Messages。只有字节级完全一致的前缀才能命中。
-
Step 2:缓存查找 (目的:决定走快速路径还是完整计算)
新请求到达时,系统用哈希值查找缓存。命中则跳过已缓存部分的 Prefill,只对新 token 执行 Prefill;未命中则完整计算,并将标记的前缀写入缓存。
-
Step 3:状态复用 (目的:跳过重复计算,降低成本和延迟)
缓存命中时,模型直接加载预存的 KV 状态,从缓存断点处继续处理。这意味着 10K token 的 System Prompt 只需计算一次,后续请求只处理新增的几百 token。
为什么必须是严格前缀匹配?
这是 Transformer 因果注意力(Causal Attention)的硬约束。第 i 个 token 的注意力表征不仅取决于自身,还取决于序列中从 1 到 i-1 的所有 token。如果前缀中间某个 token 发生变化,该点之后的所有 KV 张量都会失效------因为计算上下文已经改变。
这意味着:Prompt Cache 不支持缓存任意片段或不连续部分。缓存必须从请求的第一个 token 开始,连续延伸到缓存断点。
三、方案与对比:三大厂商的实现差异
3.1 Anthropic Claude:显式断点控制
Claude 的 Prompt Caching 提供两种模式:
自动缓存(Automatic Caching) :在请求顶层添加 cache_control 字段,系统自动将断点放在最后一个可缓存块,随对话增长自动前移。适合多轮对话场景。
显式缓存断点(Explicit Breakpoints) :在具体 content block 上标记 cache_control,精确控制缓存边界。适合需要分层缓存的复杂场景。
json
{
"model": "claude-sonnet-4-20250514",
"system": [
{
"type": "text",
"text": "<10K token 的稳定指令与上下文>",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "针对上面上下文的提问"}
]
}
定价模型(以 Claude Sonnet 4 为例):
| Token 类别 | 价格 | 相对成本 |
|---|---|---|
| 标准输入 | $3.00 / MTok | 1.0x |
| 缓存写入(5 分钟 TTL) | $3.75 / MTok | 1.25x |
| 缓存写入(1 小时 TTL) | $6.00 / MTok | 2.0x |
| 缓存读取 | $0.30 / MTok | 0.1x |
| 输出 | $15.00 / MTok | 5.0x |
关键约束:每个缓存断点最少 1,024 token,每个请求最多 4 个断点,默认 TTL 为 5 分钟(每次命中自动续期)。
回本计算:首次请求多付 25% 写入溢价,第二次请求开始享受 90% 折扣。只要同一前缀在 TTL 内被复用 2 次以上,就是赚的。
3.2 OpenAI:自动前缀缓存
OpenAI 的实现走了一条完全不同的路线------零配置、全自动。
不需要任何代码改动,系统自动将请求路由到最近处理过相同前缀的服务器。缓存对开发者完全透明。
定价模型(以 GPT-4o 为例):
| Token 类别 | 价格 | 相对成本 |
|---|---|---|
| 标准输入 | $2.50 / MTok | 1.0x |
| 缓存读取 | $1.25 / MTok | 0.5x |
| 输出 | $10.00 / MTok | 4.0x |
关键约束:前缀最少 1,024 token,缓存 TTL 约 5-10 分钟(非活跃期),高峰期可延长至 1 小时。没有写入溢价------缓存写入与标准输入同价。
3.3 Google Gemini:上下文缓存
Gemini 的 Context Caching 采用显式创建缓存对象的方式,与其他两家差异较大。
开发者需要先创建 CachedContent 对象,然后在后续请求中引用该缓存:
python
# 创建缓存
cached_content = caching.CachedContent.create(
model='models/gemini-2.0-flash',
display_name='doc_cache',
system_instruction='You are a document analyst.',
contents=[{'role': 'user', 'parts': [large_document]}],
cache_expiration_duration={'seconds': 3600}
)
# 使用缓存
model = GenerativeModel('models/gemini-2.0-flash')
response = model.generate_content(
'Summarize the key findings',
cached_content=cached_content
)
定价模型(以 Gemini 1.5 Pro 为例):
| Token 类别 | 价格 | 相对成本 |
|---|---|---|
| 标准输入 | $1.25 / MTok | 1.0x |
| 缓存存储 | $4.50 / MTok / 小时 | 按时长计费 |
| 缓存读取 | $0.3125 / MTok | 0.25x |
关键约束:最少 2,048 token,TTL 范围 5 分钟到 24 小时,免费层每分钟 1 MB 缓存配额,付费层 50 MB/分钟。
3.4 三厂商对比
| 维度 | Anthropic Claude | OpenAI GPT | Google Gemini |
|---|---|---|---|
| 缓存模式 | 显式断点 + 自动缓存 | 全自动 | 显式创建缓存对象 |
| 代码改动 | 需添加 cache_control | 无需改动 | 需创建 CachedContent |
| 缓存读取折扣 | 90%(0.1x) | 50%(0.5x) | 75%(0.25x) |
| 写入溢价 | 25%(5min)/ 100%(1h) | 无 | 按存储时长计费 |
| 最小 token 数 | 1,024 | 1,024 | 2,048 |
| 最大断点数 | 4 个 | 不限(自动) | 1 个缓存对象 |
| 默认 TTL | 5 分钟 | 5-10 分钟 | 可配置(5min-24h) |
| 回本点 | 2 次命中 | 1 次命中(无溢价) | 取决于存储时长 |
| 适用场景 | Agent 多轮对话、RAG | 通用 API 调用 | 长文档批量分析 |
3.5 选型建议
选 Anthropic 的场景:Agent 应用、多轮对话、RAG 系统------这些场景 System Prompt 长且稳定,90% 的读取折扣收益最大。4 个断点允许分层缓存(System Prompt → 工具定义 → 文档上下文 → 对话历史),精细控制缓存边界。
选 OpenAI 的场景:快速接入、不想改代码------零配置对存量应用友好。50% 的折扣虽然不如 Anthropic,但没有写入溢价意味着第一次请求也不亏。
选 Gemini 的场景:长文档批量分析、TTL 需要超过 1 小时------Gemini 支持最长 24 小时缓存,适合定时批处理任务。
四、实战步骤:从 0% 到 80%+ 缓存命中率
Step 1:Prompt 结构重组(目的:最大化可缓存前缀)
缓存命中的前提是前缀一致。如果动态内容混在静态内容中间,每次请求的前缀都不同,缓存形同虚设。
❌ 错误结构:动态内容破坏了前缀一致性
python
# 动态变量嵌入 System Prompt,每次请求前缀都不同
system_prompt = f"""
你是 {user_name} 的助手。
当前时间:{current_time}。
以下是你必须遵守的规则:
1. 使用中文回复
2. 返回结果需包含数据可视化建议
"""
这个 System Prompt 包含 user_name 和 current_time,每次请求都不同。整个前缀无法命中缓存。
✅ 正确结构:静态内容前置,动态内容后置
python
# 静态规则放在 System Prompt,动态信息放在 User Message
system_prompt = """
你是一个数据分析助手,需遵循以下规则:
1. 使用中文回复
2. 返回结果需包含数据可视化建议
""" # 这部分稳定不变,可以缓存
user_message = f"用户 {user_name} 在 {current_time} 提问:{question}" # 动态部分放最后
解读:将 System Prompt 中的动态变量剥离到 User Message 中,System Prompt 保持完全静态。这样所有请求共享相同的 System Prompt 前缀,缓存命中率从 0% 直接提升。
Step 2:缓存断点设计(目的:精准控制缓存边界)
以 Anthropic Claude 为例,4 个断点可以构建分层缓存:
[Tools 定义] ──── 断点 1:工具目录缓存
[System Prompt] ── 断点 2:系统指令缓存
[文档上下文] ──── 断点 3:RAG 检索结果缓存
[对话历史] ──── 断点 4:多轮对话缓存
[用户提问] ──── 不缓存:每次不同
断点放置原则:
| 层级 | 内容 | 变化频率 | 缓存价值 |
|---|---|---|---|
| 断点 1 | Tools 定义 | 极少变 | 高------所有请求共享 |
| 断点 2 | System Prompt | 几乎不变 | 最高------核心指令 |
| 断点 3 | 文档/代码上下文 | 按会话变 | 中------同会话复用 |
| 断点 4 | 对话历史 | 每轮增长 | 中------Agent 循环复用 |
提醒:Anthropic 的请求处理顺序是 Tools → System → Messages。如果 Tools 定义发生任何变动,后续所有层级的缓存都会失效。确保 Tools 定义稳定是缓存策略的根基。
Step 3:TTL 策略选择(目的:匹配业务请求间隔)
Anthropic 提供两种 TTL,选择依据很简单------看请求间隔:
| 场景 | 请求间隔 | 推荐 TTL | 理由 |
|---|---|---|---|
| 交互式对话 | < 5 分钟 | 5 分钟(默认) | 写入溢价低(1.25x),交互内自然续期 |
| Agent 工具循环 | < 5 分钟 | 5 分钟(默认) | 每轮 tool call 间隔短,5 分钟足够 |
| 定时批处理 | 5-60 分钟 | 1 小时 | 避免缓存过期反复重建,2x 写入溢价仍划算 |
| 低频查询 | > 1 小时 | 评估是否值得缓存 | 每次都可能重建缓存,收益有限 |
决策规则:如果两次请求间隔在 5 分钟以内,选默认 TTL;间隔在 5-60 分钟之间,1 小时 TTL 比反复重建 5 分钟缓存便宜;超过 1 小时,需要重新评估缓存是否有意义。
Step 4:监控与调优(目的:持续验证缓存效果)
缓存不是"设了就忘"。需要监控关键指标来验证效果。
核心指标 (来自 API 响应的 usage 字段):
json
{
"usage": {
"input_tokens": 12,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 30000,
"output_tokens": 240
}
}
| 指标 | 含义 | 关注点 |
|---|---|---|
cache_creation_input_tokens |
本次写入缓存的 token 数 | 首次请求或缓存过期后出现 |
cache_read_input_tokens |
本次从缓存读取的 token 数 | 越高越好,代表命中 |
input_tokens |
未命中缓存的 token 数 | 越低越好 |
缓存命中率计算:
命中率 = cache_read_input_tokens / (cache_read_input_tokens + cache_creation_input_tokens + input_tokens)
告警阈值建议:
- 命中率 < 50%:检查 Prompt 结构是否有动态内容破坏前缀
cache_creation_input_tokens持续不为 0:缓存可能在反复过期重建,考虑延长 TTLinput_tokens占比过高:动态内容占比太大,需要重新审视 Prompt 结构
Step 5:Agent 场景的特殊考量
Agent 应用是 Prompt Caching 收益最大的场景,但也有特殊陷阱。
多轮对话的缓存策略:
Agent 的每次 tool call 都是一次新的 API 请求。对话历史不断增长,但前缀(System Prompt + 已有对话)保持不变。这正是缓存的最佳场景------对话越长,可缓存的前缀越长,收益越复利。
arXiv 论文 Don't Break the Cache(2026)的关键发现:
该论文在 DeepResearch Bench 上评估了 500+ Agent 会话,System Prompt 约 10,000 token。核心结论:
- Prompt Caching 将 API 成本降低 41%-80% ,首 Token 延迟改善 13%-31%
- 策略性缓存断点控制比"全量缓存"效果更稳定------全量缓存反而可能增加延迟
- 动态内容(如 tool call 返回结果)应排除在缓存断点之外,避免频繁失效
- 不同厂商对相同策略的响应存在差异,需要针对性调优
Tool Call 缓存的陷阱:
Agent 的 function calling 返回结果通常是动态的(如搜索结果、数据库查询结果)。如果把这些结果放在缓存断点之前,每次结果变化都会导致缓存失效。
❌ 错误做法:Tool 结果放在缓存断点内
[System Prompt] ──── 断点
[Tool Results] ──── 包含在缓存前缀中(每次结果不同,缓存失效)
[User Question]
✅ 正确做法:Tool 结果放在缓存断点之后
[System Prompt] ──── 断点(稳定,缓存命中)
[Tool Results] ──── 不缓存(动态内容,不影响前缀)
[User Question]
五、总结
Prompt Caching 的本质是对 LLM 推理中 Prefill 阶段的计算结果做持久化和复用。它不是简单的"存一下结果",而是基于 Transformer 因果注意力的硬约束------严格前缀匹配------来设计缓存策略。
三个核心结论:
-
Prompt 结构决定缓存命运 。静态内容前置、动态内容后置,是缓存命中率的决定性因素。一个
current_time变量嵌入 System Prompt,就能让命中率归零。 -
厂商实现差异显著,选型要看场景。Anthropic 的 90% 折扣 + 显式断点适合 Agent/RAG;OpenAI 的零配置适合存量应用快速接入;Gemini 的长 TTL 适合批处理。
-
Agent 场景收益最大,但陷阱也最多。多轮对话天然适合缓存,但 Tool Call 结果等动态内容如果放在缓存断点内,会适得其反。策略性断点控制优于全量缓存。
适用边界:Prompt Caching 对"固定前缀 + 高频复用"的场景效果显著(Agent、RAG、多轮对话、批处理)。对于每次请求内容都不同的场景(如一次性文档分析),缓存无法命中,没有收益。
参考资料
- Anthropic, "Prompt Caching", platform.claude.com/docs, 2026
- OpenAI, "Prompt Caching", platform.openai.com/docs, 2025
- Google, "Context Caching in Firebase AI Logic", firebase.google.cn/docs, 2026
- Elias Lumer et al., "Don't Break the Cache: An Evaluation of Prompt Caching for Long-Horizon Agentic Tasks", arXiv:2601.06007, 2026
- 阿里云通义团队, "KVCache in the Wild", 2025
- Mooncake Team, "Mooncake: A KV Cache-centric Disaggregated Architecture for LLM Serving", 2024