收益:
- 解释一下提示缓存的前缀匹配逻辑
- 通过将固定上下文放在前面、将可变上下文放在后面来增加缓存命中率
- 可以计算缓存写入/读取经济性和收支平衡点
LLM 产品的原型看起来很便宜;当你走到秤上时,账单会让人大吃一惊。在大多数工作负载中,大部分账单来自于每次请求时一遍又一遍发送的相同固定上下文:长系统提示、规则手册、参考文档。即时缓存恰恰消除了这种浪费。在本单元中,您将学习缓存是如何工作的,如何安排提示命中,以及如何计算缓存经济的盈亏平衡点。如果安装正确,它本身就可以将您的账单减少一半甚至更低。
缓存如何工作?一条不变的规则
提示缓存是前缀匹配。提供程序临时存储自提示开始以来它已处理的令牌。如果下次请求时提示以相同的前缀开头,则不会重新计算此公共部分;读取比缓存便宜得多。
由此得出一个不可变的规则:如果前缀中任何位置的单个字节发生更改,则整个缓存从该点开始将变得无效。也就是说,固定内容应该在开头,可变内容应该在结尾。如果你在系统提示符的开头放一行随每个请求而变化的行,例如“今天的日期:18.07.2026”,则其后面的所有内容都将无法进入缓存。
处理顺序通常为:工具→系统提示→消息。您将缓存点(断点)放在固定部分的末尾。
缓存经济
缓存分为三个价格等级:
- 缓存写入:第一次存储。 ~1.25 倍正常输入价格(5 分钟存储)。
- 缓存读取:读取后续请求。 ~正常投入价格的 0.1 倍——即十分之一。
- 普通输入:不进入缓存的部分,每次都按全成本处理。
盈亏平衡点:第一个请求支付写入费用(1.25×)。从第二个请求开始,读数 (0.1×) 开始发挥作用。粗略地说,您将在两个请求上并驾齐驱;之后就是净储蓄。固定上下文越大,重用的请求越多,增益就越大。
设想
缓存有用吗?
大型固定系统提示,数千个请求
是的——收入最高
关于同一参考文档的许多问题
是的
每个请求的短文本完全不同
不——写奖金被浪费了
一次性请求
不——根本不读书
日期/ID 在系统提示时随每个请求而更改
否 — 前缀已损坏,命中为零
一步一步:如何设置点击提示?
- 分开常量和变量。哪些内容永远不会改变(系统提示、规则手册、文档)?每个请求(用户问题、日期、ID)会发生哪些变化?
- 将常数放在开头。在加工过程中,首先的部分(工具、系统)必须稳定。
- 将变量放在最后。最后是用户当前的问题。
- 将标志放置在边框的末端。将缓存点放在固定部分的最后一个块中。
- 验证命中。检查响应中的使用字段中的cache_read_input_tokens是否大于零。如果为零,则前缀中存在隐藏的干扰因素。
{ "system": [ { "type": "text", "text": "{{large_constant_system_promptu_and_rules}}", "cache_control": { "type": "ephemeral" } } ], "messages": [ { "role": "user", "content": "{{user_current_question}}" } ]}
提示:不要猜测缓存命中,而是测量它们。如果连续请求时 use.cache_read_input_tokens 仍然为零,则静默断路器(系统提示符下的 datetime.now()、无序 JSON、随每个请求更改的工具列表)正在运行。逐字节比较两个请求的原始提示并找出差异。
无声的干扰者
不知不觉中损坏缓存的典型模式:
# BREAKER: 在系统提示中嵌入随每个请求而变化的信息 "Today's date: {{now}}. You are an Assistant..." ← 前缀随每个请求而变化,hit 为零# TRUE: 将变量移动到 messagesystem: "You are an Assistant..." ← 常量进入缓存messages: [{role: user, content: "Today is {{now}}. Question: ..."}] ← 变量在末尾
其他破坏者:JSON 对每个请求进行不同的排序(将键保持固定顺序)、工具列表因用户而异(首先处理工具;如果它们发生变化,则不会进入缓存)、在对话中更改模型(缓存是特定于模型的)。
弱提示/强提示(缓存友好结构)
# 弱(缓存破坏构建)系统:“日期:2026 年 7 月 18 日 14:32。用户:Ahmet(id 8842)。你是一个支持机器人。规则:...(2000 个代币)...”
# STRONG(缓存友好结构)系统:“您是一个支持机器人。规则:...(2000 个代币,永不更改)...” [缓存标志]消息:[ { 角色:用户,内容:“日期:2026 年 7 月 18 日 14:32。用户 ID:8842。问题:如何发起退款?” }]
在弱版本中,2000 个代币的规则块在每个请求上都按全部成本进行处理。在强版本中,同一个块只写入一次,并以十分之一的价格在所有后续请求中读取。
三个迷你箱
案例 1 — 缓存规则手册。会计自动化正在将 12,000 个代币的规则手册添加到每张发票中;每天 5,000 个请求。无缓存输入成本约为每天 180 美元。他们保持规则手册不变并缓存它:第一个请求支付写入费用,后续读取支付 0.1×。投入成本下降约 90% 至每天约 18 美元。
案例 2 — 隐藏日期变更线的成本。一个团队设置了缓存,但没有命中; cache_read_input_tokens 始终为零。原因:系统提示符第一行有datetime.now(),前缀随着每次请求而变化。当我们将日期移至用户消息中时,命中率突然从 0% 增加到 94%。
情况 3 — 缓存放错位置。搜索应用程序针对每个请求发送完全不同的简短查询;他们急切地添加了一个缓存标志。由于没有公共前缀,每个请求仅支付写入费用,无需读取,从而增加了成本。他们移除了标志。教训:只有当存在重复使用的大且恒定的前缀时,缓存才会付费。
常见错误
- 混合常量和变量:当变量内容在前缀中时,命中被重置。
- 在系统提示中嵌入日期/ID:最常见的无声破坏者。
- 不测量命中:如果不检查cache_read_input_tokens,则不会注意到浪费。
- 无公共前缀时添加缓存:只需支付写入费用,成本增加。
- 更改车辆列表或型号:前缀从一开始就被打破;一切都被重写。
- 忘记最小缓存大小:非常短的缓存(根据型号,大约 1-4k 令牌以下)不会静默地进入缓存。
更深入:按工作负载类型设计缓存
缓存的实际回报取决于工作负载的性质;所以首先要了解你的流量。三种典型模式及正确安装:
共同的系统提示,不同的问题。最常见的企业模式:包含数百个不同用户问题的大型系统提示(角色、规则,可能是参考文档)。这里最初缓存的是固定部分(系统);每个新问题只需为其一小部分支付全价。收益非常高,因为大部分都是以十分之一的价格反复念诵的。
多轮独白。随着对话的拖延,每一轮新的对话都建立在之前所有历史记录的基础上。如果将缓存标志放在最后一轮的末尾,则每个请求都会重用之前的会话前缀;随着对话的深入,点击量会不断增加。这极大地控制了长时间助理会议的成本。
共享前缀是最后一位要改变的。多个请求共享大量固定先验(样本集、指令),但最后由一个问题分隔开。您将缓存指针放在共享部分的末尾;否则,每个请求都会写入自己单独的缓存,并且不会读取任何内容。
需要注意的是:缓存取决于型号和特定的最小大小。即使您标记它们,非常小的前缀(在几千个令牌以下,取决于模型)也不会默默地进入缓存 - cache_creation_input_tokens 保持为零。此外,在对话中更改模型会使整个缓存失效;如果不同的任务需要廉价的模型,请将主要流程保留在一个模型中,并将副业放在单独的调用中。
综上所述
提示缓存是前缀匹配:固定内容应该在开头,可变内容应该在结尾。对于大型的、重用的上下文,读取成本是全价的十分之一,在两个请求中大致收支平衡。最常见的错误是通过在系统提示符中嵌入变量数据来破坏前缀;您可以通过在使用字段中测量来验证命中。
应用任务
选择工作负载。 (1) 将内容分为两栏:“从不改变”和“随每次请求而改变”。 (2) 重新绘制提示结构,将常量部分放在开头,将变量部分放在末尾。 (3) 估计固定部分的代币大小,并比较有/没有缓存的每月成本。 (4) 注意您将验证哪个字段 (cache_read_input_tokens) 的命中。
清单
- [ ] 我可以解释一下,缓存是前缀匹配,也是唯一不可变的规则。
- [ ] 我可以通过将固定内容放在开头并将变量放在结尾来提高准确性。
- [ ] 我知道写/读经济学和两个请求盈亏平衡点。
- [ ] 我可以识别无声干扰(日期、无序 JSON、更改车辆列表)。
- [ ] 我可以使用usage.cache_read_input_tokens验证命中。