提示缓存怎么开?同一段前缀别重复付钱,Anthropic、OpenAI、DeepSeek 配置一次讲清(2026实战)

上个月我给一个客服机器人接了三家 API。跑了一周对账,发现一件挺别扭的事:输入 token 占了总成本的九成,其中又有九成的内容每次请求都一模一样。系统提示词、工具定义、产品手册,一个字都没变。

模型每次都老老实实从头 prefill 一遍。💰 这部分钱花得挺冤。后来我把提示缓存(Prompt Caching)打开,同样的流量,账单掉了一半多。

缓存到底在存什么

不是存文本。存的是这批 token 算过一遍之后留下的 KV 状态。下一轮请求如果前缀逐 token 相同,模型直接把这批 KV 读回来,跳过重复计算。

关键在「逐 token 相同」。它不是相似度匹配,也不是语义匹配。中间多一个空格、少一个换行、工具列表换个顺序,从那个位置往后全部作废。🔍

Anthropic 的文档把拼接顺序写得很明确:tools、system、messages,按这个顺序排,缓存覆盖到断点所在的那一块为止。OpenAI 的表述是前缀,静态内容必须放在最前面。所以真正决定命中率的不是参数调得多细,是提示词的结构排得对不对。

如果你还没搞清 prefill 到底在算什么,可以先看Prefill 和 Decode 是什么那篇。缓存省掉的就是这一段计算。

Anthropic:得自己打断点

默认不开。要在具体的内容块上挂 cache_control 字段。我一般挂在 system 那一块,因为工具说明和产品手册都在那里。

import anthropic

client = anthropic.Anthropic()

resp = client.messages.create(
    model="claude-sonnet-5-5",
    max_tokens=1024,
    system=[{
        "type": "text",
        "text": SYSTEM_PROMPT,      # 工具定义 + 产品手册,约 6000 token
        "cache_control": {"type": "ephemeral"},
    }],
    messages=[{"role": "user", "content": question}],
)

u = resp.usage
print(u.cache_creation_input_tokens, u.cache_read_input_tokens)

ttl 可以写成 "1h",价格是 5 分钟档的两倍。多轮对话我一般用默认的 5 分钟,因为用户基本在几分钟内会追问。隔一夜再回来的场景才值得上 1 小时。

计费按官方文档的口径是三档倍数:5 分钟 TTL 的写入是基础输入价的 1.25 倍,1 小时 TTL 的写入是 2 倍,命中读取是 0.1 倍。Opus 5.5 和 Sonnet 5.5 的命中读更低,0.05 倍。

最小可缓存长度按模型分档,这一点最容易踩空。Opus 5.5、Sonnet 5.5、Haiku 5.5 是 512 token;Opus 4.7 要 2048;Opus 4.6 和 4.5 要 4096。写不到门槛,请求照常返回 200,就是不缓存,也不报错。你的代码会以为一切正常。

判断有没有真的命中,看 usage 里那两个字段:cache_creation_input_tokens 和 cache_read_input_tokens 都是 0,说明缓存没吃到。

一个请求最多 4 个断点。断点把提示词切成几段,前面不变就复用前面。文档里还写了个 20 block 的回看窗口:系统会从断点往前最多找 20 个位置有没有旧写入。对话很长、又只在末尾追加消息的时候,在上一轮结尾多挂一个断点会更稳。

OpenAI:默认开着,给个路由提示就行

支持的模型默认启用,不用加任何参数。GPT-5.6 及以后最小 1024 token 才进缓存,早期模型的门槛要查模型对照表。

价格跟 Anthropic 一个路数。默认 30 分钟生命周期,写入 1.25 倍,读取 0.1 倍,GPT-6.1 Sol 和 Terra 是 0.05 倍。想要 12 小时生命周期,写入价 2.5 倍。

想提高命中率,用 prompt_cache_key 把共享同一段前缀的请求指到同一处去。多租户场景下这个字段挺有用,同一个客户的请求带同一个 key,别和其他客户混着打散。

from openai import OpenAI

client = OpenAI()

resp = client.responses.create(
    model="gpt-5.6",
    prompt_cache_key="support-bot-v3",   # 同一前缀用同一个 key
    input=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": question},
    ],
)

print(resp.usage.input_tokens_details)
# cached_tokens / cache_write_tokens 都在这里面

成本自己按字段算,别拍脑袋估。cached_tokens 乘读价,剩下的乘原价,两项加起来才对得上账单。

DeepSeek:硬盘缓存,前缀得完整对上

上下文缓存自动开,不需要配置。它的缓存落在硬盘上,后面请求和前面请求前缀重合的部分直接从缓存取,重合部分算命中。

有个细节和另外两家不太一样。命中要求完整匹配一个缓存单元,不是「大致像」就行。官方文档提到滑动窗口注意力让存储和匹配的方式变了,每个缓存前缀是独立完整的单元。

缓存写入发生在请求边界。你把一段长内容放在请求末尾,下次又改了它,前面那部分照样能命中,末尾那块基本白写。

真正管用的是把提示词切段

参数只决定开不开,结构决定能复用多少。我现在把提示词固定切成四段,从稳定到易变依次往后排:工具定义、角色与规则、知识库文档、对话历史。

好处是失效范围可控。只换知识库文档时,前两段的缓存还在。只追加对话时,前三段都不动。Anthropic 文档给的例子也是这个思路,四段各挂一个断点,换哪一段就只废哪一段之后的内容。

反面例子是把知识库文档放在 system 最前面。文档一更新,工具定义跟着一起重算,白白多付一遍 1.25 倍的写入费。我第一版就是这么写的,改完顺序之后命中率明显上来了。

四个我踩过的坑

1. 时间戳写在开头

「当前时间:2026-11-04 09:27:13」这种一行字,第一个 token 就和上一次不一样,后面全废。我第一次做的时候犯的就是这个错,命中率 0%,还以为是服务端问题。把它挪到用户消息里,或者干脆不传。

2. 工具列表顺序抖动

工具定义从字典里遍历出来,Python 3.7 之后是插入有序,但如果你从数据库查出来再拼,顺序可能不稳定。JSON schema 的键顺序也会变。两个工具对调位置,缓存就断了。我的做法是工具列表固定排一次,序列化结果写死,或者按名字排序后缓存成常量。

3. 把会变的东西塞进 system

用户昵称、会员等级、当天的活动文案,这些每分钟都在变。放进 system,等于每次都重新写一遍缓存,还多付 1.25 倍的钱。固定不变的放最前,会变的往 messages 里塞,中间可以用断点切开。

4. 以为缓存能跨工作区

Anthropic 是按工作区隔离的,同一组织下不同工作区之间不共享。你换了个 key、换了个环境就命中不了,这个不是 bug。

这笔账我算了一遍

假设处理一次前缀的成本是 1。不缓存,调三次是 3。打开缓存,写一次 1.25,读两次 0.2,加起来 1.45。也就是说第二次命中就已经回本,多花的 0.25 换回了 0.9。

我的门槛是:前缀稳定在 2000 token 以上,同一段前缀在生命周期内至少复用两次,就值得开。低于这个量,折腾的收益还不如把提示词删短一点来得直接。✅

上下文越拉越长的时候,光靠缓存不够用。裁剪和压缩这些动作我写在上下文管理那篇里了,两个手段搭着用效果最好。本地部署想省显存的话,思路在KV 缓存量化那篇,那是另一条路。

📌 最后一句实话:提示缓存改的是输入侧的账,输出质量一点没变。它不解决模型答得对不对,只解决你为同一段字付了几遍钱。这两件事经常被混在一起说,其实完全无关。

发表评论