提示缓存:同一个智能体,同样的规则,账单只剩约四分之一

HN Code LLM Research 2026-10-31T15:52:56.464035

一条从 0% 升到 94% 的折线:一个小食谱智能体的提示命中缓存的比例,四个步骤前后对比。

提示缓存(prompt caching)已经被讲过很多次了。大厂商的文档解释了它的原理,也有不少文章讲某个团队改了一处之后命中率如何飙升。我们想补一个视角:把这件事直接画出来——同一个智能体的提示,每一步都用同样的方式绘制,每张图里都标出实测数字。有时候同一个道理换个画法,才会让人恍然大悟。

为此,我们照着自家智能体踩过的坑,做了一个小食谱智能体:提示看上去没什么问题,四个步骤改完,每个版本都发到真实的模型 API 上跑。同一个模型,同样的规则,一个字没改——提示成本降低了 73%,整个测试花费不到一美分。最后我们还会给出一个防止这些优化被改回去的测试,以及在自家智能体上做同样四步的效果。

提示缓存怎么决定你付多少钱

每次调用模型,整个请求都要重新发一遍:工具、指令、对话。提示缓存让服务商保留已经处理过的请求开头部分,下次请求开头完全一样时直接复用。按 OpenAI 和 Anthropic 的文档,从缓存读取的价格是正常输入价的十分之一甚至更低,写入缓存则贵四分之一。

背后的规则很短:提示缓存会复用从开头到第一个变化点为止的所有内容。整件事就这么简单——永远不变的内容放前面,会变的内容放后面。

食谱智能体上的四个步骤

我们的测试智能体负责写食谱。它有两个工具,find_in_pantry 和 save_recipe,大约一千字的规则,还有每个人的档案。应用有两个套餐:Pro 可以保存食谱,免费版不行。Ana 是 Pro 用户,素食者;Leo 是免费用户。某个晚上,两人各发了三条消息。

一个看起来没问题的提示词

很多 agent 一开始都会这么写。日期放在最上面,让 agent 知道今天是几号;接着是 Ana 的个人信息,方便做个性化;她选的套餐决定能用哪些工具。和不少 agent 框架一样,它每次调用模型之前,都会把整个提示词重新拼一遍。

recipe agent 的第一版提示词:先是她套餐对应的工具,然后是日期、Ana 的信息和规则,最后是她的消息。当晚只有 0% 来自缓存。

缓存明明开着,却一点都没命中。每条消息都把整段提示词写进缓存,却没有一条去读它,结果这一晚的开销比完全不开缓存还高。

第一步:把每条消息都会变的内容挪到最后

日期每分钟都在变,而它偏偏放在最上面。它后面的所有内容,每来一条消息都得重新算,包括那上千字的规则。两份指南说的是同一件事:每次请求都会变的东西,放到最后。于是日期被挪进了用户消息里。

第一步:日期从指令区挪到用户消息里。当晚 65% 来自缓存。

现在每个人后续的消息,几乎整段提示词都能从缓存里读到,当晚的缓存命中率从 0% 升到了 65%。凡是每条消息都会变的东西,都放到最后。

提示缓存能复用的,是「第一个变化点」之前的所有内容。

可 Leo 的第一条消息,还是什么都没读到。

第二步:个人化信息别放进共享部分

Ana 的信息是因人而异的。把它留在指令区里,别人的请求就没法复用 Ana 那段提示词。于是这些信息被挪进用户消息,指令区则变成所有人完全相同的文本。

第二步:Ana 和 Leo 的信息挪到用户消息里。Leo 的工具列表更短。当晚 64% 来自缓存。

晚上又降了一点,到 64%,因为 Ana 的详细信息现在会跟着她每条消息一起发送。而 Leo 仍然什么都没读到。属于个人的内容排在所有人共享的内容之后,但光这样还不够:还有地方不一样,而且它出现在指令之前。

第 3 步:让每个套餐拿到相同的工具列表

工具排在指令之前,而 Leo 的免费套餐只给了他一份更短的列表。他的请求从第一行起就和 Ana 不同,因此无法复用她留在缓存里的任何内容。

解决办法是给每个套餐发送同一份列表。列表只描述有哪些工具,并不决定 Leo 能用什么。能不能用是另外决定的:OpenAI 的指南说,要"限制哪些工具可被调用,同时保持所提供的工具列表稳定",而真正执行工具的代码会拒绝他套餐不允许的任何调用。给所有人同一份菜单,在点单时再决定每个人能点什么。

第 3 步:Leo 拿到和 Ana 相同的工具列表,尽管他的套餐用不了 save_recipe。这个晚上 80% 的内容来自缓存。

Leo 的第一条消息现在复用了 Ana 留在缓存里的内容,这个晚上的缓存命中率达到 80%。套餐依然决定每个人能做什么,只是不再决定提示词里写什么。

第 4 步:工具调用几乎免费:追加,别重建

为了回答 Ana,agent 先查她的食品储藏室:模型请求调用 find_in_pantry,agent 执行它,然后带着结果再次调用模型。如果这次调用只是在末尾追加工具调用及其结果,那么它们之前的所有内容都来自缓存。OpenAI 的指南说得很直白:"追加新消息,而不是重写之前的轮次。"

第 4 步:工具调用之后的那次调用,在请求末尾加了两行。其中 94% 来自缓存。

工具调用读完之后的那次请求,94% 的 prompt 来自缓存。一个 agent 会发出很多这样的调用,所以它们几乎不花钱。而在最初的版本里,agent 每次调用前都把整个 prompt 重建一遍,顶上还加一个新的时间戳,同样的调用从缓存里啥也没读到。一次工具调用应该往请求上追加,绝不能重建请求。

四个动作加在一起的效果

四个动作合起来,改变了那晚的成本。按每百万输入 token 1 美元算,每千次调用的 prompt 成本从 1.76 美元降到 0.48 美元,工具调用之后的那次只要 0.25 美元。

Results by step: 0%, 65%, 64% and 80% of the evening from the cache, and 94% for the call after a tool. Prompt cost per 1,000 calls from $1.76 to $0.48, and $0.25 after a tool call.

这四个动作没有改动规则里的任何一个字。测试跑在 gpt-6-luna 上,关闭了推理;换成任何模型,把这份成本乘以它每百万输入 token 的价格即可。

检查你自己的 agent

取你 agent 发出的两个请求,来自两个人,相隔一分钟,找出它们第一个不一样的地方。它之前的内容都可以走缓存;之后的内容每次调用都要付费。如果这个位置靠近开头,看看那儿放了什么:一个日期、一个名字、一份只为某个方案构建的工具列表。

然后用一个测试把它固定住。这个测试不调用模型:它构建两个请求,比较它们的开头。

如果以后有人把日期放回开头、把名字写进指令,或者为某个方案裁剪工具列表,第一个检查就会失败。如果有人工具调用后重建 prompt,第二个就会失败。

我们自己的 agent 是怎么做的

在之前一篇讲自建 harness(运行框架)的文章里,我们说过缓存值得单独写一篇。今年十月,我们在自己的真实 agent 上做了同样的调整:时间戳挪到每个请求的末尾,工具定义不再随项目、角色或语言变化,每次工具调用都往请求上追加,而不是重建。

一次工具调用应该往请求上追加,绝不能重建请求。

实际效果比这个晚间小测试还要好。我们的智能体经常在每条消息中发起多次工具调用,而这些调用正是缓存读取量最大的部分。工具调用越多,平均值越高。我们的仪表盘从 60% 多升到了 93%。我们开发的产品还在起步阶段,人少、消息间隔长,所以很多调用遇到缓存已过期的情况,就像 Ana 当晚的第一条消息那样。如果持续使用,我们预计能达到 96% 到 98%。

前后对比:同一个智能体、同样的规则,只是顺序不同。之前每次调用从缓存读取 0%;之后,工具调用后的那次调用从缓存读取了 94%。

看看你提示词最前面的几行。如果每次调用它们都会变,就把它们移到末尾,剩下的交给缓存。

加入候补名单。

Werk 还没正式发布。现在注册,可以比别人更早获得访问权限。

查看原文