Prompt Caching 工程实战:省 90% LLM 成本的技巧
Anthropic / OpenAI / Gemini 都支持的"prefix caching"——让重复的 system prompt 不再重复花钱。
LLM 调用的费用结构里,输入 token 经常是最大头—— 特别是有大段 system prompt、RAG 检索内容、文档分析的场景。
Prompt Caching 是过去两年最重要的成本优化技术:
同一前缀只算一次,后续命中降到 1/10 的价格**。
谁会被坑
典型场景:
你的 system prompt = 5000 tokens
每次用户请求 = 100 tokens 用户输入 + 800 tokens 输出
每次实际计费 = 5000 + 100 input + 800 output
但 system prompt 永远不变——
为什么每次都要重新付钱算它?
如果一天 1 万次请求:
- 没缓存:5000 × 10000 = 5000 万 input tokens
- 有缓存:5000 一次 + 100 × 10000 = 600 万 input tokens → 省 88%
三家厂商的 Prompt Caching 对比
Anthropic(最显式 + 最实用)
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
system=[
{"type": "text", "text": "短指令"},
{
"type": "text",
"text": LARGE_KNOWLEDGE_BASE, # 大段内容
"cache_control": {"type": "ephemeral"} # ← 关键
}
],
messages=[{"role": "user", "content": user_question}]
)
效果:
- 写入缓存:1.25× 正常 input 价格(首次)
- 读取缓存:0.1× 正常价格 = 省 90%
- 缓存生效期:5 分钟(ephemeral),新版可买 1 小时
可对多个段分别打 cache_control,最多 4 个 cache breakpoint。
OpenAI(自动)
OpenAI 没有显式 cache_control—— 它会自动检测你重复发送的前缀(>= 1024 tokens),自动缓存:
# 不需要任何特殊配置
response = openai.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": LARGE_SYSTEM_PROMPT},
{"role": "user", "content": user_q}
]
)
# 第二次同样请求 → 自动命中 cache → 50% 折扣
效果:
- 自动检测,无需改代码
- 缓存命中:50% 折扣(不如 Anthropic 的 90% 狠)
- 缓存生效期:~5-10 分钟
Gemini(context caching)
需要显式创建:
from google.generativeai import caching
cache = caching.CachedContent.create(
model="gemini-2.5-pro",
system_instruction=LARGE_SYSTEM_PROMPT,
contents=[],
ttl=datetime.timedelta(hours=1)
)
response = model.generate_content(
"user question",
cached_content=cache.name
)
效果:
- 缓存读取:~75% 折扣
- TTL 可控(最长几小时)
- 适合长文档 RAG
缓存命中的”对齐”原则
所有家厂商都有同一个底层逻辑:
缓存是按 token 前缀匹配的——前缀必须一字不差。
意思是:
请求 A:
system: "You are an assistant. Knowledge base: [10k tokens]"
user: "What is X?"
请求 B:
system: "You are an assistant. Knowledge base: [10k tokens]"
user: "What is Y?"
↑
10k tokens 完全相同 → 命中缓存 → 省钱
请求 C:
system: "You are a helpful assistant. Knowledge base: [10k tokens]"
↑
差一个字 → 不命中 → 全价
这是为什么放可变内容时要特别小心——日期、随机 ID、用户名等放到前面会破坏缓存。
一个实战模板
def build_request(user_query: str, user_context: dict):
return {
"system": [
# ① 缓存层 1:不变的指令
{"type": "text", "text": SYSTEM_INSTRUCTIONS,
"cache_control": {"type": "ephemeral"}},
# ② 缓存层 2:大段知识库(不变)
{"type": "text", "text": KNOWLEDGE_BASE_10K,
"cache_control": {"type": "ephemeral"}},
# ③ 不缓存:每次都变的用户上下文
{"type": "text", "text": f"User profile: {json.dumps(user_context)}"},
],
"messages": [{"role": "user", "content": user_query}]
}
顺序很关键:变的内容必须放前缀之后。把变的东西塞前面会让所有缓存失效。
常见 RAG 场景
# ❌ 错误:检索结果在前,问题在后
prompt = f"""
{retrieved_chunks} ← 每次问题不同,检索结果也不同
SYSTEM_INSTRUCTIONS
User: {user_query}
"""
# ✓ 正确:固定部分在前
prompt = f"""
SYSTEM_INSTRUCTIONS ← 缓存这部分
RETRIEVAL_PROMPT_TEMPLATE
{retrieved_chunks} ← 这部分变化,不缓存
User: {user_query}
"""
不能保证缓存——但最大化命中率。
缓存的盲区
1. ❌ 流式响应起初不算缓存命中
模型刚开始流式输出时,缓存信息可能还没传回。监控成本要看完整响应的 token usage。
2. ❌ tool / function calling 改变前缀
当你的 tools 列表变了,前缀也变了——别频繁修改 tools。
3. ❌ Conversation memory 容易破坏缓存
# 多轮对话,每次都把整个历史发回
messages = [
{"role": "system", "content": ...}, # 缓存
{"role": "user", "content": "Q1"},
{"role": "assistant", "content": "A1"}, # 历史
{"role": "user", "content": "Q2"},
{"role": "assistant", "content": "A2"}, # 历史
{"role": "user", "content": "Q3"}, # 新问题
]
每次新对话轮次,前缀变了!需要在 system 段后立刻打 cache_control,再让历史增长。
监控
OpenAI / Anthropic 都在 response 里返回缓存使用情况:
# Anthropic
print(response.usage)
# cache_creation_input_tokens: 5000 ← 写入了 5000 tokens
# cache_read_input_tokens: 0 ← 命中了 0 个
# 第二次请求同 prompt
# cache_creation_input_tokens: 0
# cache_read_input_tokens: 5000 ← 命中!
埋点记录这两个数字,计算每月省了多少钱:
def estimated_savings(cache_read_tokens, model="claude-sonnet"):
# 90% off on cache reads
full_price_per_M = 3.00 # input
cached_price_per_M = 0.30
saved_per_M = full_price_per_M - cached_price_per_M
return cache_read_tokens / 1_000_000 * saved_per_M
反模式速查
| 反模式 | 后果 |
|---|---|
| 把当前时间戳放到 system prompt 里 | 每次缓存失效 |
| 把 user ID 放在 system 前缀 | 每个用户独立缓存 = 没省 |
| Tools 列表频繁改 | 缓存频繁失效 |
| < 1024 token 也想缓存 | OpenAI 不会缓存 |
| 一段拆得太细,多个 cache breakpoint | Anthropic 限制 4 个 |
配合 Semantic Cache
Prompt Caching 是”完全相同的前缀才省”。 Semantic Cache 是”意思相似的问题直接返回上次答案”:
用户 A: "What's the capital of France?" → 调用 LLM, cache result
用户 B: "France capital?" → embedding 接近 → 返回 A 的答案
两者互补——Prompt Caching 省 input tokens;Semantic Cache 完全跳过 LLM 调用。
收益预估
按 2026 价格,5000 token system prompt + 1 万次/天调用:
| 状态 | 月度 input 成本 |
|---|---|
| 无缓存 | $4,500 |
| OpenAI 自动 50% off | $2,250 |
| Anthropic 90% off | $450 |
一个简单优化,每月省几千美金。
Prompt Caching 是 2024-2025 最被低估的 LLM 工程优化:
- 配置成本:< 1 小时
- 收益:省 50-90%
- 适用场景:80% 的应用都符合
任何用 LLM 的产品上线后,第一周就该实现 prompt caching。 不做 = 每月白送几千上万美金给 OpenAI / Anthropic。
下一篇推荐:L4-12 LLM 成本优化 或 L7-03 推理优化。
🚧 3 个常见坑
坑 1:以为缓存自动命中 需要显式标记缓存点(Anthropic)/ 配置 prefix(OpenAI)——不配置等于没用缓存。
坑 2:缓存的 TTL 太短 默认 5-10 分钟,每次请求会刷新——高并发场景能保持热缓存,低并发反而频繁 miss。
坑 3:把动态内容放到 cached 段 缓存段必须前缀稳定——把 user query / 时间戳 / random ID 放到 cached prompt 里 = 100% miss。
读到这里说明你认真在学 🎯
订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。
讨论区
· 用 GitHub 账号登录评论src/components/Comments.astro 顶部填入
仓库 ID 和分类 ID(见组件注释里的配置步骤)。