Prompt Caching:将 LLM 成本降低 90% 的优化方案
大多数基于 LLM 构建产品的团队都多付了 60%–90% 的费用。这并不是因为他们使用了错误的模型或提示词效率低下,而是因为他们在每次请求中都在重复处理相同的 Token。提示词缓存(Prompt caching)可以解决这个问题,且只需大约 10 分钟即可实现。然而,它仍然是生产级 LLM 系统中利用率最低的优化手段之一。
实际情况是:每次你向 LLM API 发送请求时,模型都会对提示词中的每一个 Token 运行注意力机制(Attention)。如果你的系统提示词(System prompt)有 10,000 个 Token,且每天处理 1,000 个请求,那么你每天仅为提示词中的静态部分(即永不变化的上下文)就要支付 1,000 万个 Token 的处理费用。提示词缓存会存储中间计算结果(即 Key-Value 注意力状态),以便后续请求可以完全跳过这部分工作。
前缀缓存的实际工作原理
在底层,Transformer 模型会构建一个 “KV 缓存” —— 即在注意力机制期间计算出的键值张量(Key-Value tensors)。前缀缓存(Prefix caching)跨 API 调用持久化这些张量。当一个新请求与已缓存的请求共享相同前缀时,模型会跳过重新计算这些注意力状态,直接处理新 Token。
不同供应商的经济模型各不相同:
Anthropic Claude 需要通过 cache_control 标记显式开启。缓存写入的成本比标准输入 Token 高 25%,但缓存读取的成本仅为基础费率的 10% —— 即 90% 的折扣。对于 Claude 3.5 Sonnet,写入费用为 0.30/M(标准费用为 $3.00/M)。
盈亏平衡点非常低:每个缓存前缀只需 1.4 次缓存命中即可回本。此后,每一次额外的读取都是纯节省。
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
system=[{
"type": "text",
"text": large_static_context, # 你的文档、代码库摘要等
"cache_control": {"type": "ephemeral"}
}],
messages=[{"role": "user", "content": user_query}]
)
OpenAI 自动进行缓存 —— 无需更改代码。你可以享受缓存 Token 的 50% 折扣,且没有写入溢价。通过 response.usage.prompt_tokens_details.cached_tokens 监控缓存效率。
自托管 vLLM 通过基于哈希表的 KV 块存储实现自动前缀缓存(APC),具备 O(1) 查找效率。对于具有共享前缀的工作负载,生产环境部署报告显示,其吞吐量比原生实现提高了 14–24 倍。
各供应商的主要技术限制:
- 至少 1,024 个 Token 才能符合缓存条件
- 默认 5 分钟生存时间(TTL)(随活动延长;Anthropic 可达 1 小时)
- 每个请求最多 4 个缓存检查点(Anthropic)
- 缓存对你的组织是私有的 —— 不存在跨租户泄漏
提示词结构即缓存架构
前缀缓存仅在请求共享完全相同的前缀时有效。这听起来显而易见,但对于如何构建提示词有着不容忽视的影响。
金科玉律:静态内容在前,动态内容在后。
[系统提示词 — 所有请求通用]
[检索到的文档 — 给定会话内稳定]
[对话历史 — 随轮次增长]
[当前用户消息 — 始终为新内容]
如果你在系统提示词中注入用户名、时间戳或请求 ID,你将在每次请求时导致缓存失效。缓存键(Cache key)是精确的字节序列;哪怕是一个字符的区别也会创建一个新的缓存条目。
对于 RAG 系统,这改变了检索架构。与其每次从头开始构建一个长提示词,不如显式地缓存检索到的文档:
# 缓存静态文档上下文
system_with_docs = [{
"type": "text",
"text": f"{base_system_prompt}\n\n# Knowledge Base\n{retrieved_docs}",
"cache_control": {"type": "ephemeral"}
}]
# 仅问题部分会变化
messages = [{"role": "user", "content": question}]
对于对话代理,你在 Anthropic 上最多可以缓存 5 轮对话。这意味着随着对话的进行,系统提示词和早期交流保持静态的多轮交互会变得越来越便宜 —— 这与你预期的相反。
并行执行陷阱
这是一个会让团队措手不及的失败模式:并行化会破坏缓存。
对于大文档,创建缓存需要 2–4 秒。如果你在第一个缓存写入之前就发出了 10 个并行请求,那么每个请求都会独立处理完整的提示词。你会得到 10 次缓存写入、0 次缓存读取,以及一份比预期高出 5–10 倍的账单。
真实数据:在没有进行缓存预热(Cache warming)的情况下盲目并行化的团队,其命中率低至 4%。解决方法是在并行处理之前进行专门的预热调用:
async def process_document_parallel(doc: str, questions: list[str]):
# 先通过一个极简调用来预热缓存
await client.messages.create(
system=[{
"type": "text",
"text": doc,
"cache_control": {"type": "ephemeral"}
}],
messages=[{"role": "user", "content": "Ready."}],
max_tokens=1
)
# 现在所有并行请求都会命中缓存
tasks = [ask_question(doc, q) for q in questions]
return await asyncio.gather(*tasks)
对于包含 3 个并行问题的 30,000 Token 文档的成本对比:不进行预热为 0.14 —— 仅此一项改动就降低了 59% 的成本。
超越前缀缓存:全栈方案
前缀缓存(Prefix caching)处理重复的前缀,但会漏掉措辞不同但语义等价的查询。语义缓存(Semantic caching)增加了另一层,在查询到达 API 之前就进行拦截。
架构如下:对传入的查询进行嵌入(embedding),并与之前查询的向量库进行搜索。如果之前回答过足够相似的查询,则直接返回缓存的响应 —— 100% 节省,完全无需调用 API。
来自在常见问题解答(FAQ)类工作负载上运行语义缓存的团队的生产指标:
- 61–69% 的缓存命中率
- 97% 以上的命中准确率(衡量缓存的答案是否合适)
- 命中时延迟降低 40–50%
完整的复式堆栈结构如下:
请求
→ 语义缓存(精确/近乎重复的查询) → 100% 节省
→ 前缀缓存(共享的静态上下文) → 50–90% 节省
→ 全量推理 → 0% 节省
一个调优良好、拥有稳定系统提示词(system prompts)、一致的文档检索和重复性用户查询的系统,可以将 70–80% 的 token 路由到其中一个缓存层。
语义缓存有明显的失败场景:创意生成、个性化响应、时效性信息,或查询极少重复的工作负载。向量相似度阈值也需要校准 —— 固定的 0.8 余弦相似度在不同类型的查询中表现不佳。根据查询复杂度进行调整的自适应阈值优于静态阈值。
缓存受损的场景
缓存并不总是百利而无一害。请留意以下情况:
单次工作流:如果每个用户会话都是完全唯一的,没有共享上下文,你将支付 25% 的写入溢价(Anthropic)且没有任何读取收益。在到处启用 cache_control 之前,先计算一下成本。
动态系统提示词:重度个性化系统提示词(注入用户偏好、当前日期、动态指令)的团队会完全破坏前缀缓存。考虑是否可以将个性化内容移动到提示词后面独立的部分。
短提示词:在 1,024 token 的阈值以下,缓存根本不会生效。如果你的系统提示词只有 500 token,无论是人为扩充还是重组结构都没有帮助 —— 你需要关注其他地方。
冷启动期间的缓存未命中:新部署的服务、部署重启后,或在缓存 TTL(生存时间)频繁过期的低流量时段,在缓存变热之前,实际成本会更高。在成本建模时要考虑到这一点。
关键指标监控
Anthropic 和 OpenAI 都会在 API 响应中返回缓存使用情况。建议建立一个仪表板来跟踪:
- 缓存命中率:
cache_read_input_tokens / total_input_tokens—— 稳定提示词工作负载的目标应为 70% 以上 - 单次请求的实际成本:综合缓存读取、写入和未命中的混合成本
- 写/读比例:比例过高意味着提示词变化过于频繁或 TTL 过期
如果在请求相似的情况下命中率依然较低,请检查你的提示词结构。常见原因:系统提示词中的时间戳、静态部分中的用户 ID,或在请求之间略有差异的文档内容。
实践清单
如果你现在正在构建基于 LLM 的产品且尚未实现缓存:
- 识别超过 1,024 token 的系统提示词 —— 这些是立竿见影的优化点
- 为静态内容添加
cache_control: {"type": "ephemeral"}(Anthropic),或确认已开启自动缓存(OpenAI) - 审查提示词结构:所有静态内容必须置于动态内容之前
- 如果在共享文档上运行并行工作负载,请添加一个预热调用(warmup call)
- 监控生产环境的命中率,并迭代提示词结构,直到达到 70% 以上
从经济角度来看,这是 LLM 产品中投资回报率(ROI)最高的优化手段之一。在 API 支出巨大时,优化与未优化的缓存策略之间的差距可能超过整个工程团队的成本。花 10 分钟添加 cache_control 标记,可能是你基础设施积压任务中最值得投入的时间。
