详解缓存亲和性在AI Agent循环中的重要性,揭示“换更便宜的模型”无法降低实际成本的本质,提供五种保持prompt缓存热度的工程实践。
你上线了一个 Agent。Tokens/月 暴涨。你切换到了"更便宜"的模型,账单却几乎没动。
问题在于:没有人会把定价页上写清楚这一点:在循环里,模型价格往往只是重复税的一个零头——而重复税本质上就是一个缓存亲和性问题。
这是《为什么 Agentic 系统应该关注缓存命中定价》的实战续篇。那篇文章论证了成本活在缓存行为里,而不是原始模型价格里。这一篇则是关于你实际能做什么。
大多数提供商(OpenAI、Gemini、Anthropic,以及基于它们之上的 OpenAI 兼容网关)都提供自动前缀缓存:如果你的 Prompt 开头与之前的某个请求字节完全一致,缓存的 Tokens 费用只有新鲜输入 Token 的一个零头。
关键词是"字节完全一致",而且只有相同部分位于 Prompt 开头时才有用。调换一行、在 System Prompt 之前注入时间戳,或者用新的 UUID 重新序列化历史——只要做了其中任何一件,你就自己驱逐了自己的缓存。模型根本看不到命中——每一步都全额支付输入费用。
所以"更便宜的模型"优化的是错误的数字。真正的杠杆是缓存亲和性:你的前缀在循环中有多稳定?
1. 将稳定的 System Prompt + 固定的 Tool Schemas 放在最顶部。 Tool 定义在步骤之间几乎不会改变。把它们放在最前面、原封不动,然后别再碰它们。
2. 只追加的历史记录。 每一步不要重新序列化整个对话。保持一份规范转录稿然后追加;让不变的前缀保持缓存状态。
3. 在稳定前缀之后注入易变上下文。 草稿板、检索到的文档和工具结果都可以——只要它们位于 System Prompt + 历史记录之下,而不是之上。
4. 将简单的 80% 路由到小模型,但保留共享前缀。 按场景路由是明智的。只是不要让小模型重新格式化前缀;不同的 Tokenizer 会悄无声息地破坏缓存。
5. 一个网关,一个规范的格式化器。 当三个子 Agent 各自用自己的方式格式化"上下文"时,你会得到三个互不兼容的前缀,缓存复用率为零。将 Prompt 组装集中化。
1. 每一步都重发整个对话。 每轮都回放完整记忆的框架,在一个会话中付出 O(n²) 的 Tokens。前缀永远无法稳定。
2. 用时间戳或 UUID 重新序列化历史并放在前缀中。 每次调用都有一个新的 updated_at = 永远的新前缀 = 永远没有缓存。
3. 将易变内容放在稳定前缀之上。 当前时间、请求 ID、Trace ID——如果它在 System Prompt 之前,就会毒化下游的每一次缓存命中。
4. 跨模型混用 Tokenizer 而没有稳定的规范形式。 在循环中切换模型而不规范前缀,会重置缓存并使输入成本翻倍。
5. 没有可观测性。 如果你无法看到每个请求的缓存命中与未命中,你就无法判断自己在做上述哪一种。你在最大的成本杠杆上盲目飞行。
让其他一切都可以被衡量的修复方案既无聊又决定性:每个请求的 Trace,报告每次调用时前缀是否命中缓存,以及多少 Tokens 被计费 vs 被缓存。
一旦你有了这个,O(n²) 陷阱就会作为一行条目显现出来。你停止猜测,开始像观察 p99 延迟一样观察缓存命中率。这是"我们削减了模型成本"和"我们削减了 Agent 成本"之间的区别。
"AI 落地"被当作模型访问问题来销售。实则不然。访问问题已经解决了。困难的部分是无聊的运维层:热缓存、可见的 Trace、合理的路由——默认如此,而不是在账单到来之后作为英雄式的重构。
如果你想要一个能呈现每个请求缓存命中/未命中,并在模型之间保持一个规范前缀的网关——这正是我们在 TokenLat 所构建的核心:https://tokenlat.com