当源数据已更改,你的 Prompt 缓存仍在提供旧的工具执行结果
一名支持代理在 14:02 查询了客户的订阅状态,发现其处于激活状态,该回答进入了 Prompt 前缀中,而缓存层刚刚将其标记为上下文的可重用部分。在 14:14,计费系统取消了该订阅。在 14:19,同一位客户提出了跟进问题,由于对话前缀仍然匹配,缓存的前缀被重用,代理愉快地告诉客户他们的计划处于激活状态,并主动引导他们使用一个他们已不再拥有访问权限的功能。下游系统是正确的。模型与上下文保持了一致。但用户被缓存命中(Cache Hit)欺骗了。
这是 Prompt 缓存为原本对数据陈旧度(Staleness)保持诚实的系统引入的失效模式。在引入缓存之前,工具调用是对单一事实源(Source of Truth)的请求,并遵循该源所声明的任何新鲜度契约。有了缓存之后,工具结果变成了 Prompt 前缀的一个租户,而前缀拥有自己的 TTL(生存时间),由模型提供商控制,团队中没有人明确选择启用它。
诱人之处在于,Prompt 缓存确实做到了文档所承诺的一切。Anthropic 的缓存会将前缀保留 5 分钟(或支付额外费用保留一小时 ),OpenAI 的自动缓存会重用超过 Token 阈值的最长匹配前缀,成本降低了,首个 Token 延迟(Time-to-first-token)降低了,命中率仪表盘也变绿了。这些都没有错。错误存在于缓存对“前缀匹配”的承诺与你的应用对“数据新鲜度”的隐含承诺之间的鸿沟中,而这个鸿沟是由你的用户的任何假设填补的。
Prompt 缓存命中是你未签署的新鲜度契约
缓存命中不仅仅是一项性能优化。它是对模型的一份声明:“此前缀中的 Token 是此请求的当前上下文。”如果工具结果存在于该前缀中,模型就会将其视为当前的真实情况。模型没有办法询问“这是 5 秒前还是 5 分钟前获取的?”——它只能看到 Token。
这使得 Prompt 前缀成为了流入其中的任何数据的“事实上的缓存”,其 TTL 由推理提供商的缓存层控制。在 14:02 获取并在 14:19 重用的客户记录正作为 14:19 的新鲜数据被提供,而不是 14:02 的新鲜数据。没有人故意这样设计;这是将易变数据放在缓存分界线(Cache Cut)之上的后果。
标准的生产建议——系统提示词在前,然后是工具,接着是静态文档,最后是用户消息——是为了“命中率优化”而编写的。它恰好在新鲜度方面也是正确的,但原因却是大多数团队没有内化的:缓存分界线之上的层被隐式地断言为时间不变的。当你把动态内容放在那里时,你不仅是在内容变化时损失了缓存命中率,你还在内容本身未变但底层源数据已变时,交付了陈旧的答案 。
这与分布式缓存设计者几十年前学到的关于 TTL 的教训是一样的:TTL 限制了从缓存角度看你的数据可能有多陈旧,但它无法告诉你在此期间源数据是否发生了变化。区别在于,HTTP 缓存对其本质是诚实的。而吸收了工具结果的 Prompt 前缀看起来像是对话的一部分,而不是缓存的一部分。新鲜度契约已经通过 LLM 上下文被“洗白”了。
失去一致性的两个缓存层
在任何生产环境的智能体中,调用路径通常存在两个缓存,它们由不同的团队拥有,并具有不同的 SLO(服务水平目标)。
第一个是工具结果缓存:CRM 前的 Redis 查询、记忆化的 HTTP 响应、数据库查询计划。它的 TTL 由负责数据层的人员选择,该选择通常反映了底层记录实际变化的频率——库存为 60 秒,产品目录为 15 分钟,静态 FAQ 为 24 小时。契约是明确的。通常在源数据发生变化时,会有某个地方触发失效钩子(Invalidation Hook)。
第二个是 Prompt 前缀缓存:模型提供商的 KV 缓存或其等效物,保存了生成模型第一个隐藏状态(Hidden States)的 Token 化前缀。它的 TTL 取决于提供商给你的值——自今年早些时候的静默回归以来,Anthropic 的默认值为 5 分钟——并且它完全不知道这些 Token 最初来自哪里。它无法被你的数据层作废,因为你的数据层甚至不知道它的存在。
这两层之间的漂移就是 Bug 所在。一个工具结果被获取,并以 60 秒的 TTL 存储在工具结果缓存中,同时流入 Prompt,被提供商缓存 5 分钟。60 秒 后,数据层作废了它的副本。下一个调用者将重新获取并得到新鲜数据——但 Prompt 前缀仍然持有陈旧的值,并且在剩余的 4 分钟内任何重用该前缀的请求都会得到旧答案。工具缓存履行了职责。Prompt 缓存也履行了职责。这种组合模式失效了。
这种情况之所以难以察觉,是因为这两个 TTL 是由不同的人商定的。设置 Redis TTL 的数据工程师从未听说过 cache_control 代码块。选择缓存断点位置的应用工程师不知道上游的失效契约是什么。没有一个单一的负责人来掌控“这个事实端到端可以有多陈旧”。
- https://platform.claude.com/docs/en/build-with-claude/prompt-caching
- https://developers.openai.com/api/docs/guides/prompt-caching
- https://developers.openai.com/cookbook/examples/prompt_caching_201
- https://www.prompthub.us/blog/prompt-caching-with-openai-anthropic-and-google-models
- https://agentmarketcap.ai/blog/2026/04/11/prompt-cache-hit-rate-engineering-2026
- https://medium.com/@arvisionlab/prompt-caching-for-ai-agents-how-to-cut-cost-and-latency-without-breaking-context-245dc2502b4b
- https://neuraltrust.ai/blog/prompt-caching
- https://redis.io/blog/prompt-caching-vs-semantic-caching/
- https://www.numberanalytics.com/blog/ultimate-guide-cache-invalidation-distributed-systems
- https://foojay.io/today/distributed-cache-invalidation-patterns/
