文章摘要
任何生产 LLM 应用每月 API 账单超过 $500 的团队,都可以通过 prompt caching 系统性降低 50-90% 的输入成本。本文从 Provider 层缓存机制出发,覆盖 Anthropic/OpenAI/Gemini 三大平台的实现差异、breakeven 分析、cache-friendly prompt 设计、生产监控,以及常见陷阱,给出可直接落地的工程决策框架。
一、为什么你需要系统性地降低 LLM API 成本
想象这样一个场景:你的团队运营一个 AI 客服系统,每天处理 5000 次对话。每次对话都包含相同的 3000-token 系统提示(角色定义、业务规则、输出格式),加上变化的用户消息。月底账单出来,光输入 token 就花了 $2,400——其中 70% 是在反复发送相同的系统提示。
你试过压缩 prompt、减少 few-shot 示例、甚至换更便宜的模型,但效果有限。问题不在于 prompt 太长,而在于你在为重复内容反复付费。
Prompt caching(提示缓存) 就是解决这个问题的。它不是客户端的 HTTP 缓存,也不是 CDN 缓存,而是 LLM Provider 在推理层面提供的优化:当你连续发送相同前缀的请求时,Provider 会复用之前计算的中间结果(KV cache),只对新增部分重新计算。结果就是:
- 成本降低 50-90%:缓存读取的价格通常是正常 input 的 10-50%
- 延迟降低 20-50%:跳过前缀的 prefill 阶段,首 token 响应更快
- 零质量损失:缓存的是计算中间态,输出结果与不使用缓存完全一致
本文能帮你做什么:读完之后,你将能够——
- 设计 cache-friendly 的 prompt 结构(静态前缀 + 动态后缀分离)
- 计算不同 Provider 的 prompt caching breakeven(缓存写入价格 vs 读取价格)
- 为 Anthropic/OpenAI/Gemini 分别配置缓存策略
- 判断哪些工作负载适合 prompt caching、哪些不适合
先澄清一个常见误解:prompt caching 与你在 token-economics 中看到的"工具结果缓存"(Agent Runtime 层)是两回事。工具结果缓存是在你的应用代码里缓存 API 返回的结果,避免重复调用;prompt caching 是 Provider 在推理时缓存 prompt 的 KV cache 表示,降低单次调用的计算成本。两者互补,不冲突。
核心术语速览(后文会详细展开):
- KV cache(键值缓存):Transformer 模型在推理时存储的中间计算状态。Prompt caching 本质上就是复用这个缓存。
- Prefix caching(前缀缓存):Provider 层面的优化机制——相同前缀的请求可复用 KV cache。与客户端 HTTP 缓存完全不同。
- Cache hit/miss(缓存命中/未命中):命中表示前缀匹配成功、复用了缓存;未命中表示需要重新计算。
- TTL(Time-To-Live):缓存存活时间。不同 Provider 的 TTL 策略差异很大。
- cache_control:Anthropic 的显式缓存控制参数,用于标记哪些内容块需要缓存。
二、Provider 层缓存 vs 客户端缓存:本质区别
在深入三大 Provider 的实现之前,必须先搞清楚 prompt caching 与你熟悉的其他缓存机制有什么区别。混淆这些概念会导致错误的优化策略。
HTTP 缓存(浏览器/CDN 层):存储完整的 HTTP 响应。命中条件是 URL + Header 完全匹配。对 LLM API 基本无效,因为每次请求的 body(prompt)都不同,即使系统提示相同,用户消息也不同。
工具结果缓存(应用层):在你的代码里缓存 API 返回的结果。比如用户问"北京天气",你缓存结果 1 小时。这是应用层优化,与 Provider 无关。
Prompt caching(Provider 层):Provider 在推理时缓存 prompt 的 KV cache 表示。命中条件是 prompt 的前缀 token 序列完全匹配。缓存的不是响应结果,而是中间计算状态——它不能直接作为响应返回,但能大幅加速后续相同前缀的处理,并降低计算成本。
为什么这个区别重要? 因为命中逻辑完全不同:
- HTTP 缓存:URL + Header 匹配 → 对 API 调用无效
- 工具结果缓存:相同输入 → 返回相同输出 → 应用层优化
- Prompt caching:前缀 token 匹配 → 复用 KV cache → Provider 层优化
从底层工作原理看:LLM 处理每个请求时,Transformer 架构需要先对 prompt 做 prefill(预填充)计算,为每个 token 生成 key 和 value 向量并存入 KV cache。前缀相同的请求,前段 token 的 key/value 完全一致,Provider 直接复用已存储的 KV cache,跳过这段 prefill 计算,这就是 prompt caching 的底层实现方式。缓存命中时,模型只需要对新增的后缀 token 执行 prefill,然后进入 decode(逐 token 生成)阶段。
一个具体例子:你的客服系统每次调用都发送相同的 3000-token 系统提示,加上不同的用户消息。
- HTTP 缓存无法命中(每次请求 body 不同)
- 工具结果缓存无法命中(每个用户问题不同,结果也不同)
- Prompt caching 可以命中(前缀 3000 tokens 相同)→ 系统提示部分成本降低 90%
关键洞察:prompt caching 是 Provider 专门为 LLM API 的计费模型设计的优化。它不关心你的 HTTP 请求是否相同,只关心 prompt 的前缀 token 序列是否相同。这意味着即使每次请求的用户消息不同,只要系统提示和工具定义相同,就能命中缓存。
三、三大 Provider 的机制对比与选型
Anthropic、OpenAI 和 Google 的 prompt caching 实现差异显著。选型时不能只看"支持 caching",必须理解各自的触发方式、TTL 策略、最小缓存单元和计费模型。
Anthropic:显式控制,灵活但需手动配置
Anthropic 的 prompt caching 需要开发者在请求中显式标记 cache_control 断点。你可以精确控制哪些内容块(系统提示、工具定义、对话历史)需要缓存。
核心参数:
- 触发方式:在 content block 中添加
cache_control: {"type": "ephemeral"} - TTL:默认 5 分钟,可通过
ttl: "1h"扩展到 1 小时 - 最小缓存单元:1024 tokens(Haiku 3.5 为 2048 tokens)
- 计费:缓存写入 = 基础 input 价格 × 1.25,缓存读取 = 基础 input 价格 × 0.1
- 断点数量:最多 4 个 cache_control 断点
适用场景:需要精细控制缓存策略的复杂应用,如多轮对话 Agent、工具密集型任务。
import anthropic
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=4096,
system=[{
"type": "text",
"text": "你是一个专业的技术顾问...(2000+ tokens 的系统提示)",
"cache_control": {"type": "ephemeral", "ttl": "1h"} # 显式标记缓存
}],
messages=[{"role": "user", "content": "分析这段代码..."}]
)
# 检查缓存命中情况
usage = message.usage
print(f"缓存读取 tokens: {usage.cache_read_input_tokens}")
print(f"缓存写入 tokens: {usage.cache_creation_input_tokens}")💡 一句话理解
Anthropic 的缓存断点本身不产生额外费用,只有实际被缓存的内容才计费。如果 prompt 长度低于最小缓存单元(1024 tokens),cache_control 会被静默忽略(不报错)。并发请求时,第一个响应开始后才能命中缓存,需要串行化首次请求。
三(续)、OpenAI 与 Gemini 的缓存策略
OpenAI:自动缓存,零配置但不可控
OpenAI 的 prompt caching 完全自动,无需任何代码修改。系统会检测相同前缀并自动复用缓存。
核心参数:
- 触发方式:自动,无需手动标记
- TTL:约 5-10 分钟,不可配置
- 最小缓存单元:1024 tokens
- 计费:缓存读取 = 基础 input 价格 × 0.5(50% 折扣)
- 适用模型:GPT-4o、GPT-4o-mini、o1 系列、o3-mini 等
适用场景:快速集成、不想修改代码的现有应用,或前缀结构简单的场景。
局限性:无法控制哪些内容被缓存;无法调整 TTL;缓存命中率不可观测(API 响应中不返回缓存统计)。
Google Gemini:显式 + 自动混合,TTL 可配置
Gemini 的 cachedContent API 允许预创建缓存对象,显式指定 TTL,适合需要长期缓存的场景。
核心参数:
- 触发方式:通过 cachedContent API 预创建缓存,或在请求中自动触发
- TTL:可配置,范围 60 秒到 1 小时
- 最小缓存单元:4096 tokens
- 计费:缓存存储按小时计费(与 input token 价格独立),缓存读取免费
- 适用模型:Gemini 1.5 Flash、Gemini 1.5 Pro
适用场景:需要长期缓存(如系统提示数天不变)、对缓存成本可预测性要求高的场景。
关键区别:Gemini 的缓存存储成本是独立的,即使没有命中也会产生费用。适合缓存内容长期稳定的场景,不适合频繁变更的系统提示。
from openai import OpenAI
client = OpenAI()
# 相同的 system prompt 会自动命中缓存
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "你是一个专业的技术顾问...(2000+ tokens)"},
{"role": "user", "content": "分析这段代码..."}
]
)import google.generativeai as genai
client = genai.Client()
# 预创建缓存
cached_content = client.cached_contents.create(
model="gemini-1.5-flash-001",
system_instruction="你是一个专业的技术顾问...(2000+ tokens)",
ttl="3600s" # 1 小时
)
# 使用缓存
response = client.models.generate_content(
model="gemini-1.5-flash-001",
contents="分析这段代码...",
cached_content=cached_content.name
)| Provider | 触发方式 | TTL | 最小缓存单元 | 缓存写入成本 | 缓存读取成本 | 适用场景 |
|---|---|---|---|---|---|---|
Anthropic | 显式 cache_control | 5min / 1h | 1024 tokens | 基础价 × 1.25 | 基础价 × 0.1 | 复杂应用,需精细控制 |
OpenAI | 自动 | 5-10min | 1024 tokens | 基础价 | 基础价 × 0.5 | 快速集成,简单前缀 |
Gemini | 显式 API + 自动 | 60s - 1h | 4096 tokens | 存储费/小时 | 免费 | 长期稳定缓存 |
四、成本建模:计算你的 breakeven point
Prompt caching 不是免费的。缓存写入通常比直接 input 更贵,只有通过多次缓存读取才能摊薄成本。你需要计算 breakeven point(盈亏平衡点):缓存命中多少次才能回本。
通用公式:
breakeven_hits = cache_write_cost / (cache_write_cost - cache_read_cost)
三大 Provider 的 breakeven 计算:
| Provider | 缓存写入成本 | 缓存读取成本 | Breakeven 次数 | 说明 |
|---|---|---|---|---|
| Anthropic | 基础价 × 1.25 | 基础价 × 0.1 | 2 次 | 第 2 次读取即开始盈利 |
| OpenAI | 基础价(自动) | 基础价 × 0.5 | 2 次 | 第 2 次读取即开始盈利 |
| Gemini | 存储费/小时 | 免费 | 取决于使用频率 | 高频场景 1 次即回本 |
实际案例:
假设系统提示 5000 tokens,使用 Claude Sonnet(基础 input 价格 $3/M tokens):
- 不使用缓存:每次调用成本 = 5000 × $3/M = $0.015
- 使用缓存:
- 首次写入成本 = 5000 × $3/M × 1.25 = $0.01875
- 后续读取成本 = 5000 × $3/M × 0.1 = $0.0015
- Breakeven = $0.01875 / ($0.01875 - $0.0015) ≈ 1.1 次
结论:只要系统提示被使用 2 次以上,prompt caching 就能降低成本。对于每天调用 1000 次的客服系统,成本可降低 80-90%。
def calculate_roi(
prompt_tokens: int,
daily_calls: int,
base_input_price: float, # 每百万 token 价格
cache_write_multiplier: float, # 如 1.25
cache_read_multiplier: float, # 如 0.1
):
"""计算 prompt caching 的 ROI"""
# 不使用缓存的日成本
no_cache_cost = prompt_tokens * daily_calls * base_input_price / 1_000_000
# 使用缓存的日成本
cache_write_cost = prompt_tokens * base_input_price * cache_write_multiplier / 1_000_000
cache_read_cost = prompt_tokens * base_input_price * cache_read_multiplier / 1_000_000
# 首次写入 + 后续读取
with_cache_cost = cache_write_cost + (daily_calls - 1) * cache_read_cost
# 节省比例
savings = (no_cache_cost - with_cache_cost) / no_cache_cost * 100
return {
"no_cache_daily_cost": no_cache_cost,
"with_cache_daily_cost": with_cache_cost,
"daily_savings": no_cache_cost - with_cache_cost,
"savings_percentage": savings
}
# 示例:客服系统,5000 token 系统提示,每天 1000 次调用
result = calculate_roi(
prompt_tokens=5000,
daily_calls=1000,
base_input_price=3.0, # Claude Sonnet
cache_write_multiplier=1.25,
cache_read_multiplier=0.1
)
print(f"日节省: {'$'}{result['daily_savings']:.2f} ({result['savings_percentage']:.1f}%)")
# 输出:日节省: $13.50 (90.0%)五、生产部署:cache-friendly prompt 设计
Prompt caching 不是万能的。以下决策框架帮助你判断哪些工作负载适合缓存,如何设计 cache-friendly 的 prompt 结构,以及如何监控和优化。
步骤 1:识别适合缓存的工作负载
适合缓存的特征:
- 系统提示或工具定义重复使用(如客服、RAG、Agent)
- 多轮对话中前缀稳定(如上下文窗口中的历史消息)
- 调用频率高(每天 > 100 次)
- Prompt 长度 > 1024 tokens(低于最小缓存单元无法生效)
不适合缓存的特征:
- 每次请求的 prompt 完全不同(如随机生成的创意写作提示)
- 调用频率极低(如每天 < 10 次,缓存写入成本无法摊薄)
- Prompt 长度 < 1024 tokens(无法触发缓存)
- 需要强一致性的场景(如实时更新的系统提示,缓存可能导致版本不一致)
步骤 2:设计 cache-friendly 的 prompt 结构
核心原则:将不变的内容放在前缀,变化的内容放在后缀。
推荐结构:系统提示(稳定,缓存)→ 工具定义(稳定,缓存)→ 上下文/知识库(半稳定,可选缓存)→ 用户消息(变化,不缓存)。
反模式:用户消息(变化)→ 系统提示(稳定)。前缀变化导致缓存永远失效。
多断点策略(Anthropic 支持最多 4 个断点):如果你的 prompt 包含多个变化频率不同的部分,可以使用多个 cache_control 断点分别标记。
步骤 3:监控与优化
关键指标:
- 缓存命中率:cache_read_input_tokens / (cache_read + cache_creation)
- 成本节省:对比启用缓存前后的 API 账单
- 延迟改善:缓存命中时 TTFT(Time To First Token)通常降低 20-50%
优化策略:
- 命中率 < 50%:检查 prompt 结构,确保稳定前缀足够长
- 命中率 > 90% 但成本仍高:考虑使用更便宜的模型处理缓存命中场景
- TTL 频繁过期:增加 TTL(Anthropic 支持 1 小时)或提高调用频率
# 多断点策略示例
system=[{
"type": "text",
"text": "核心系统提示...(1000 tokens)",
"cache_control": {"type": "ephemeral", "ttl": "1h"} # 断点 1:长期稳定
}]
tools=[{...}] # 工具定义较少变化,可自动缓存
messages=[{
"role": "user",
"content": [{
"type": "text",
"text": "知识库内容...(5000 tokens,每日更新)",
"cache_control": {"type": "ephemeral", "ttl": "5m"} # 断点 2:短期缓存
}]
}]def track_cache_performance(responses: list):
"""追踪缓存命中率和成本节省"""
total_cache_read = sum(r.usage.cache_read_input_tokens for r in responses)
total_cache_write = sum(r.usage.cache_creation_input_tokens for r in responses)
total_input = sum(r.usage.input_tokens for r in responses)
hit_rate = total_cache_read / (total_cache_read + total_cache_write) if (total_cache_read + total_cache_write) > 0 else 0
print(f"缓存命中率: {hit_rate:.1%}")
print(f"缓存读取 tokens: {total_cache_read}")
print(f"缓存写入 tokens: {total_cache_write}")
print(f"未缓存 input tokens: {total_input}")
# 估算成本节省(假设 Claude Sonnet,节省 90%)
savings = total_cache_read * 0.9 * 3 / 1_000_000
print(f"估算节省: {'$'}{savings:.2f}")六、边界情况与常见陷阱
即使理解了机制,实际部署中仍会踩坑。以下是五个最常见的陷阱及其解决方案。
陷阱 1:缓存断点位置错误
错误做法:在变化的内容上标记 cache_control。前缀每次都不同,缓存永远无法命中。正确做法:在稳定前缀的末尾标记 cache_control。
陷阱 2:忽略最小缓存单元
如果 prompt 长度低于最小缓存单元(Anthropic 1024 tokens,Gemini 4096 tokens),cache_control 会被静默忽略,不会报错但也不会生效。验证方法:检查响应中的 cache_creation_input_tokens,如果为 0 说明未触发缓存。
陷阱 3:并发请求导致缓存未命中
首次请求的缓存只有在响应开始后才能被后续请求命中。如果多个请求同时发出,它们都会触发缓存写入,造成浪费。解决方案:串行化首次请求,或使用预热脚本提前触发缓存。
陷阱 4:TTL 过期导致成本激增
如果调用间隔超过 TTL,缓存会过期,下次调用需要重新写入。对于低频场景(如每小时 1 次),prompt caching 可能不划算。解决方案:选择支持长 TTL 的 Provider(如 Anthropic 的 1 小时),或提高调用频率。
陷阱 5:跨 Provider 缓存不互通
Anthropic 的缓存不能用于 OpenAI,反之亦然。如果你使用多个 Provider,需要分别配置缓存策略。
陷阱 6:Gemini 的存储成本陷阱
Gemini 的缓存存储是按小时计费的,即使没有命中也会产生费用。如果你创建了一个缓存但很少使用,存储成本可能超过节省的 input 成本。解决方案:只在缓存内容长期稳定且高频使用时才使用 Gemini 的 cachedContent API。
陷阱 7:缓存导致输出不一致
虽然 prompt caching 不会改变模型的输出质量,但如果你的系统提示频繁更新,缓存可能导致不同用户看到不同版本的系统提示(取决于缓存是否过期)。解决方案:对于需要强一致性的场景(如合规要求),禁用缓存或使用极短 TTL。
# 预热脚本:在应用启动时触发缓存
import anthropic
client = anthropic.Anthropic()
# 首次请求:触发缓存写入
warmup = client.messages.create(
model="claude-sonnet-4-5-20250514",
max_tokens=1,
system=[{
"type": "text",
"text": "你的系统提示...",
"cache_control": {"type": "ephemeral"}
}],
messages=[{"role": "user", "content": "warmup"}]
)
print(f"缓存已写入: {warmup.usage.cache_creation_input_tokens} tokens")
# 后续请求即可命中缓存七、进阶:多 Provider 混合策略与检查清单
如果你的应用同时使用多个 Provider(如 Anthropic 处理复杂推理,OpenAI 处理简单任务),需要为每个 Provider 设计独立的缓存策略。
策略 1:按任务类型分配 Provider。复杂推理用 Anthropic(显式缓存,长 TTL,90% 折扣);简单补全用 OpenAI(自动缓存,零配置,50% 折扣)。
策略 2:按成本敏感度分配。成本敏感场景(高频客服)用 OpenAI 自动缓存;质量敏感场景(复杂推理)用 Anthropic 显式缓存。
策略 3:Gemini 作为长期缓存层。如果系统提示数天不变(企业级应用的角色定义),使用 Gemini 的 cachedContent API 预创建缓存,避免每次调用都支付写入成本。
关键洞察:多 Provider 混合策略的核心是"按场景优化",而不是"一刀切"。每个 Provider 的缓存机制不同,需要根据具体场景选择最合适的方案。
部署前检查清单:
- 稳定前缀长度 > 最小缓存单元(1024 tokens for Anthropic/OpenAI, 4096 for Gemini)
- 调用频率 > breakeven point(通常 2 次以上)
- cache_control 标记在稳定前缀末尾,而非变化内容
- 并发请求已串行化首次调用,或使用预热脚本
- TTL 设置与调用频率匹配(高频用短 TTL,低频用长 TTL)
- 监控缓存命中率和成本节省
- 跨 Provider 策略已独立配置
实际部署案例:某电商客服团队在启用 prompt caching 前后的对比数据。启用前,每月 API 账单 $4,200,其中系统提示(3,500 tokens)占输入成本的 68%。启用 Anthropic 显式缓存(1h TTL)后,系统提示部分成本降低 87%,整体账单降至 $1,950,月节省 $2,250。缓存命中率稳定在 94%,TTFT 从 1.8 秒降至 1.1 秒。关键成功因素:系统提示连续 6 个月未变更,客服调用频率高(每天 3,000+ 次),TTL 设置与调用间隔匹配(平均 2 分钟一次调用,远小于 1 小时 TTL)。该团队后续还将工具定义部分也加入了缓存断点,进一步将总成本降低了 12%。
失败案例:某初创公司尝试对创意写作应用启用 prompt caching。每次请求的 prompt 都不同(用户输入的故事开头千变万化),缓存命中率始终低于 5%。启用缓存后,由于每次都要支付 1.25 倍的写入成本但几乎没有读取命中,实际成本反而增加了 8%。教训:prompt caching 只适合前缀稳定的场景,对于高度动态的 prompt,应该考虑其他优化策略(如 prompt 压缩、模型降级或语义缓存)。
| 场景 | 推荐 Provider | 缓存策略 | 预期成本节省 |
|---|---|---|---|
高频客服(每天 > 1000 次) | Anthropic | 显式缓存,1h TTL | 80-90% |
简单补全(每天 > 500 次) | OpenAI | 自动缓存 | 40-50% |
长期稳定系统提示 | Gemini | cachedContent API | 60-80% |
低频复杂推理(每天 < 50 次) | Anthropic | 显式缓存,5min TTL | 10-30% |
多轮对话 Agent | Anthropic | 多断点缓存 | 70-85% |
⚠️ 常见踩坑
常见误区:假设一个 Provider 的缓存配置能迁移到另一个(不能);假设自动缓存总是比显式缓存差(不一定,取决于场景);忽略 Gemini 的存储成本(可能导致成本激增)。最佳实践:为每个 Provider 独立设计缓存策略,监控每个 Provider 的缓存命中率和成本节省,定期评估是否需要调整 Provider 分配策略。
参考资料
本文的技术细节和数据来自以下官方文档:
- Anthropic Prompt Caching 官方文档:cache_control 参数、TTL 配置、最小缓存单元、计费模型
- OpenAI Prompt Caching 官方指南:自动缓存机制、适用模型、50% 折扣
- Google Gemini Context Caching:cachedContent API、TTL 配置、存储计费
- Anthropic 定价页面:各模型的缓存写入/读取单价
- OpenAI 定价页面:各模型的缓存折扣与适用版本
三大 Provider 的定价信息可参考各自的官方定价页面。实际成本节省取决于你的具体工作负载、prompt 结构和调用频率,建议先用本文的 ROI 计算模板估算 breakeven point,再决定是否启用缓存。
延伸阅读:prompt caching 是 LLM 成本优化的重要一环,但它不是孤立的。在实际生产中,它通常与以下策略组合使用:prompt 压缩(减少总 token 数)、模型路由(根据任务复杂度选择不同模型)、语义缓存(应用层缓存相似请求的结果)。这些策略在 token-economics 和 llm-034 中有详细讨论,建议结合阅读以构建完整的成本优化知识体系。
🎯 相关面试题
巩固本篇知识点,备战 AI 岗位面试。
- 中级概念查看详解 →
Prompt Caching(提示缓存)如何降低 LLM 调用成本与延迟?
缓存固定前缀的 KV 状态,重复请求时跳过该前缀的 prefill 计算,省掉重复算力与延迟。
- 高级概念查看详解 →
比较 Subquadratic Attention 与标准 Self-Attention 的复杂度差异。长上下文 LLM 的工程挑战有哪些?
考察候选人对注意力机制复杂度的理解:标准自注意力为何是 O(n²)、亚二次注意力的实现路径(稀疏/低秩/线性/SSM)、SubQ 的工程突破,以及长上下文 LLM 在内存、延迟、成本上的工程挑战。
- 中级场景查看详解 →
LLM 如何跑在手机 / 边缘设备上?
端侧 LLM 靠 4bit 量化 + KV cache 管理 + 小模型 + 专用框架,主要受内存带宽限制。
- 高级概念查看详解 →
多查询注意力(MQA)与分组查询注意力(GQA)解决了什么问题?
让多个 Query 头共享 K/V 头,缩小 KV-cache 显存与解码访存,MQA 共享到 1 组、GQA 折中分多组。
