Claude Prompt Caching 实战:把大模型 API 成本降低 90% 的工程技巧
一、问题场景
“我做了一个 RAG 系统,每次提问都要把几千 Token 的文档塞进 Prompt,账单越来越吓人。”
“我的 Agent 有一个 3000 Token 的 System Prompt,每次调用都要重新计费,一天下来成本爆炸。”
“多轮对话越聊越长,历史消息每轮都重新计算,Token 消耗像滚雪球。”
如果你也遇到这些问题,那Prompt Caching(提示词缓存)就是你的救星。这是一个被很多开发者忽略,但能实打实把成本降低 90%、延迟降低 85%的工程技巧。
本文将讲清楚 Prompt Caching 的原理、使用方法、计费模型,并给出可直接套用的实战代码。
读完你会获得:
- Prompt Caching 的工作原理和省钱逻辑
- Claude API 中如何正确设置缓存断点
- 三个典型场景的完整实战代码
- 计费规则详解和 5 个避坑要点
二、原理分析
2.1 为什么 Prompt 会重复计费?
大模型 API 是无状态的——每次请求,模型都要把整个 Prompt 从头处理一遍(重新计算注意力)。
第 1 次请求:[3000 Token System Prompt] + [问题A] → 计费 3000+ 第 2 次请求:[3000 Token System Prompt] + [问题B] → 又计费 3000+ 第 3 次请求:[3000 Token System Prompt] + [问题C] → 再计费 3000+那个固定的 3000 Token System Prompt,每次都在重复付费。这就是浪费的根源。
2.2 Prompt Caching 如何省钱?
Prompt Caching 让模型把处理过的固定前缀缓存起来,后续请求命中缓存时,这部分几乎不收费、不重算。
第 1 次请求:[3000 Token] 写入缓存(缓存写入费,略贵)+ [问题A] 第 2 次请求:[3000 Token] 命中缓存(仅 10% 费用!)+ [问题B] 第 3 次请求:[3000 Token] 命中缓存(仅 10% 费用!)+ [问题C]核心收益:
- 成本:缓存命中部分只收10%的费用(降低 90%)
- 延迟:命中缓存可减少最多85%的首 Token 延迟
- 适用:System Prompt、长文档、Few-shot 示例、对话历史等固定前缀
2.3 计费模型详解
Claude Prompt Caching 有三种 Token 价格:
| 类型 | 价格(相对基础输入) | 说明 |
|---|---|---|
| 缓存写入 | 1.25 倍 | 首次写入缓存,比普通输入略贵 |
| 缓存命中(读取) | 0.1 倍 | 命中缓存,仅 10% 费用 |
| 普通输入 | 1.0 倍 | 未缓存的部分正常计费 |
关键:缓存写入虽然贵 25%,但只要后续命中2 次以上就回本了。对于高频复用的固定前缀,省钱效果惊人。
缓存有效期(TTL):默认 5 分钟,每次命中会刷新计时。也就是说只要 5 分钟内有请求,缓存就一直有效。
2.4 缓存的工作机制
请求结构(从前到后): ┌─────────────────────────────┐ │ Tools 定义 │ ┐ ├─────────────────────────────┤ │ 设置 cache_control 断点 │ System Prompt │ │ 这部分被缓存 ├─────────────────────────────┤ ┘ │ 对话历史 / 用户问题 │ 这部分每次变化,不缓存 └─────────────────────────────┘核心规则:缓存是前缀匹配的。缓存断点之前的内容只要完全一致,就能命中。所以固定内容必须放在前面,变化内容放在后面。
三、实践验证:完整代码
3.1 基础用法:缓存 System Prompt
""" 基础场景:缓存固定的 System Prompt 适用:Agent、客服等有长 System Prompt 的场景 """fromanthropicimportAnthropic client=Anthropic()# 一个较长的 System Prompt(假设 2000+ Token)LONG_SYSTEM_PROMPT="""你是一个专业的法律顾问助手... (这里是大量的规则、术语解释、回答规范,假设 2000 Token) ..."""defask(question:str):response=client.messages.create(model="claude-sonnet-4-6",max_tokens=1024,system=[{"type":"text","text":LONG_SYSTEM_PROMPT,"cache_control":{"type":"ephemeral"}# ← 关键:标记缓存断点}],messages=[{"role":"user","content":question}])# 查看缓存使用情况usage=response.usageprint(f"缓存写入:{usage.cache_creation_input_tokens}")print(f"缓存命中:{usage.cache_read_input_tokens}")print(f"普通输入:{usage.input_tokens}")returnresponse.content[0].text# 第一次调用:写入缓存ask("合同违约金的上限是多少?")# 第二次调用:命中缓存,System Prompt 部分只收 10% 费用ask("租赁合同可以提前解除吗?")3.2 RAG 场景:缓存检索到的长文档
""" RAG 场景:缓存大段检索文档 适用:基于同一批文档多次问答 """fromanthropicimportAnthropic client=Anthropic()defrag_query_with_cache(documents:str,question:str):"""documents 是检索到的长文档,question 是变化的问题"""response=client.messages.create(model="claude-sonnet-4-6",max_tokens=1024,system=[{"type":"text","text":"你是知识库助手,基于提供的资料回答问题。"},{"type":"text","text":f"参考资料:\n{documents}","cache_control":{"type":"ephemeral"}# ← 缓存长文档}],messages=[{"role":"user","content":question}])returnresponse.content[0].text# 同一批文档,多次提问,文档部分只在第一次付全费docs="(这里是检索到的 5000 Token 文档)"rag_query_with_cache(docs,"产品的核心功能有哪些?")rag_query_with_cache(docs,"如何配置环境?")# 命中缓存rag_query_with_cache(docs,"常见错误怎么排查?")# 命中缓存3.3 多轮对话:缓存对话历史
""" 多轮对话场景:随着对话变长,缓存前面的历史 适用:聊天机器人、长对话 Agent """fromanthropicimportAnthropic client=Anthropic()classCachedChat:def__init__(self,system_prompt:str):self.system=system_prompt self.messages=[]defchat(self,user_input:str):self.messages.append({"role":"user","content":user_input})# 在倒数第二条消息上打缓存断点,缓存之前的对话历史messages=self._add_cache_breakpoint()response=client.messages.create(model="claude-sonnet-4-6",max_tokens=1024,system=[{"type":"text","text":self.system,"cache_control":{"type":"ephemeral"}}],messages=messages)reply=response.content[0].text self.messages.append({"role":"assistant","content":reply})returnreplydef_add_cache_breakpoint(self):"""在最后一条用户消息前的内容打缓存断点"""msgs=[dict(m)forminself.messages]iflen(msgs)>=2:# 给倒数第二条消息加缓存标记last_cached=msgs[-2]ifisinstance(last_cached["content"],str):last_cached["content"]=[{"type":"text","text":last_cached["content"],"cache_control":{"type":"ephemeral"}}]returnmsgs# 使用chat=CachedChat("你是一个友好的助手。")print(chat.chat("我想学 Python"))print(chat.chat("从哪里开始?"))# 缓存前面的对话print(chat.chat("推荐一些项目练手"))# 继续命中缓存3.4 验证缓存是否生效
通过response.usage的字段判断:
usage=response.usage# cache_creation_input_tokens > 0 → 这次写入了缓存# cache_read_input_tokens > 0 → 这次命中了缓存(省钱了!)# input_tokens → 未缓存的普通输入ifusage.cache_read_input_tokens>0:saved=usage.cache_read_input_tokens*0.9# 省了 90%print(f"✅ 缓存命中!约节省{saved:.0f}Token 的费用")四、避坑指南
| # | 坑 | 现象 | 解决方案 |
|---|---|---|---|
| 1 | 缓存内容放错位置 | 缓存从不命中 | 固定内容必须放前面,变化内容放后面(前缀匹配) |
| 2 | 缓存前缀有微小变化 | 命中率低 | 哪怕一个字符不同都无法命中,确保前缀完全一致 |
| 3 | 缓存块太小 | 没省到钱甚至更贵 | Claude 有最小缓存长度要求(通常 1024 Token),太短不缓存 |
| 4 | 忽略 5 分钟 TTL | 低频调用缓存失效 | 高频场景才划算;低频场景可考虑保活或不用缓存 |
| 5 | 只调用一次还用缓存 | 写入费比省的多 | 缓存写入贵 25%,命中 2 次以上才回本 |
| 6 | 断点设置过多 | 管理复杂、效果差 | 一般 1-2 个断点足够(最多支持 4 个) |
计费回本公式:
缓存写入成本 = N × 1.25(首次) 不用缓存成本 = N × 1.0 × 调用次数 用缓存成本 = N × 1.25 + N × 0.1 × (调用次数 - 1) 当调用次数 ≥ 2 时,用缓存就开始省钱 调用次数越多,省得越多(趋近 90%)五、总结
Prompt Caching 是大模型工程化中性价比最高的优化手段之一,核心要点:
- 原理:缓存固定前缀,命中只收 10% 费用
- 用法:在固定内容(System Prompt、长文档、对话历史)后打
cache_control断点 - 布局:固定内容放前面,变化内容放后面
- 回本:命中 2 次以上就省钱,高频场景降本 90%
最适合用 Prompt Caching 的场景:
- 有长 System Prompt 的 Agent / 客服
- 基于同一批文档多次问答的 RAG
- 多轮长对话的聊天机器人
- 大量 Few-shot 示例的分类/抽取任务
推荐实践路径:
- 先用
response.usage分析你的请求,找出重复计费的固定部分 - 在固定前缀后加缓存断点,验证
cache_read_input_tokens是否 > 0 - 优化 Prompt 结构,把所有固定内容前移
- 监控缓存命中率,持续调优
延伸阅读:
- Anthropic: Prompt Caching 官方文档
- Anthropic: 降低成本的最佳实践
省下来的钱,就是赚到的钱。一个cache_control断点,可能就帮你把月度账单砍掉一大半。