一、什么是 Prompt Caching(提示词缓存)
Prompt Caching(提示词缓存,也称前缀缓存 Prefix Caching)是模型供应商提供的一项能力:当你多次请求中前缀内容完全一致时,供应商会把这段前缀的注意力计算结果缓存下来,后续命中缓存的请求只需计算增量部分。对 LLM 应用来说,系统提示词、知识库上下文、工具定义这些在每轮对话里几乎不变的内容,正是缓存的最佳对象。用好 Prompt Caching,能同时换来「更低的 token 成本」和「更低的首 token 延迟」两笔收益。尤其在 RAG、智能客服、代码助手这类「长上下文 + 高频复用」场景里,收益最为明显。
二、它为什么能大幅降本
大模型计费通常分「输入 token」「输出 token」「缓存写入」「缓存命中」几类。未命中时,整段前缀按标准输入价计费;命中缓存后,前缀改按远低于标准价的「缓存命中价」计费(通常只有标准输入价的 1/10 左右),而写入缓存时会有一次性的小额写入费。当同一前缀被高频复用时,写入费被摊薄,整体成本直线下降。换句话说,缓存把「每次重复计算」变成了「一次写入、多次低价读取」,复用次数越多越划算。
| 计费项 | 示例单价(每百万 token) | 说明 |
|---|---|---|
| 未命中输入 | $3.00 | 每轮全量计费 |
| 缓存写入 | $3.75 | 首次写入一次性计费 |
| 缓存命中 | $0.30 | 后续命中低价计费 |
三、Anthropic Claude 落地
举个真实账:一个客服助手每天处理 1 万次对话,每次都要带上 3000 token 的系统提示词与知识库。若不缓存,每天输入 token 约 3000 万;若 95% 请求命中缓存、按命中价 1/10 计费,输入成本直接降到原来的 15% 左右,一个月往往能省下数百美元。更关键的是,前缀的注意力计算已被缓存,首 token 延迟明显更短,用户体验也同步提升——这是「降本」与「提速」一举两得。
Claude 通过消息体里的 cache_control 字段标记「从这里开始缓存」。最常见的做法是在系统提示词或长上下文的末尾加一个 {"type":"ephemeral"} 的断点。
{
"model": "claude-sonnet-4-0",
"system": [
{
"type": "text",
"text": "你是一个严谨的 SQL 分析助手。以下是数据库 schema 与业务背景:\n...(长上下文)...",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [{"role":"user","content":"上季度华东区 GMV 是多少?"}]
}
首次请求会返回 cache_creation 相关的用量并收取写入费;只要前缀不变,后续请求命中缓存,返回体里的 cache_read_input_tokens 即命中计费的 token 数。
四、OpenAI 落地
OpenAI 的 Prompt Caching 更「自动」:当请求体里某个前缀达到最小 token 阈值(通常 1024 tokens)且内容稳定时,平台自动缓存,无需显式标记。你只需保证高频请求共享相同前缀即可。目前 GPT-4o、o 系列与部分嵌入模型均已支持,可在用量明细里直接看到 cached_tokens 字段,便于核算节省。
from openai import OpenAI
client = OpenAI()
# 把不变的 system + 知识库拼在前面,用户问题放最后
resp = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role":"system","content": SYSTEM_PROMPT + KNOWLEDGE_BASE}, # 稳定前缀
{"role":"user","content": user_question}, # 变化部分放末尾
],
)
print(resp.usage.prompt_tokens_details.cached_tokens) # 命中缓存的 token 数
注意:OpenAI 只对「最后一个可缓存点之前」的内容做缓存,且前缀必须从第一个 token 起完全一致,因此把易变内容放在消息末尾是关键。
五、工程实践:哪些内容该缓存
| 内容 | 是否适合缓存 | 原因 |
|---|---|---|
| 系统提示词 / 角色设定 | 非常适合 | 几乎不变,复用率极高 |
| 知识库 / RAG 检索结果 | 适合(需权衡) | 内容随查询变化;同一会话内可缓存 |
| 工具 / 函数定义 | 非常适合 | 函数清单长期稳定 |
| Few-shot 示例 | 适合 | 固定样例可复用 |
| 每轮用户新问题 | 不适合 | 每轮都变,放末尾 |
六、避坑清单
缓存不是「开了就省」,下面这些坑会让你白忙:
- 前缀必须逐 token 一致。多一个空格、换一行、改个标点都会让缓存失效,动态注入的时间戳、随机 id 要避开前缀区。
- 最小 token 阈值。Anthropic 默认至少 1024 tokens 才触发缓存;太短的前缀不值得缓存。
- 缓存有 TTL。空闲一段时间缓存会被回收,需要重新写入;高并发场景复用率高、成本低。
- 多轮对话保持前缀。把历史消息整体作为稳定前缀的一部分时,要固定截断策略,避免每轮前缀漂移。
- 别为缓存牺牲质量。不要为了凑稳定前缀而把本应随用户变化的上下文硬塞进缓存区。
七、一个省钱的封装示例
把「稳定前缀 + 可变问题」封装成函数,统一在末尾追加用户问题,天然契合缓存要求:
def ask_with_cache(client, stable_prefix: str, question: str) -> str:
# stable_prefix: 系统提示 + 知识库,长期不变
# question: 每轮不同,放在最后
resp = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": stable_prefix},
{"role": "user", "content": question},
],
)
used = resp.usage
cached = used.prompt_tokens_details.cached_tokens
print(f"缓存命中 token:{cached} / 输入 {used.prompt_tokens}")
return resp.choices[0].message.content
配合监控把 cached_tokens 比例做成看板,命中率长期低于 50% 就要回头检查前缀是否真的稳定。
八、多轮对话里的缓存策略
聊天机器人常要保留历史消息,但把整段历史逐字拼进每次请求,历史一变前缀就失效。稳妥做法是:把「系统提示词 + 工具定义 + 当前会话的固定背景」作为缓存前缀,历史对话做摘要化后放在前缀末尾、用户最新问题之前;这样既复用了稳定前缀,又避免历史无限膨胀拖慢首 token。Claude 还支持在消息中间设置多个 cache_control 断点,把「长期稳定」与「中期稳定」分层缓存,例如系统提示词做长期缓存、本会话摘要做中期缓存,进一步抬高整体命中率。
小结
Prompt Caching 是大模型应用上线后最值得做的成本优化之一:零模型改动、纯接入层改造,就能把重复前缀的计费砍到十分之一。把它和推理成本优化、上下文工程配合起来,再叠加RAG 混合检索与Function Calling 工具调用,就能在效果不降的前提下把单位请求成本压到最低。




