深色模式
上下文压缩与缓存
摘要:本文面向被长系统提示、长文档、多轮对话推高 token 成本与延迟的团队。覆盖 Anthropic Prompt Caching(缓存断点 + 5 分钟/1 小时 TTL)、OpenAI 自动提示缓存(≥1024 token 前缀、约 50% 折扣)、Google Context Caching(存储计费、长 TTL),并给出命中率验证、成本拐点与回滚要点。适用模型:Claude 3.5+/4(200K)、GPT-4o 系列(128K);价格与 TTL 标注
[版本相关,以官网为准]。
核心概念:缓存的是「输入前缀」
提示缓存(prompt/context caching)缓存的是请求中稳定不变的前缀(system 指令、长文档、few-shot 示例、工具定义),而非输出。当后续请求以完全相同的前缀开头时,只需付低价「缓存读取」token,并跳过前缀的重复处理,从而同时降成本与降延迟。这与传统缓存「存输出」不同——LLM 输出是动态的。
三家机制对比(来自各官方文档与汇总)
| 厂商 | 机制 | 最小可缓存 | 折扣 | TTL |
|---|---|---|---|---|
| Anthropic | 手动 cache_control 断点,最多 4 个 | 1024 token(Claude 3.5 为 2048) | 读 10% 基础输入价;写 1.25×(5min)/2×(1h) | 5 分钟(用即续)或 1 小时 |
| OpenAI | 自动,无需改代码 | 1024 token 前缀 | 缓存 token 约 50% 折扣 | 通常 5–10 分钟,低峰可延至 1 小时 |
| 手动 Context Caching | 较大(约 32K+,[版本相关]) | 按存储时长 + 低价读取计费 | 默认 1 小时,可定制更长 |
架构与原理:前缀精确匹配
缓存命中的前提是前缀 100% 精确匹配——从开头到缓存断点之间的所有文本/图片,哪怕一个空白符不同也会 miss。因此设计原则是:把稳定内容放在最前,动态内容(用户消息)放在最后。
Anthropic 官方数据(Chat with a book,100K 缓存前缀):无缓存首 token 延迟 11.5s → 有缓存 2.4s(-79%),成本 -90%;多轮长 system prompt 对话成本 -53%。这些为官方公布用例,非通用保证。
生产实践
缓存断点设计
- 系统提示、工具定义、知识库文档放最前,并标记缓存断点。
- 把「会变化的内容」(用户问题、检索到的不同片段)放在断点之后。
- Anthropic 最多 4 个断点,可按刷新频率分层(如「极少变」与「每小时变」分两层)。
- 注意:哪怕静态内容里嵌了会变的变量(如时间戳),也会破坏匹配——把时间等动态量放到断点之后。
1. Anthropic Prompt Caching
python
import anthropic
client = anthropic.Anthropic()
resp = client.messages.create(
model="claude-3-5-sonnet-20240620",
max_tokens=1024,
system=[{
"type": "text",
"text": "这里是一整本知识库 / 超长系统规则……(≥2048 token)",
"cache_control": {"type": "ephemeral"}, # 5 分钟 TTL 断点
}],
messages=[{"role": "user", "content": "{{user_question}}"}], # 动态内容在断点后
)
# 首请求返回 usage.cache_creation_input_tokens
# 后续命中返回 usage.cache_read_input_tokens
print(resp.usage)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2. OpenAI 自动缓存(无需改代码)
python
from openai import OpenAI
client = OpenAI()
# 只要 system + 前缀 ≥1024 token 且稳定,SDK 自动命中缓存
resp = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "超长固定指令与示例……"},
{"role": "user", "content": "{{user_question}}"},
],
)
# 命中时 usage 含 prompt_tokens_details.cached_tokens [版本相关]1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
3. Google Context Caching
python
# 伪代码:Google Gemini Context Caching [未实测,按官方 SDK 替换]
# 需显式创建缓存并指定 TTL,按存储时长计费
cache = genai.caching.create(
model="gemini-1.5-pro",
contents="超长固定上下文……",
ttl=3600, # 1 小时
)1
2
3
4
5
6
7
2
3
4
5
6
7
验证
bash
# 1. 观察 usage 字段:首请求 cache_creation>0,后续 cache_read>0 即命中
# 2. 对比开/关缓存的单位请求成本与延迟(压测)
# 3. 故意改前缀一个字符,确认 cache_read 归零(验证精确匹配)[未实测,示意]1
2
3
2
3
回滚与清理
缓存不是永远划算
- 写入溢价:Anthropic 缓存写比基础输入价贵 25%(5min)/100%(1h)。若两次请求间隔超过 TTL,缓存过期需重写,反而更贵。低频、稀疏请求不该开缓存。
- 5 分钟 TTL 太短:批处理友好、零星查询恼人。先用真实到达间隔算盈亏平衡点(Anthropic 官方举例约 1.67 次命中即回本)。
- 换模型版本/改系统提示会使整个缓存失效,回滚到旧前缀即可恢复。
故障排查
| 现象 | 原因 | 处理 |
|---|---|---|
| cache_read 始终为 0 | 前缀不精确匹配 | 检查空白符/变量/顺序,动态内容后置 |
| 开了缓存更贵 | TTL 内命中不足 | 评估到达间隔,低频则关缓存 |
| 长上下文仍 OOM | 仅缓存未降显存峰值 | 配合 FP8 KV / 限并发(见 上下文窗口) |
| 命中率波动 | 前缀被动态量污染 | 把时间戳等移出断点前 |
安全与合规
- 缓存内容若含敏感文档,会在 provider 侧保留至 TTL 到期——确认合规边界与数据驻留要求。
- 多租户不要共享同一缓存前缀:租户 A 的文档前缀若与租户 B 命中,可能泄露。每个租户用独立前缀或独立缓存键。
成本与性能
以 Anthropic Claude 3.5 Sonnet 公布价为例(价格 [版本相关,以官网为准]):基础输入 $3/MTok,缓存写 $3.75/MTok,缓存读 $0.30/MTok(即 10%)。一个 100K token 的固定知识库:首次写入约 $0.375,之后每次读取仅 $0.03(对比全价 $0.30),12 次查询总成本从 $3.6 降到约 $0.705。延迟方面,长前缀跳过重复 prefill,首 token 显著下降。
经济结论
缓存的核心价值在高吞吐、前缀稳定、到达密集的场景(代码库问答、文档助手、Agent 多轮工具调用)。低流量、前缀多变、到达稀疏的场景,请先做盈亏平衡计算,不要盲开。