通过将 2800 token 系统提示词块启用前缀缓存,API 费用从 $47/天降至 $6.80/天;缓存命中后读取费用仅为正常输入的 10%。
上个月我对一个每天处理约 4000 次请求的 AI Agent 进行了对比测试。该 Agent 有一个很长的 system prompt(大约 2800 个 token,包含规则、工具定义和示例),每次调用都会发送这条 system prompt。开启 prompt caching 之前:$47/天。启用该 system prompt 块的缓存后:$6.80/天。
这不是四舍五入的误差。这是一次配置变更带来的 85% 成本削减,且 Agent 的行为完全没有变化。
以下是 prompt caching 的具体工作原理以及如何规避陷阱进行配置。
Anthropic 的 prompt caching 在前缀级别工作。当你发送一个请求时,API 会检查你的消息前缀是否与之前缓存的前缀完全匹配。如果匹配,这些缓存的 token 会从 KV 存储中提供服务,而不是通过完整模型重新处理——并且你只需支付显著降低的每 token 费率。
定价结构(2026 年中旬,Claude 3.5 Sonnet):
普通输入 token:$3.00/百万 缓存写入(首次使用,或缓存未命中):$3.75/百万(写入缓存需额外支付 25% 溢价) 缓存读取(缓存命中):$0.30/百万(相比普通价格打 9 折)
两次请求之间缓存保留 5 分钟(每次命中都会重置 TTL)。对于调用频率高于每 5 分钟一次的任何 Agent——这几乎是所有生产环境 Agent——几乎总是划算的。
关键是 cache_control 块。你将它作为"断点"添加在任何你想要缓存的消息块的末尾。API 会缓存从开头到该断点的所有内容。
import anthropic
client = anthropic.Anthropic()
# Your long system prompt - tool definitions, rules, examples, etc.
SYSTEM_PROMPT = """
You are a support agent for Acme Corp...
[2,800 tokens of rules, tool definitions, persona, examples]
"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"} # <-- this is the entire setup
}
],
messages=[
{"role": "user", "content": user_message}
]
)
# Check what actually happened
usage = response.usage
print(f"Input tokens: {usage.input_tokens}")
print(f"Cache write tokens: {usage.cache_creation_input_tokens}")
print(f"Cache read tokens: {usage.cache_read_input_tokens}")
cache_creation_input_tokens 字段告诉你缓存被写入了(你需要支付 25% 的溢价)。在后续 5 分钟内的调用中,cache_read_input_tokens 将被填充,你只需支付 $0.30/M 而不是 $3.00/M。
每次调用都重复的大号 system prompt。如果你的 system prompt 有 1000+ token,且你调用 API 的频率高于每 5 分钟一次,缓存几乎总是净正的。
工具定义。工具 schema 算作输入 token,且可能大得惊人。一组 10 个描述合理的工具可能有 800-1200 个 token。缓存工具块。
System prompt 中的少样本示例。这是一个大头。人们在 system prompt 中添加 5-10 个完整示例来提高输出质量。这些示例可能有 2000-4000 个 token。缓存它们。
大规模文档分析。如果你在用许多不同的问题分析同一份文档(想象一下:从一份合同中提取 20 个不同字段),将文档文本作为 user message 缓存,然后针对同一缓存发出全部 20 个查询。
投资回报低或为负的场景:
请求间隔超过 5 分钟。缓存过期,你每次调用都要支付写入溢价,没有任何读取来分摊它。启用之前先检查你实际的请求频率。
非常短的 system prompt(<500 token)。数学上不划算——除非你有非常高的调用量,否则写入溢价超过读取节省。
一次性或批处理作业(每个 prompt 只处理一次)。没有重复读取 = 没有收益。
每个请求最多可以有 4 个缓存断点。这允许你独立缓存 prompt 的不同部分:
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=1024,
system=[
{
"type": "text",
"text": BASE_RULES, # Always the same
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": TOOL_DEFINITIONS, # Changes rarely
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": dynamic_context # Changes per request — NOT cached
}
],
messages=[...]
)
前缀缓存规则很严格:API 缓存按顺序到达的最后一个标记断点的所有内容。如果你的动态内容放在两个缓存块之间,第二个缓存命中不会生效——前缀必须完全相同。始终将动态内容放在最后。
空白字符和字符级一致性很重要。
缓存键是前缀的精确 token 序列。如果你的 system prompt 是动态生成的——比如说,你将用户名或账户等级插入其中——每个变体都会产生不同的 token 序列,即使 95% 的内容相同,你也会得到零缓存命中。
修复方法:将所有动态内容移到末尾,放在最后一个缓存断点之后。只把真正静态的内容(规则、工具定义、示例)放在缓存块中。
# Bad: dynamic content inside the cached block breaks caching
system = f"""
You are an agent for {company_name}. # <-- this makes every request unique
[2,800 tokens of static rules]
"""
# Good: static block cached, dynamic content appended outside the cache
STATIC_BLOCK = """
[2,800 tokens of static rules]
"""
system = [
{"type": "text", "text": STATIC_BLOCK, "cache_control": {"type": "ephemeral"}},
{"type": "text", "text": f"Current context: working for {company_name}."}
]
启用缓存之前,先跑一下这个数学:
设:
T = 缓存块中的 token 数
R = 每小时请求数
W = 缓存写入成本 = T * $3.75/M
S = 每次读取的节省 = T * ($3.00 - $0.30) / M = T * $2.70/M
盈亏平衡读取次数 = W / S = $3.75 / $2.70 ≈ 1.4 次读取/缓存窗口
如果你在 5 分钟内收到超过 1.4 次请求(大约每小时 17 次请求),缓存就是净正的。在每天 4000 次请求的情况下,你在每个 5 分钟窗口内会有数百次缓存命中。
始终对缓存使用情况进行插桩。响应中的 usage 对象会精确告诉你发生了什么:
usage = response.usage
total_input = usage.input_tokens
cache_writes = getattr(usage, 'cache_creation_input_tokens', 0)
cache_reads = getattr(usage, 'cache_read_input_tokens', 0)
# A healthy caching ratio: most calls should be reads, not writes
print(f"Cache write: {cache_writes} tokens (paid at $3.75/M)")
print(f"Cache read: {cache_reads} tokens (paid at $0.30/M)")
print(f"Regular: {total_input} tokens (paid at $3.00/M)")
如果你看到的更多是 cache_creation_input_tokens 而很少有 cache_read_input_tokens,说明你的请求频率低于 5 分钟,或者你的 prompt 实际上并不是静态的。修复内容,而不是缓存配置。
Prompt caching 是这类罕见的 API 功能之一:实现成本 30 分钟,回报即时且持续。它不会改变你的 Agent 做什么——它只是改变你为相同工作支付的费用。
如果你的 Agent 每小时调用超过约 20 次,且 system prompt 超过约 800 个 token,你就应该使用缓存。cache_control 块是一行代码。usage 字段可以立即告诉你它是否生效。
如果你在生产规模上构建可靠的 AI Agent,免费的 Reliable Agent Field Guide 涵盖了可靠性模式、成本控制和测试策略:penloomstudio.com/field-guide.html