一、工作原理与核心概念
Prompt Caching 是 Anthropic 为 Claude API 推出的一项优化功能,核心思路极其简单:把那些每次请求都不变的内容缓存起来,后续请求直接复用,不再重新计算 Token。
对于需要频繁调用 Claude 的应用(比如文档问答、代码审查、多轮对话助手),大量 Token 消耗其实来自于每次都要重复传入的系统提示、背景文档、示例数据等"静态内容"。开启 Prompt Caching 后,这些内容只需支付一次写入费用,后续读取时价格仅为原始价格的 10%。
缓存的工作流程
每次 API 请求到达 Anthropic 服务器时,系统会按以下顺序检查缓存:
- 计算请求中标记为可缓存部分的加密哈希值
- 在当前 Organization 的缓存池中查找匹配项
- 命中缓存(Cache Hit):直接使用已缓存的 KV 表示,跳过重新计算
- 未命中(Cache Miss):正常处理全部 Token,处理完成后将结果写入缓存
关键参数一览
| 参数 | 值 | 说明 |
|---|---|---|
| 默认 TTL(存活时间) | 5 分钟 | 每次命中缓存自动刷新计时 |
| 延长 TTL 选项 | 1 小时 | 额外费用约为基础输入 Token 的 2× |
| 最大缓存断点数 | 4 个 | 超过 4 个断点会返回 400 错误 |
| 最小可缓存长度(Sonnet/Opus) | 1024 tokens | 低于此长度不触发缓存 |
| 最小可缓存长度(Haiku 3.5) | 2048 tokens | Haiku 系列门槛更高 |
二、环境准备与 SDK 安装
Prompt Caching 在最新版 Anthropic SDK 中已全面支持,无需手动添加 Beta Header。以下以 Python 为例完成环境搭建。
确保 SDK 版本 ≥ 0.30.0,旧版需要手动传递 anthropic-beta Header,新版已内置支持。
pip install --upgrade anthropic
# 验证版本
python -c "import anthropic; print(anthropic.__version__)"
推荐使用环境变量,避免硬编码到代码中。
export ANTHROPIC_API_KEY="sk-ant-xxxxxxxxxxxxxxxx"
或者在 Python 代码中显式传入(仅适合测试环境):
import anthropic
client = anthropic.Anthropic(api_key="sk-ant-xxxxxxxxxxxxxxxx")
发送一条简单请求,确认账号配置正确。
import anthropic
client = anthropic.Anthropic()
resp = client.messages.create(
model="claude-opus-4-6",
max_tokens=32,
messages=[{"role": "user", "content": "Hello, reply with 'OK' only."}]
)
print(resp.content[0].text) # 应输出 OK
claude-opus-4-6 或 claude-sonnet-3-7 获取最佳缓存支持。三、显式缓存:手动控制缓存断点
显式缓存是 Prompt Caching 的经典用法,通过在消息块上添加 cache_control 字段,精确标记哪些内容应该被缓存。
场景示例:长文档问答系统
假设你在做一个 PDF 文档问答系统,每次请求都要传入完整文档内容(数万字)。这是显式缓存最典型的适用场景。
将系统角色指令和文档内容分开,分别标记 cache_control。缓存前缀按 tools → system → messages 的层级顺序创建。
import anthropic
client = anthropic.Anthropic()
# 模拟一份长文档(实际使用中替换为真实内容)
long_document = """
[这里是一份数万字的技术文档,每次问答都相同...]
第一章:系统架构
...
第二章:接口规范
...
""" # 假设此处超过 1024 tokens
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你是一位专业的技术文档分析助手,请根据提供的文档回答用户的问题。",
# 第一个缓存断点:角色指令
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": f"以下是完整的参考文档:\n\n{long_document}",
# 第二个缓存断点:文档内容(最大收益处)
"cache_control": {"type": "ephemeral"}
}
],
messages=[
{
"role": "user",
"content": "请简述第二章的接口规范中关于认证的部分。"
}
]
)
print(response.content[0].text)
# 查看缓存使用情况
usage = response.usage
print(f"\n=== Token 使用情况 ===")
print(f"输入 Token: {usage.input_tokens}")
print(f"缓存写入 Token: {usage.cache_creation_input_tokens}")
print(f"缓存命中 Token: {usage.cache_read_input_tokens}")
print(f"输出 Token: {usage.output_tokens}")
5 分钟内用相同文档发送第二个问题,应该看到 cache_read_input_tokens 数值大幅增加,cache_creation_input_tokens 降为 0。
response2 = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你是一位专业的技术文档分析助手,请根据提供的文档回答用户的问题。",
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": f"以下是完整的参考文档:\n\n{long_document}",
"cache_control": {"type": "ephemeral"}
}
],
messages=[
{
"role": "user",
"content": "请列举文档中提到的所有 HTTP 状态码。" # 不同的问题
}
]
)
usage2 = response2.usage
print(f"=== 第二次请求 Token 使用情况 ===")
print(f"输入 Token: {usage2.input_tokens}")
print(f"缓存写入 Token: {usage2.cache_creation_input_tokens}") # 应为 0
print(f"缓存命中 Token: {usage2.cache_read_input_tokens}") # 应大幅增加
print(f"输出 Token: {usage2.output_tokens}")
cache_control 的内容有任何变化(哪怕一个字符),缓存就会失效并重新写入。同理,如果在 System Prompt 之前的 tools 定义发生变更,后续所有断点的缓存也会连带失效。四、自动缓存:零改动享受缓存
如果你不想修改现有代码结构,自动缓存(Automatic Caching)是最低成本的接入方案。只需在请求顶层添加一个字段,系统自动为最后一个可缓存块打上断点,并随着对话历史增长自动移动缓存位置。
显式缓存 vs 自动缓存
| 维度 | 显式缓存 | 自动缓存 |
|---|---|---|
| 接入复杂度 | 需修改每个消息块结构 | 顶层一行配置即可 |
| 控制粒度 | 精确控制缓存位置 | 自动选择最后可缓存块 |
| 多轮对话支持 | 需手动管理断点位置 | 自动跟随对话增长 |
| 最大断点数 | 4 个(手动) | 1 个(自动) |
| 适用场景 | 固定大文档、精细优化 | 多轮聊天、快速接入 |
自动缓存代码示例
import anthropic
client = anthropic.Anthropic()
# 对话历史
conversation_history = []
def chat(user_message: str) -> str:
"""支持缓存的多轮对话函数"""
conversation_history.append({
"role": "user",
"content": user_message
})
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
# ✅ 自动缓存:只需这一个字段,无需修改 system/messages 结构
betas=["prompt-caching-2024-07-31"],
extra_body={
"cache_control": {"type": "ephemeral"} # 顶层缓存控制
},
system="你是一位有经验的软件架构师,擅长系统设计与代码审查。",
messages=conversation_history
)
assistant_message = response.content[0].text
conversation_history.append({
"role": "assistant",
"content": assistant_message
})
# 输出 Token 使用情况
u = response.usage
cache_hit = getattr(u, 'cache_read_input_tokens', 0)
cache_write = getattr(u, 'cache_creation_input_tokens', 0)
print(f"[缓存命中: {cache_hit} | 缓存写入: {cache_write} | 输出: {u.output_tokens}]")
return assistant_message
# 模拟多轮对话
print(chat("请帮我设计一个高并发的消息队列系统"))
print(chat("这个系统应该如何处理消息丢失的问题?"))
print(chat("能给出 Go 语言的实现示例吗?"))
五、进阶技巧与最佳实践
1. 延长缓存到 1 小时
默认 5 分钟 TTL 适合快速交互,但对于慢速批处理任务,5 分钟内可能无法完成足够多的请求来充分利用缓存。此时可以升级到 1 小时 TTL:
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": "你的系统提示...",
# 使用 1 小时缓存(需要额外费用)
"cache_control": {"type": "ephemeral", "ttl": 3600}
}
],
messages=[{"role": "user", "content": "用户问题"}]
)
2. 结合 Tool Use 使用缓存
如果你的应用使用了 Function Calling(Tool Use),工具定义也可以被缓存。注意工具定义的缓存断点优先级高于 System Prompt:
tools = [
{
"name": "search_database",
"description": "搜索产品数据库",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"category": {"type": "string"}
},
"required": ["query"]
},
# ✅ 工具定义也可以缓存
"cache_control": {"type": "ephemeral"}
}
# 更多工具定义...(最多配合 3 个其他断点,总计不超过 4 个)
]
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
tools=tools,
system=[{
"type": "text",
"text": "你是一位购物助手...",
"cache_control": {"type": "ephemeral"}
}],
messages=[{"role": "user", "content": "帮我找一款蓝牙耳机"}]
)
3. 批量任务中的缓存策略
对于需要对同一份文档做大量分析的批量任务,推荐使用以下模式,最大化缓存收益:
import asyncio
import anthropic
client = anthropic.Anthropic()
# 一份固定文档,需要用 N 个不同 Prompt 分析
document_content = "... 大型文档内容 ..."
analysis_tasks = [
"提取所有人名和机构名",
"总结文档的主要论点",
"找出文中的数据和统计信息",
"分析作者的写作风格",
"列出所有引用的参考文献",
]
def analyze(task: str) -> dict:
resp = client.messages.create(
model="claude-opus-4-6",
max_tokens=512,
system=[
{
"type": "text",
"text": "你是专业文档分析助手。",
"cache_control": {"type": "ephemeral"}
},
{
"type": "text",
"text": f"待分析文档:\n{document_content}",
"cache_control": {"type": "ephemeral"} # 核心:文档只写入一次
}
],
messages=[{"role": "user", "content": task}]
)
u = resp.usage
return {
"task": task,
"result": resp.content[0].text,
"cache_hit": getattr(u, 'cache_read_input_tokens', 0),
"cache_miss": getattr(u, 'cache_creation_input_tokens', 0)
}
# 串行执行(或改为并发执行)
results = [analyze(t) for t in analysis_tasks]
for r in results:
print(f"任务: {r['task'][:20]}...")
print(f" 缓存命中: {r['cache_hit']} tokens, 缓存写入: {r['cache_miss']} tokens\n")
六、成本计算与效果评估
最新定价(Claude Opus 4.6,每百万 Token)
实际成本计算示例
假设你有一份 10,000 tokens 的文档,每天被查询 100 次:
def calculate_cache_savings(
doc_tokens: int,
daily_requests: int,
model: str = "claude-opus-4-6"
):
"""计算 Prompt Caching 的成本节省"""
pricing = {
"claude-opus-4-6": {
"input": 5.0, # $5 per MTok
"cache_write_5m": 6.25, # $6.25 per MTok
"cache_read": 0.50, # $0.50 per MTok
}
}
p = pricing[model]
MTok = 1_000_000
# 不使用缓存
cost_no_cache = (doc_tokens * daily_requests / MTok) * p["input"]
# 使用缓存(每 5 分钟最多被使用一次,假设请求均匀分布 8 小时内)
cache_writes_per_day = 8 * 60 / 5 # 96 次写入
cache_reads_per_day = daily_requests - cache_writes_per_day
cost_with_cache = (
(doc_tokens * cache_writes_per_day / MTok) * p["cache_write_5m"] +
(doc_tokens * cache_reads_per_day / MTok) * p["cache_read"]
)
saving = cost_no_cache - cost_with_cache
saving_pct = saving / cost_no_cache * 100
print(f"=== 文档规模: {doc_tokens:,} tokens | 每日请求: {daily_requests} 次 ===")
print(f"不使用缓存日成本: ${cost_no_cache:.4f}")
print(f"使用缓存后日成本: ${cost_with_cache:.4f}")
print(f"每日节省: ${saving:.4f} ({saving_pct:.1f}%)")
print(f"每月节省(30天): ${saving * 30:.2f}")
# 测试不同场景
calculate_cache_savings(doc_tokens=10_000, daily_requests=100)
print()
calculate_cache_savings(doc_tokens=50_000, daily_requests=500)
print()
calculate_cache_savings(doc_tokens=100_000, daily_requests=1000)
如何在 Anthropic Console 监控缓存效果
登录 console.anthropic.com,进入「Usage」→「Prompt Caching」面板,可以查看:
- 缓存命中率(Cache Hit Rate)趋势
- 各类 Token 用量分布(输入 / 缓存写入 / 缓存读取 / 输出)
- 按模型、按时间段的成本分解
七、常见问题排查
client.messages.count_tokens() 检查实际 Token 数。另一个原因是两次请求间隔超过 5 分钟,缓存已过期。
cache_control 断点超过了 4 个上限。检查 tools、system、messages 中的所有 cache_control 字段,确保总数 ≤ 4。工具定义中的断点也计入总数。
cache_control 断点之前的任何内容(包括图片、工具调用结果)发生变化,都会导致该断点及其后续断点缓存失效。建议将图片类内容放在缓存断点之后,仅缓存纯文本静态部分。
cache_control)可通过这些平台的 Claude API 直接访问路径使用,具体请参阅各平台最新文档。
核心要点回顾
- Prompt Caching 通过复用静态内容,最高节省 90% 输入 Token 成本、85% 响应延迟
- 显式缓存(
cache_control字段)适合固定文档场景,最多 4 个断点 - 自动缓存(顶层配置)适合多轮对话,随对话增长自动移动缓存位置
- 最小缓存长度:Sonnet/Opus 为 1024 tokens,Haiku 为 2048 tokens
- 缓存默认 TTL 5 分钟,可付费升级到 1 小时;写入比基础价贵 25%,读取便宜 90%
- 静态内容放前面、动态内容放后面,是最大化缓存命中的黄金法则
- 通过
usage.cache_read_input_tokens实时监控缓存命中情况
Prompt Caching 是 Claude API 中为数不多的"用了就能立刻省钱"的功能,无论你是构建 RAG 系统、多轮对话助手,还是批量文档处理管线,只要有重复使用的静态内容,都值得立刻接入。