1. 成本压力下的真实处境:为什么要盯上 Prompt 缓存
先说一个我在不少团队里看到的场景:项目上线前,大家最关心的是模型效果——准确率够不够、回答像不像人话。等项目真正跑起来,账单开始按月推送之后,焦虑点就变成了每次调用花了多少钱、一个月烧掉多少 token。尤其是接 Claude 这类长上下文模型之后,很多团队发现一个尴尬的事实:每次请求都在重复发送一大堆一模一样的系统提示词、背景资料、工具定义,这些内容一个字都没变,但每一次都被完整计费。
我见过最典型的一个例子,某跨平台系统的客服助手,系统提示词加产品知识库固定前缀大约 4000 token,每次用户提问都要重发一遍。假设每天 20 万次调用,光这个固定前缀每天就要消耗 80 亿 token。按当时的计费标准粗算,一天的纯前置开销就不是小数目,一个月下来占了整个模型花费的一半以上。这个数字听起来夸张,但做过生产环境的人都知道,这种情况非常普遍。
Claude Prompt 缓存解决的就是这个痛点:把频繁重复使用的公共前缀部分缓存起来,后续请求命中缓存后,这部分输入不再按完整价格计费,而是按远低于正常价格的缓存读取费率计算。换句话说,同样的模型能力,调用成本能直接压下一大截。
这篇文章不聊概念,只聊落地。我会从前缀设计、缓存命中率优化、接入流程、治理机制、成本核算几个方面,把企业实际使用 Claude Prompt 缓存控制成本这件事拆开讲清楚。内容来源于我在多个生产项目里的实操记录,包括踩过的坑和验证过的做法。
注意:本文讨论的是通过合理的提示词工程和缓存机制来降低成本,这是完全正常的 API 使用优化手段。所有内容均基于主流大模型服务的通用缓存能力,不涉及任何非正常访问方式。
2. 缓存机制的核心逻辑:先搞清楚钱省在哪里
2.1 Cache 的工作原理与计费差异
要控制成本,先得搞清楚 Claude 的 Prompt 缓存到底是怎么工作的。它的设计思路是:如果请求的 prompt 前缀和之前某个请求的前缀完全一致,且长度达到最低门槛,服务端可以复用之前计算好的处理结果,不需要重新处理这段文本。
这个机制依赖两个关键点:一是前缀的稳定性,二是最小缓存长度。前缀必须逐字逐句一致,哪怕一个空格、一个换行变了,都会导致缓存无法命中。最低门槛是 1024 token,也就是说,如果你要缓存的内容不足 1024 token,这个机制根本不会生效。实际使用中,我建议把缓存前缀控制在 2048 token 以上,因为太短的前缀即便命中,省下的钱也有限,反而增加了管理复杂度。
计费差异是核心。正常输入 token 按标准输入价格计费,而命中缓存的输入 token 按缓存读取价格计费,两者通常相差数倍。写入缓存时还有一笔缓存写入费用,但只在你首次提交新前缀或修改前缀时产生。也就是说,设计良好的缓存策略下,写入费用占比很低,大头都在读取费用的节省上。
举一个直观的对比:假设一个固定系统提示词加知识上下文为 4000 token,每天调用 10 万次。不启用缓存时,每天这部分的输入成本是 4000 token 乘以 10 万次,再乘以标准输入单价。启用缓存后,假设缓存命中率 95%,那么每天只有 4000 token 的写入费用(首次写入一次),加上 10 万次乘以 4000 token 乘以缓存读取单价。以常见价差来算,启用缓存后这部分费用大约能降到原来的 20% 到 40%。具体比例取决于你的单价差和命中率,但方向非常明确。
2.2 为什么说前缀设计决定成败
明白了计费逻辑你就会发现,缓存省钱的核心不是技术配置,而是 prompt 结构设计。如果 prompt 里频繁变动的部分出现在开头,比如先把用户 ID、时间戳、随机信息放到最前面,每次请求的前缀都不一样,那缓存就彻底失效了。
正确的思路是:把稳定不变的内容全部集中到 prompt 的最前面,形成一个"静态前缀区"。这个区域里放系统指令、角色设定、业务规则、知识库片段、工具定义、输出格式要求等。凡是每次请求都一样的,统统往前提。
变动内容放哪里?放在静态前缀区之后,作为"动态后缀区"。用户问题、上下文对话、临时变量、请求相关的参数,全部放在这个位置。这样每次请求的前缀部分都是同一个字符串,服务端才能识别出这是可复用的缓存。
我见过不少团队在这个地方栽跟头。有人把用户姓名动态拼进了系统提示词,有人把当前日期直接放在 prompt 第一行,还有人把多轮对话历史原样拼接到了固定文档前面。这些做法都会让缓存形同虚设,钱没省到,反而增加了排查难度。设计的第一原则就是:动与静分离,静态内容永远在前,动态内容永远在后。
3. 前缀区设计方法:如何搭出一个高命中率的缓存结构
3.1 四段式前缀结构参考
基于我在多个生产项目里的实践,我总结出一套四段式前缀结构,稳定性和命中率都比较理想:
第一段:系统指令区。包含模型的角色定位、回答风格、行为边界、安全约束。这一段是全局最稳定的,只要业务方向不变,它一般不会改动。
第二段:业务规则区。包含当前业务场景的处理规则、判断逻辑、优先级说明。如果业务有多个场景,可以按场景拆分子模块,通过动态后缀区传入场景 ID 来触发不同分支。注意,场景 ID 属于动态内容,只能放在后缀区,不能塞进前缀。
第三段:知识资料区。包含产品说明、政策条款、FAQ、历史工单样本等相对固定的知识内容。这里要注意控制总长度,因为缓存前缀越长,每次写入和读取的数据量就越大。知识内容应该做定期更新机制,而不是每次请求都拉最新版本。
第四段:工具定义区。包含可调用工具的名称、参数结构、使用约束。工具定义在版本迭代期会变化,但稳定期基本不变,非常适合放在前缀尾部。
这四段顺序不是固定的,你可以根据业务实际情况调整,但核心原则不能变:稳定度从高到低排列,最稳定的放在最前面。为什么?因为如果你将来要更新前缀,比如知识库内容变了,那么从变更点开始,包括它后面的所有内容都会失效。把最不稳定的放在最后面,可以最大程度减少更新带来的缓存失效范围。
3.2 动态后缀区的内容组织
后缀区虽然不参与缓存,但它的组织方式同样影响成本。原因很简单:上下文越长,总输入 token 就越多。缓存只帮你省掉了固定前缀的费用,动态部分的费用该多少还是多少。
我建议动态后缀区采用"最小化原则":只放当前请求真正必要的信息。这包括用户问题、必要的会话上下文、业务参数、临时变量。多轮对话场景下,不要无脑把全部历史消息都拼进去,而是做截断或摘要。一个可行的策略是:对超过 N 轮的历史消息,用上一次模型生成的摘要代替原始消息。这样既能保留对话意图,又能显著压缩 token 长度。
另外,后缀区内部也建议做结构化管理,比如用 XML 标签或 JSON 格式组织字段。这样做的好处有两个:一是让模型更容易区分不同来源的信息,提升回答质量;二是排查问题时能快速定位是哪一段在变化,便于判断缓存失效原因。
3.3 一个可直接参考的模板示例
SYSTEM_PROMPT = ( "你是某跨平台系统的智能客服助手。\n" "你的职责是:根据用户问题,结合知识库内容,给出准确、简洁的回答。\n" "回答要求:使用简体中文;不超过200字;不确定的信息必须明确说明;禁止编造事实。\n" "\n" "=== 业务规则 ===\n" "1. 支持查询订单状态、物流信息、退换货政策。\n" "2. 涉及账户安全问题时,引导用户通过官方渠道验证身份。\n" "3. 用户情绪激动时,先安抚再回答问题。\n" "\n" "=== 知识库 ===\n" f"{knowledge_base_text}\n" "\n" "=== 可用工具 ===\n" "- 订单查询:输入订单号,返回订单状态和物流轨迹。\n" "- 退换货申请:输入订单号和原因,创建售后工单。\n" "当前时间:{current_time}\n" "用户问题:{user_question}\n" )注意上面的模板,知识库文本和工具定义都是静态内容,放在前缀区。而当前时间和用户问题被放到最后,属于动态内容。这里有个细节值得专门说明:时间信息如果放在前缀区,那么缓存的有效期就受限于当前时刻——因为缓存内容里写死了时间,过了几分钟,前缀就变了。所以我把时间放到最后,让前缀保持纯静态,缓存在一段时间内可以持续命中。
提示:知识库文本是动态更新的,但在更新周期内它是静态的。如果你每小时更新一次知识库,那么在这一个小时里缓存都是有效的。更新动作只产生一次短暂的写入费用,之后的所有请求都享受缓存读取价格。
4. 接入流程与配置解析:从代码到生产环境的完整路径
4.1 代码层面的接入方式
Claude Prompt 缓存的接入不算复杂,核心就是在 API 请求里标记哪些内容是缓存前缀。不同语言的 SDK 调用方式略有差异,但思路一致:在 messages 数组的开头部分,给要缓存的消息添加一个 cache_control 标记。
from anthropic import Anthropic client = Anthropic() response = client.messages.create( model="claude-sonnet-4-5", max_tokens=2048, system=[ { "type": "text", "text": SYSTEM_PROMPT, "cache_control": {"type": "ephemeral"} } ], messages=[ {"role": "user", "content": user_question} ] )关键在于 system 参数里的 cache_control 字段。加上这个标记,system 内容就会作为缓存前缀参与缓存。如果你的前缀区还包含其他静态消息,也可以在 messages 数组里对任意一条消息设置 cache_control。但要注意,设置位置决定了缓存边界。在 system 上设置缓存,缓存范围就是整个 system 内容。在 messages 数组的某条消息上设置缓存,缓存范围是从消息序列开始到该条消息为止的全部内容。
这里有一个很容易踩的坑:如果你把缓存控制放在 messages 中的第二条或第三条消息上,但前面的消息内容不稳定,那么缓存一样失效。缓存机制要求的是"前缀完全一致",只要缓存边界内的任何字符变了,就命中不了。所以最简单的做法就是在 system 上设置缓存,让整个 system 成为缓存前缀。
4.2 缓存写入与命中的生命周期
理解缓存的生命周期有助于你设计更新策略。整个流程大致是这样:
第一次发送带 cache_control 的请求时,服务端检查是否有匹配的缓存。如果没有,就处理请求并写入一个新的缓存条目,同时收取缓存写入费用。后续请求发送相同前缀时,服务端直接命中缓存,按缓存读取价格计费。缓存条目有存活时间,类型为 ephemeral 的缓存通常存活几分钟到几小时不等。超过存活时间后,缓存自动过期,下一次请求会重新写入,再次产生写入费用。
这意味着,如果你的业务请求频率极低,比如每天只有几十次调用,那么缓存可能频繁过期,每次都要重新写入,写入费用反而可能比节省的读取费用还高。这种情况就不适合用缓存。反之,如果请求频率高、间隔短,缓存的收益就非常显著。
我在实际项目里测过两类场景:一类是客服助手,调用频率高且均匀,缓存命中率能达到 95% 以上;另一类是后台管理报表生成,调用稀疏且集中在上午,缓存在午后就基本失效了。前者省了 60% 以上的输入成本,后者几乎没省到钱。所以接入之前,先评估你的请求频率分布。
4.3 缓存命中率观测与验证
接入缓存后,不要只凭账单判断效果。我建议通过两方面的数据来验证:
第一,API 返回的 usage 字段。每次响应里都会包含 input_tokens、cache_creation_input_tokens、cache_read_input_tokens 这几个指标。cache_creation_input_tokens 表示本次请求写入缓存的 token 数,cache_read_input_tokens 表示本次请求命中缓存读取的 token 数。如果 cache_read_input_tokens 长期为零,说明你的缓存从来没被命中过,前缀设计大概率有问题。
第二,成本核算表。建立一张每日成本表,记录总请求数、平均输入 token、缓存命中率、缓存写入次数、预计无缓存成本和实际成本。对比这两列,就能直观看到缓存带来的节省比例。我用表格记录过一个月的运行数据,节省比例稳定在 55% 到 65% 之间,和理论推算基本吻合。
这里再补充一个经常被忽视的问题:不要只关注缓存命中,还要关注缓存写入频率。如果发现 cache_creation_input_tokens 频繁出现,说明缓存前缀频繁变化。常见的诱因包括:前缀里藏了时间戳、前缀里拼接了随机 ID、知识库内容每次请求都重拉最新版。找到频繁写入的根因,缓存成本才能进一步压缩。
5. 常见接入问题和排查思路速查
5.1 缓存不生效的典型症状
我在不同项目里排查过很多缓存问题,最常见的就是:接入代码没问题,缓存就是不命中。下面整理一份症状对照表,方便你快速定位:
| 症状 | 可能原因 | 排查方向 |
|---|---|---|
| cache_read_input_tokens 始终为零 | 前缀内容不稳定,存在隐藏动态字段 | 检查前缀中是否包含时间、随机数、用户 ID |
| cache_creation_input_tokens 频繁出现 | 缓存前缀经常变化或被修改 | 抓取两次请求的前缀做逐字节对比 |
| 缓存命中但成本下降不明显 | 动态后缀区太长,输入成本大头在后缀 | 检查 messages 中动态内容的 token 占比 |
| 偶尔命中、偶尔不命中 | 前缀中嵌入了会话级别的临时状态 | 确认前置消息序列是否包含上下文变量 |
| 首次请求成本偏高 | 缓存写入费用叠加 | 属于正常现象,评估高频调用后的整体收益 |
逐个解释一下。前缀内容不稳定是最常见的原因。我遇到过一个团队,把用户所在城市的天气信息放进了系统提示词,每天更新一次。看起来是静态内容,但因为天气数据每小时变化,他们的缓存实际上从来没命中过。排查方法很简单:连续抓取几次请求的 system 原文,逐字节 diff,任何不一致都可能导致失效。
动态后缀区过长的情况也经常被忽略。有团队把缓存前缀压得很短,只有 1500 token,但动态上下文拉到 6000 token。算下来缓存只覆盖了总输入的五分之一,节省自然有限。这时候应该回头审视动态内容的组织方式,而不是继续调缓存参数。
5.2 成本反而变高的罕见场景
有个情况值得单独提醒:某些极端条件下,启用缓存后成本不降反升。原因有两个:一是请求频率极低,缓存反复过期,每次都在交写入费;二是前缀被频繁修改,比如每次请求前都重新生成系统提示词,导致写入费用累积。
针对第一种情况,解决方案是评估请求频率,如果单日请求数少于几百次,缓存可能不适合当前场景。针对第二种情况,解决方案是把动态变化的字段从前缀区移出。比如版本号、当天日期、随机生成的 corr_id,这些都不应该出现在前缀区。
注意:缓存不是动画开关,开了就省钱。它是一个依赖前缀稳定性的机制,使用前必须梳理好 prompt 结构。我的建议是:任何变化频率高于缓存存活期的内容,一律不进前缀区。
5.3 多场景多版本前缀的治理方案
生产环境中,一个项目往往有多个业务场景,比如客服助手可能要区分订单咨询、售后投诉、商品推荐三种类型。如果三种场景的 prompt 前缀完全不同,就等于维护三份缓存。这时候需要考虑是共享前缀还是独立前缀。
我建议采用"公共前缀加场景分支"的结构。公共前缀包含通用的系统指令、行为规则、安全约束,所有场景共享。场景相关的特殊规则放在动态后缀区,通过传入场景 ID 来让模型选择相应的处理逻辑。这样做的优贷很明显:公共前缀足够长、足够稳定,缓存命中率高;不同场景的逻辑差异通过动态内容表达,不会破坏前缀的一致性。
当然,如果两个场景的规则差异实在太大,比如一个要求极简回复、一个要求详细分析,强行共享前缀反而会让模型行为混乱。这时候拆成两个独立的系统提示词,各自维护缓存,反而更清晰。关键在于平衡:共享前缀能省钱,但不要让业务灵活性迁就成本优化。
6. 治理机制建设:把成本优化变成常态化动作
6.1 建立 Prompt 变更审批机制
很多团队的成本失控,根源不在技术,而在流程。Prompt 是产品经理、运营、算法工程师都在碰的东西,今天改一句话,明天加一段规则,变动频繁且无记录。每次改动都可能让缓存前缀发生变化,导致缓存整体失效。更麻烦的是,没有人意识到这次改动带来了多大的成本波动。
我建议引入一个轻量级的 Prompt 变更管理机制。核心动作有三步:一是 Prompt 文件版本化,统一存放在代码仓库中,任何修改都走 MR/PR 流程;二是变更前后对比,明确标注修改区域,评估该区域是否属于缓存前缀;三是变更后观察缓存命中率指标,确认没有异常下降。
这个机制不需要额外开发系统,基于现有的 Git 仓库加一个简单的 review 流程就能跑起来。我在项目里就是这样做的,效果很好。产品经理想调整话术时,先提出变更,代码评审者会顺带检查一下缓存影响面。如果发现缓存前缀被改,就会评估成本影响、安排预热或接受一次性写入费用。
6.2 建立成本监控与预警面板
除了变更审批,还得有数据层面的监控。每天看一次账单是不够的,因为账单出来的时候,钱已经花了。我建议至少做到以下几点:
- 记录每天的请求总量、缓存命中率、缓存写入 token 量。
- 设置缓存命中率低于阈值的告警,例如连续 3 天低于 80% 就触发排查。
- 对比代码发布记录,确认每次 Prompt 变更前后的命中率趋势。
- 记录缓存写入费用占比,如果连续多天超过总输入成本的 10%,说明前缀不稳定。
这套监控不需要复杂的平台,用简单的定时任务加一个指标表也能实现。关键是形成习惯:每次发布后看一眼命中率,每周复盘一次成本趋势。持久优化靠的是机制,不是个人自觉。
6.3 定期前缀审计与清理
最后分享一个容易被忽略但很重要的动作:定期审计前缀内容。我在项目里每两周做一次前缀内容的 review,主要检查三件事:
前缀里有没有混入本应该放在后缀的动态内容。比如有人为了图方便,把当前用户等级直接拼进了系统提示词,结果缓存命中率大幅下降。这种问题通过审计很容易发现。
知识库文本是否还在持续使用。有些知识内容过期后没有删除,仍然占据着前缀 token。删除过期内容不仅能减少输入费用,还能让缓存前缀更紧凑、命中率更稳定。
工具定义是否有冗余参数。模型工具定义往往包含了大量描述性文字,如果某些字段实际不会被模型使用,可以考虑精简。每减少一个 token,都是在降低成本。
7. 写在最后的成本优化经验
这部分不写什么宏大结论,就分享几个我在实际项目中反复验证过的体会。
第一个体会是:Prompt 缓存的价值,不在于缓存本身,而在于它倒逼你重新审视 prompt 结构。很多团队的 prompt 是东拼西凑出来的,改来改去没有章法。为了接入缓存,你必须把稳定内容和动态内容分开,把系统指令、业务规则、知识库、工具定义梳理清楚。这个过程本身带来的收益,有时候比节省的费用还要大。因为清晰的 prompt 结构意味着更好的模型效果、更低的排查成本、更顺畅的协作流程。
第二个体会是:成本优化的第一步永远是度量。如果项目没有统计每天的 token 消耗,没有区分输入和输出、缓存和写入,那么任何优化手段都是空中楼阁。接入缓存前,先花一周时间记录基线数据。有了基线,你才能精准说出省了多少钱,也才能让团队其他人理解这项工作的价值。
第三个体会是:不要追求极端的最优解,而是追求可持续的方案。缓存前缀理论上可以做到几千 token 甚至上万 token,但前缀越长,每一次变更的代价就越大,知识库更新的灵活性就越差。我在项目里的做法是找一个合理的平衡点:缓存前缀覆盖所有真正不变的内容,但不强行把内容塞进去。如果某个知识模块每周都要更新,它更适合放在动态后缀区,而不是为了省那几十个 token 的读取费用去牺牲更新效率。
最后再分享一个小技巧:接入缓存时,建议先在测试环境用低频流量验证,再逐步放量。第一次上线时我把所有请求都切到了缓存版本,结果因为前缀里一个不起眼的换行符差异,缓存命中率只有 10%,但那天的账单里多了一堆写入费用。后来改成小流量灰度,用 usage 字段的四个 token 指标做对比,确认命中率达标后再切全量。这个流程多花两天时间,但能避免很多不必要的损失。
成本优化这件事,没有一劳永逸的方案。模型在更新,业务在变化,prompt 也在持续演进。只要你把前缀设计、缓存接入、监控治理这三件事形成闭环,成本控制就不再是每次账单日才想起来的事,而是融入日常开发流程的常规操作。