如果你正在发送一段很长的系统提示 —— RAG 上下文、工具目录、智能体规则、 示例 —— 那你很可能每次调用都在付全额输入费用。Anthropic 的提示缓存能把 缓存部分的费用降到 费率的 10%。OpenAI 也隐式地做着同样的事。大多数团队都觉得 这 30 分钟的功夫值得,因为它能稳稳地把输入成本砍掉 60~90%。
Brievio 把这两种风格都原样透传,对接的是真模型。Anthropic 风格的 cache_control 在 Messages API 上生效;OpenAI 风格的自动缓存 在 Chat Completions API 上生效。这篇文章会把两者都走一遍,讲清楚那些会 悄悄让缓存失效的坑,以及如何验证你的命中率。
前后对比
一个朴素的循环,把同一段 18K token 的系统提示发了十次:
# 大多数人一开始的写法:每次调用都为整段提示重新付一遍钱。
import anthropic
client = anthropic.Anthropic(
api_key="sk-brievio-...",
base_url="https://api.brievio.com",
)
SYSTEM = open("system-prompt.md").read() # 18,000 个 token 的规则 + 示例
# 一个会话里有 10 个用户问题。每次调用都发送这段 18K token 的系统块。
for question in questions:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system=SYSTEM,
messages=[{"role": "user", "content": question}],
)
# 每次调用的成本(仅输入):18,000 × $3 / 1M = $0.054
# 10 次调用:光输入就要 $0.54。现在把系统块标记为可缓存 —— 只多加一个字段:
# 解法:把静态前缀标记为可缓存。第一次调用之后,约 5 分钟内
# 的后续调用,对缓存命中的部分只按输入费率的 10% 计费。
# 同样的答案,便宜 89%。
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system=[
{
"type": "text",
"text": SYSTEM,
"cache_control": {"type": "ephemeral"}, # 标记为可缓存
}
],
messages=[{"role": "user", "content": question}],
)
# 第一次调用(写缓存): 18,000 × $3 / 1M = $0.054
# 第 2~10 次(命中缓存):18,000 × $0.30 / 1M = 每次 $0.0054
# 合计:$0.054 + 9 × $0.0054 = $0.103 (原本是 $0.54 —— 省了 81%)你刚刚在这个会话上省下了 81% 的输入成本。第一次调用其实比朴素版本略贵一点(写缓存约为输入费率的 1.25 倍)。第 2 到第 10 次 调用按费率的 10% 计费。盈亏平衡点在第 2 次 —— 到第 3 次你就开始赚了。到 第 10 次,那就是一边倒的胜负了。
OpenAI 风格:什么都不用做
如果你用的是 OpenAI SDK,Chat Completions 会对任何超过 1,024 个 token 的 提示自动缓存其前缀。无需开关,无需配置。Brievio 背后的同一个模型, 无论你走的是 Anthropic 路线还是 OpenAI 路线,都能拿到这个折扣:
# OpenAI Chat Completions 风格 —— 对于 ≥1024 个 token 的提示自动缓存。
# 无需设置任何开关。"usage" 对象会告诉你哪些命中了缓存。
from openai import OpenAI
client = OpenAI(
api_key="sk-brievio-...",
base_url="https://api.brievio.com/v1",
)
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "system", "content": LONG_SYSTEM_PROMPT}, # >1024 个 token
{"role": "user", "content": "Latest question…"},
],
)
# 在响应里:
# resp.usage.prompt_tokens_details.cached_tokens → 17,800
# resp.usage.prompt_tokens → 18,200
# 17.8K/18.2K = 98% 的输入来自缓存。账单会自动反映这一点。读取 usage.prompt_tokens_details.cached_tokens,就能看到有多少 是从缓存里取的。固定前缀越大,省得越多。一条经验法则:如果你的系统提示比 可变的用户内容还短,那缓存就没起多大作用。重新组织一下提示,让静态部分又 大又靠前。
多层缓存 —— 最多 4 个断点
对于某些层比其他层变化更快的智能体循环,可以设置多个 cache_control 断点。每一个都是到该位置为止全部内容的一次 快照:
# Anthropic 每个请求最多支持 4 个缓存断点 —— 用它们
# 在后面的层发生变化时,依然让缓存保持热度。
client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system=[
{"type": "text",
"text": ROLE_AND_RULES, # 约 3,000 个 token
"cache_control": {"type": "ephemeral"}}, # 断点 1
{"type": "text",
"text": LARGE_KNOWLEDGE_BASE, # 约 15,000 个 token,很少改动
"cache_control": {"type": "ephemeral"}}, # 断点 2
],
messages=[
{"role": "user",
"content": [
{"type": "text",
"text": CONVERSATION_HISTORY, # 每一轮都在增长
"cache_control": {"type": "ephemeral"}}, # 断点 3
{"type": "text", "text": new_question},
]},
],
)
# 当 CONVERSATION_HISTORY 变化时,断点 1+2 仍然命中缓存。
# 只有断点 3 + 新问题按完整输入费率计费。缓存键是整段前缀。在位置 N 处加一个 token,N 及其之后的每一个断点都会 失效。顺序很重要:最稳定的内容放最前面。规则层应该很少改动; 知识库每周更新;对话则每一轮都在增长。
那些会悄悄让缓存失效的常见做法
- 在提示里放当前日期或 request_id。 每次调用都是一个新前缀,缓存命中率为 0%。请对提示输入做哈希,并在多次 调用之间做对比。
- 不确定的系统提示拼装方式。如果你是用一个字典来拼装系统 提示,在某些 Python 版本里字典的遍历顺序是有讲究的。请显式地对键排序。
- 缓存的存活时间约为 5 分钟。稀疏的流量模式(每 10 分钟 才一次调用)会一次都命不中。要么把调用攒成一批,要么就接受这个损失。
- 1,024 个 token 的最低门槛。低于 1K token,OpenAI 风格的 缓存不会启用。把零碎的静态片段合并成一段更长的前缀。
- 工具 / 函数定义也是前缀的一部分。 往目录里新加一个工具,会让所有人的缓存都失效。请保持工具目录稳定,并为 它做版本管理。
验证命中率
看不见的缓存算不上工程 —— 那只是在碰运气。在每一次调用上都记录 usage:
# 始终读取 usage。如果你预期会命中、cached_tokens 却是 0,
# 说明哪里出了问题 —— 通常是某个不确定的前缀在作祟。
resp = client.messages.create(...)
u = resp.usage
print({
"input_uncached": u.input_tokens,
"input_cache_read": u.cache_read_input_tokens,
"input_cache_write": u.cache_creation_input_tokens,
"output": u.output_tokens,
})
# 一个常见的坑:在系统提示里放上今天的日期或一个 request_id,
# 会悄无声息地让缓存失效。请对输入做哈希;在第 2 次完全相同的调用上,
# 确认 cache_read_input_tokens 不为零。在 Brievio 仪表板里, 用量页面会按模型、按天展示缓存占比。如果你 看到 cache_read_input_tokens 占总输入的百分比在上升,说明缓存 正在生效。如果它一直停在 0、或者剧烈波动,那就对照上面那张坑点清单逐条 排查。
在 Brievio 上它到底要花多少钱
每个模型的缓存费率都在 定价页面上公开列出:
- Anthropic 模型:缓存读取是输入费率的 10%。写缓存按输入 费率计费 —— 没有任何上游附加费。
- OpenAI / Gemini 模型:缓存读取是输入费率的 20%(这是 供应商公开的比例)。
- 所有缓存价格反映的都是 Brievio 的标价 —— 大约比供应商官方费率 低 15%。所以在 Brievio 上一次 Sonnet 4.6 的缓存读取是
$3 × 0.95 × 0.10 = $0.285 per 1M tokens。比未缓存的输入费率 便宜约 10 倍。
什么时候缓存并不是答案
有几种情况,这个功夫并不值得:
- 短提示(总共 <1K token)。额外开销占了大头;干脆别 折腾了。
- 一次性任务,没有重复流量。第一次调用略贵一点 —— 缓存 只有在第 2 次及以后才回本。
- 高输出、低输入的任务(创意写作、代码生成)。输入本来 在账单里就只占一小份。把精力放在给输出设上限上吧。
至于其他所有场景 —— RAG 聊天机器人、带固定工具目录的智能体、对着一份 静态评分标准跑的分类器、用一致 few-shot 示例的结构化抽取流水线 —— 缓存 是你能在一个下午就上线的、投入产出比最高的优化。把它和 我们成本优化手册里另外四项技术 搭配起来,在输出质量毫不妥协的前提下,降本 70% 是完全现实的。
已经在用 Brievio 了?打开 /app/usage,查一下你最大那个模型的缓存列。 如果它是零,那你就是在白白把钱留在桌上。完整指南: /docs/caching。