如果你发送很长的系统提示词——RAG 上下文、工具目录、代理规则、示例——你可能每次调用都在支付完整的输入价格。Anthropic 的提示缓存会将缓存部分的费用降至费率的 10%。OpenAI 会在后台隐式执行相同的操作。大多数团队认为这 30 分钟的工作值得,因为它能稳定地将输入费用这一项降低 60–90%。
Kunavo 两种都支持。你自己的 cache_control 分界点会原样传递给 Messages API;而在 Chat Completions 和 Responses API 中,格式没有可发送的此类字段,Kunavo 会替你设置分界点,并在你发送的内容周围补充分界点,同时绝不会超过 4 个的上限。本文将介绍两种情况、会悄悄使缓存失效的陷阱,以及如何验证命中率。
前后对比
一个朴素循环,将相同的 18K token 系统提示词发送十次:
# What most people start with: every call re-pays for the whole prompt.
import anthropic
client = anthropic.Anthropic(
api_key="sk-kn-...",
base_url="https://api.kunavo.com",
)
SYSTEM = open("system-prompt.md").read() # 18,000 tokens of rules + examples
# 10 user questions in a session. Every call sends the 18K-token system block.
for question in questions:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system=SYSTEM,
messages=[{"role": "user", "content": question}],
)
# Cost per call (input only): 18,000 × $3 / 1M = $0.054
# 10 calls: $0.54 in input alone.现在将系统块标记为可缓存——只需增加一个字段:
# The fix: mark the static prefix as cacheable. After the first call,
# subsequent calls within ~5 minutes pay 10% the input rate on the cached
# portion. Same answer, 89% cheaper.
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system=[
{
"type": "text",
"text": SYSTEM,
"cache_control": {"type": "ephemeral"}, # mark cacheable
}
],
messages=[{"role": "user", "content": question}],
)
# First call (cache write): 18,000 × $3.75 / 1M = $0.0675 (1.25× input)
# Calls 2–10 (cache hit): 18,000 × $0.30 / 1M = $0.0054 each
# Total: $0.0675 + 9 × $0.0054 = $0.116 (was $0.54 — 78.5% saved)你刚刚节省了本次会话输入费用的 78.5%。第一次调用实际上比朴素版本稍微更贵(写入缓存约为输入费率的 1.25 倍)。第 2 到第 10 次调用按 10% 的费率计费。转折点在第 2 次调用——到第 3 次时你已经节省费用。到第 10 次时,差距已经非常明显。
OpenAI 风格:无需操作
OpenAI Chat Completions 格式没有 cache_control 字段,因为 OpenAI 会在自己的服务器上隐式缓存。Claude 不会这样做——它只缓存分界点标记的内容。因此,当你通过 /v1/chat/completions 或 /v1/responses 使用 Claude 模型时,Kunavo 会替你设置分界点:当对话至少包含一轮 assistant 后,在最后一条消息上设置一个滚动分界点,然后在 system 之后和 tools 之后各设置一个。在模型首次回答之后发送的 system 或 developer 消息会留在对话中,而不会被提升到 system(除非它自己的分界点要求一小时缓存)。如果请求以这样一条消息结尾,滚动分界点会放在紧挨着它前面的那个块上;剩余可用的分界点则放在最后几条后面还有内容的此类消息前面。你自己设置的任何分界点都会保持原位——Kunavo 只填充你留空的位置,并且最多填充到 4 个。无需配置,无论通过 Anthropic 路线还是 OpenAI 路线接入,同一模型的费用都相同:
# OpenAI Chat Completions style — Kunavo sets the breakpoints for you.
# No flag to set. The "usage" object tells you what was cached.
from openai import OpenAI
client = OpenAI(
api_key="sk-kn-...",
base_url="https://api.kunavo.com/v1",
)
resp = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[
{"role": "system", "content": LONG_SYSTEM_PROMPT}, # >1024 tokens
{"role": "user", "content": "Latest question…"},
],
)
# In the response:
# resp.usage.prompt_tokens_details.cached_tokens → 17,800
# resp.usage.prompt_tokens → 18,200
# 17.8K/18.2K = 98% of input came from cache. Bill reflects that automatically.阅读 usage.prompt_tokens_details.cached_tokens,查看有多少内容来自缓存。固定前缀越大,节省越多。经验法则是:如果系统提示词短于可变的用户内容,缓存的作用就不大。重新组织提示词,让静态部分更大并置于前面。
多层缓存——最多 4 个分界点
对于不同层变化速度不同的代理循环,设置多个 cache_control 分界点。每个分界点都是截至该位置的全部内容的快照:
# Anthropic supports up to 4 cache breakpoints per request — use them
# to keep the cache hot even as later layers change.
client.messages.create(
model="claude-sonnet-4-6",
max_tokens=600,
system=[
{"type": "text",
"text": ROLE_AND_RULES, # ~3,000 tokens
"cache_control": {"type": "ephemeral"}}, # breakpoint 1
{"type": "text",
"text": LARGE_KNOWLEDGE_BASE, # ~15,000 tokens, rarely changes
"cache_control": {"type": "ephemeral"}}, # breakpoint 2
],
messages=[
{"role": "user",
"content": [
{"type": "text",
"text": CONVERSATION_HISTORY, # grows each turn
"cache_control": {"type": "ephemeral"}}, # breakpoint 3
{"type": "text", "text": new_question},
]},
],
)
# When CONVERSATION_HISTORY changes, breakpoints 1+2 still hit cache.
# Only breakpoint 3 + the new question pay full input rate.缓存键是整个前缀。在位置 N 添加一个 token,会使 N 及其之后的每个分界点失效。顺序很重要:最稳定的内容放在最前面。规则层应该很少变化;知识库每周更新;对话每轮增长。
悄悄破坏缓存的常见方式
- 在提示词中放入当前日期或 request_id。每次调用都会产生新的前缀,缓存命中率为 0%。请对提示词输入进行哈希,并在多次调用之间比较。
- 以非确定方式组装系统提示词。如果你从字典构建系统提示词,某些 Python 版本中的字典迭代顺序会产生影响。请显式地对键排序。
- 缓存生命周期约为 5 分钟。流量稀疏时(每 10 分钟调用一次)不会命中缓存。要么批量调用,要么接受这项损失。
- 1,024 token 的最低要求。低于 1K token 时,OpenAI 风格的缓存不会启用。请将较小的静态片段合并为一个更长的前缀。
- 工具/函数定义属于前缀的一部分。向目录添加新工具会使所有人的缓存失效。保持工具目录稳定,并对其进行版本管理。
验证命中率
看不见的缓存不是工程,而是祈祷。每次调用都记录 usage:
# Always read usage. If cached_tokens is 0 when you expected a hit,
# something's wrong — usually a non-deterministic prefix.
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,
})
# A common gotcha: putting today's date or a request_id in the system prompt
# silently invalidates the cache. Hash your inputs; verify cache_read_input_tokens
# is non-zero on the 2nd identical call.在 Kunavo 控制台中,使用情况页面会按模型、按天显示缓存拆分。如果你看到 cache_read_input_tokens 占总输入的比例不断增加,说明缓存正在生效。如果它保持为 0 或剧烈波动,请逐项检查上面的陷阱清单。
在 Kunavo 上实际要花多少钱
每个模型的缓存费率都公布在价格页面:
- Anthropic 模型:缓存读取为输入费率的 10%——在Claude Fable 5.1上的比例为2.5%,在Claude Opus 5.5上的比例为5%。缓存写入为输入费率的 1.25 倍——这是 Anthropic 的五分钟倍率;Kunavo 对一小时写入也按 1.25 倍计费,低于 Anthropic 的 2 倍。
- OpenAI / Gemini 模型:缓存读取为输入费率的 10%(供应商公布的比例)。GPT-5.6 和 GPT-6 Astra 的缓存写入为 1.25 倍,其他所有模型均按普通输入费率计费。
- 所有缓存价格都已经包含 Kunavo 的模型折扣(按模型低于上游标价)。因此,Kunavo 上的 Sonnet 4.6 缓存读取价格为
$3 × 0.40 × 0.10 = $0.12 per 1M tokens,比未缓存的上游费率便宜约 25 倍。
缓存不是答案的情况
以下几种情况下,投入可能不值得:
- 短提示词(总计 <1K token)。开销占主导,直接不要使用缓存。
- 没有重复流量的一次性任务。第一次调用会稍微更贵,缓存只有从第 2 次调用开始才会回本。
- 高输出、低输入的任务(创意写作、代码生成)。输入本来就只占账单的一小部分。应将重点放在输出预算上限。
对于其他所有情况——RAG 聊天机器人、使用固定工具目录的代理、基于静态评分标准运行的分类器、使用一致 few-shot 示例的结构化提取流水线——缓存是你在一个下午内就能上线、投资回报率最高的优化。将其与我们的成本优化指南中的另外四种技术结合,在不牺牲任何输出质量的情况下,将成本降低 70% 是现实可行的。
已经在使用 Kunavo?打开 /app/usage,查看你最大模型的缓存列。如果是零,你就白白损失了节省机会。完整指南:/docs/caching。