跳到主要内容

AI缓存失效:为什么答案可以改变时每个缓存层都更难处理

· 阅读需 11 分钟
Tian Pan
Software Engineer

Phil Karlton有句名言——"计算机科学中只有两件难事:缓存失效和命名"——这句话诞生于语言模型进入生产之前。将AI加入技术栈后,缓存失效不只是变得更难;它在每一层同时变得更难,而且每一层的原因从根本上不同。

传统缓存存储的是确定性输出:数据库行、渲染的HTML、计算出的价格。当源数据变化时,你使该键失效,下一个请求获取新数据。契约很简单,因为答案是一个事实。

AI缓存存储的是不同的东西:对查询的响应,而这些响应的"正确"答案取决于上下文、时效性、模型行为以及模型所获取的源文档。这里的"陈旧"不意味着过时——它意味着在语义上出错,而你的监控不会发现,直到用户注意到为止。

你可能没有区别对待的四个缓存层

一个生产级LLM应用通常至少有四个不同的缓存层,每层都有不同的失效特性:

提示缓存(由Anthropic和OpenAI提供)存储提示前缀的KV计算。这些最接近传统缓存——它们对token序列进行精确匹配,对缓存前缀的任何更改都会使下游所有内容失效。失效规则是机械的但不容忽视:重组系统提示,将频繁变更的内容移到文档更高处,就会驱逐整个缓存。

语义缓存(如GPTCache、Redis语义缓存等工具)基于嵌入相似度而非精确字符串匹配来存储LLM响应。余弦距离在阈值内的新查询会得到缓存的响应。这些是最难推理的危险缓存——因为没有明确的失效边界——同一个缓存响应可能为数千个语义相似但不完全相同的查询提供服务。

RAG检索缓存存储向量相似度搜索的结果:给定查询嵌入,返回这些文档。当源文档更新时,缓存的检索结果可能指向陈旧的文档版本,或遗漏新近相关的内容。

嵌入索引是底层——文档的预计算向量表示。当文档更改时会重建,但重建代价高昂且通常是异步的,这意味着存在一个窗口期,你的检索在陈旧的嵌入上运行。

每一层都有不同的故障模式、不同的适当一致性模型,以及不同的等待爆发的生产事故。

为什么传统失效在每一层都失效

核心问题不是技术性的——是语义性的。传统缓存存储事实。AI缓存存储在特定模型、特定源文档集合和特定时刻下适当的响应。TTL和基于标签的失效假设你能在"仍然有效"和"陈旧"之间划清界限。AI缓存不提供这条线。

语义缓存会放大幻觉。 如果LLM对某个查询生成了错误的响应,未来所有语义相似的查询都会从缓存中获取那个幻觉内容。错误不会衰减——它会复利累积。标准缓存可能提供陈旧数据直到你推送修复;语义缓存会提供错误数据,直到你识别出那个特定的查询簇并明确使其失效。

嵌入模型升级是你不会注意到的缓存破坏事件。 当你升级嵌入模型——从旧模型迁移到更新、更强大的版本——新嵌入在数值上与旧嵌入不兼容。模型A的0.9相似度分数与模型B的含义完全不同。如果你没有将模型版本作为缓存键的一部分,你会无声地混合来自不同模型的嵌入,并得到悄然出错的检索结果。

语义漂移使TTL失去意义。 2022年提交到开发者文档系统的查询中,"Python"这个词有一种分布,而现在则有另一种分布。产品名称改变了含义。监管术语不断演化。六个月前正确表示文档的缓存嵌入,现在可能映射到嵌入空间的错误区域——不是因为文档改变了,而是因为围绕它的语言改变了。

文档更新不会在缓存栈中传播。 一家法律科技公司曾深受其苦:律师更新了一份合同,再次搜索时,却通过RAG检索缓存获取到了旧文档的缓存版本。检索缓存不知道源文档已经更改。传统缓存失效会很简单——当文档更改时你知道要使哪个键失效。但检索缓存是按查询嵌入为键的,而不是文档ID,所以没有干净的失效路径。

为每个缓存层匹配一致性模型

并非每个缓存层都需要相同的一致性保证,试图在所有地方应用强一致性会损害你的延迟,而不会有效地提高正确性。

加载中…
Let's stay in touch and Follow me for more thoughts and updates