LLM 语义缓存:大多数团队都会忽略的成本控制层
大多数构建 LLM 应用的团队都了解 Prompt caching —— 这是 API 提供商提供的一种前缀重用机制,旨在对重复的输入 Token 进行折扣。部署其上一层技术的团队则少之又少:语义缓存 (Semantic Caching),它能彻底消除那些语义相同但表述不同的查询所产生的 LLM 调用。这种差距并非源于怠惰,而是源于对语义缓存供应商文档中 “95% 准确率” 含义的普遍误解。
那 95% 的数字指的是缓存命中时的匹配正确性,而不是缓存实际被命中的频率。实际生产环境中的命中率从开放式聊天的 10% 到结构化 FAQ 系统的 70% 不等 —— 在你编写任何缓存代码之前,你应该先计算出你处于该范围的哪一侧。
什么是语义缓存 (Semantic Caching)
传统的缓存基于精确的字符串匹配。对 “法国的首都是哪里?” 进行 SHA-256 哈希处理,只 有当有人再次发送完全相同的字符串时,你才会获得缓存响应。这适用于结构化数据库查询和 API 调用,但面向用户的 LLM 应用不会重复精确的字符串 —— 它们重复的是意图。
语义缓存运行在意图层级。其工作流程如下:输入的查询被转换为向量嵌入 (Vector Embeddings),这些嵌入会通过余弦相似度在存储了先前缓存的 “查询-嵌入” 对的向量库中进行搜索。如果相似度得分超过设定的阈值,则直接返回缓存的响应,无需进行任何 LLM 调用。如果低于阈值,则调用 LLM,存储响应,并为查询嵌入建立索引以供未来匹配。
“我该如何重置密码?”、“我忘记密码了,该怎么办?”以及“无法登录,求助”都会哈希成不同的字符串,但可以匹配相同的语义缓存条目。向量搜索会增加 5–20ms 的开销,但在命中时能消除 1–5 秒的 LLM 往返时间。
这与在 LLM 推理层内部运行的 Prompt/前缀缓存有着本质的区别。两者是互补而非竞争关系,理解其界限对于权衡成本至关重要。
两个缓存层
Prompt/前缀缓存(由 OpenAI 和 Anthropic 提供)通过重用重复 Prompt 前缀的计算出的 Key-Value 注意力状态来工作。如果你有一个 100,000 Token 的系统提示词,LLM 会处理它一次并缓存结果。后续带有相同前缀的请求将跳过这些 Token 的重新处理 —— 你仍然会发起 LLM API 调用,仍然会生成输出 Token,但输入 Token 的成本会降低 50–90%。
语义缓存则在命中时完全消除了 LLM 调用。响应是在之前的请求中生成的;缓存只是将其返回。没有推理发生。
失效模式的区别也随之而来。过期 的前缀缓存会返回根据旧上下文生成的正确答案 —— LLM 仍在进行新鲜的推理。而过期的语义缓存则会逐字返回旧的答案。当你开始考虑时效性内容、个性化以及 “缓存失效” 实际需要什么时,这种区别就变得至关重要。
一个合理的层级架构如下:
请求 → [精确哈希匹配] → [语义相似度匹配] → [前缀缓存] → 完整的 LLM 推理
精确哈希匹配的实现成本为零,通常可以覆盖约 18% 的真实生产流量(重复相同请求的用户和集成)。语义缓存将覆盖范围扩展到改写和近乎重复的内容。前缀缓存通过重用共享上下文来降低剩余 LLM 调用的成本。与对每个请求进行朴素推理相比,实现这三种缓存的团队通常能看到有效 Token 支出减少 70–80%+。
命中率的现实核查
你在大多数语义缓存供应商内容中看到的数字 —— “95%” —— 是匹配准确率:即缓存命中中返回正确响应的比例。它并不是指请求命中缓存的比例。
实际生产环境的命中率因用例而异:
- FAQ 和客户支持应用:40–70%
- EdTech 和辅导平台:~45%
- 分类和意图路由任务:40–60%
- 通用 RAG 流水线:18–60%(范围较广,高度依赖流量)
- 开放式对话聊天:10–20%
- 代码生成:5–20%
你的流量分布决定了你的上限。对多个系统的生产查询日志分析显示,大约 18% 的请求是完全重复的,而 60–70% 的真实查询是真正唯一的。如果你的应用处于较高的那个区间 —— 例如开发者询问新颖的代码问题,或者用户探索各种研究主题 —— 语义缓存可能永远无法收回成本。
正确的起点是分析一周的查询日志。对你的查询进行嵌入,运行两两相似度计算,并统计有多少在 0.85 以上。这个数字就是你的理论上限。如果它是 20%,那么在经过阈值调整后,你的实际上限可能接近 15%。
以每月 1,000。45% 的命中率可节省 $2,250。向量数据库和嵌入基础设施的成本通常不到节省费用的 5%。对于具有重复流量的应用,通常在几周内即可达到盈亏平衡点。
阈值选择与灰色地带问题
相似度阈值是语义缓存中最具影响力的配置决策,而且并没有唯一的正确答案。
通常的建议是从 0.88 的余弦相似度开始,然后在此基础上进行调整。这种直觉是合理的,但底层的几何特征产生了一个仅靠阈值调优无法解决的问题。
对于正确的缓存命中(即查询确实等同于缓存条目)和错误的缓存命中(即查询虽然相近但意图完全不同)的相似度得分分布,在 0.85 到 0.92 之间存在大量重叠。在这个范围内,没有哪个单一阈值能干净利落地将同义改写与不同意图区分开来。将阈值提高到 0.95 可以减少错误命中,但也会漏掉有效的同义改写。降低到 0.85 可以捕获更多改写,但会引入错误答案。
对此有几种实用的应对方案:
特定领域嵌入可以缩小灰色地带。 通用嵌入模型(如 all-MiniLM-L6-v2、text-embedding-ada-002)在标准阈值下的精确率可达到 64–78%。而在特定领域查询对上进行微调后的模型,在相同阈值下可以达到 84–92% 的精确率,同时还能实现更低的嵌入延迟。对于任何具有可观流量的生产系统,投入资源训练或采用特定领域模型通常都是值得的。
LLM 重排序增加了一个置信度层。 对于高风险用例,可以将排名靠前的语义候选条目通过一个低成本模型(如 GPT-4o-mini 或类似模型)进行处理,该模型在返回缓存响应之前,会明确判 断缓存查询与输入查询是否等效。这增加了约 100 毫秒的延迟和少量的边际成本,但为落入灰色地带的那部分查询消除了不确定性。
按响应类型分级设置阈值。 对于错误答案会造成损害的事实性查询,使用更严格的阈值(0.92–0.97);而对于 FAQ 或支持类查询,由于稍有偏差的回答成本较低,可以使用更宽松的阈值(0.85–0.90)。AWS 的验证缓存方法使用了三个层级:相似度在 80% 以上直接返回缓存答案;60–80% 将缓存答案作为少样本(few-shot)示例,但仍调用 LLM;低于 60% 则回落到标准推理。
缓存失效:四大难题
关于缓存失效是计算机科学中两大难题之一的格言,在语义缓存中比在大多数系统中更为贴切。
时效性内容。 任何昨天正确的响应今天都可能错误——价格、可用性、新闻,甚至是系统提示词更改后的模型行为。基于 TTL 的过期机制是标准方法:15–30 分钟为实时数据,数小时为业务数据,数天到数周为稳定的参考内容。但 TTL 要求你提前知道新鲜度窗口,而真实内容并不能整齐地划分为这些类别。
个性化。 语义缓存默认是全局的。如果两个用户都问“我的账户余额是多少?”,他们的嵌入向量将近乎相同,如果不进行范围限制,第二个用户就会得到第一个用户的答案。解决方法是在缓存查询中加入租户或用户标识符作为元数据过滤器——大多数生产实现都支持这一点,且开销可以忽略不计。这种架构上的影响在于,个性化响应不应进行全局缓存,这会立即缩 小你流量中可缓存的部分。
嵌入模型升级。 旧的嵌入和新的嵌入是不可比的。当你升级嵌入模型时,使用新模型对旧嵌入计算出的相似度得分是毫无意义的——0.9 的相似度得分不再代表它过去的含义。当嵌入模型更改时,必须使整个缓存失效或进行版本化。从业者经常提到,这是团队在升级导致缓存错误响应激增后才痛苦发现的失效模式。
缓存幻觉。 如果原始 LLM 响应是错误的,那么未来每一个与该原始查询语义相似的查询都将收到相同的错误答案。缓存不仅保留了正确的响应,它还放大了错误。实际的缓解措施包括:在缓存前应用质量门禁(最小响应长度、格式校验、结构化输出的置信度得分),实现触发标记响应逐出的用户反馈循环,并且绝不缓存未通过验证的响应。
安全:大规模缓存投毒
语义缓存引入了一个纯服务端缓存所不具备的安全攻击面:能够影响缓存内容的攻击者可以向未来的许多用户投放毒害响应。
研究表明了一种缓存投毒攻击,通过构造对抗性提示词使 LLM 生成恶意响应,然后这些响应被索引到语义缓存中。由于语义匹配是模糊的,随后与注入提示词语义相似的正当查询就会检索到恶意的缓存响应。在根据缓存内容调用工具的智能体(Agent)场景中,这种攻击对注入响应的命中率达到了 90.6%。
纵深防御的方法包括:在存储前根据允许的格式和内容策略验证缓存响应;应用输出边界强制执行(不要缓存未通过内容过滤的原始 LLM 输出);使用用户/租户元数据限制缓存条目的范围以缩小影响范围;以及监控缓存命中模式是否存在异常(特定查询簇的命中率突然飙升可能表明存在注入攻击)。
语义缓存不划算的场景
在以下特定的工作负载中,语义缓存往往难以弥补其带来的运维复杂性:
开放式对话聊天。 用户在持续对话中的每一轮都会生成真正新颖的查询。先前的对话上下文意味着两个文本相同的查询可能需要不同的响应。在此类别中,10–20% 的缓存命中率通常不足以覆盖基础设施和维护成本。
代码生成。 开发者的查询通常很长、很具体且具有唯一性。即使它们在主题上相似(如“编写一个 Python 函数……”),所需的输出差异也足够大,以至于在任何合理的阈值下,语义匹配都会在关键细节上产生错误的响应。
复杂的多步推理。 需要整合多条信息的问题很少会有语义等价的先前查询,而且对于看似相似的问题,缓存的响应往往在关键细节上是错误的。
快速演变的领域。 如果你的底层内容变化足够频繁,导致大多数缓存响应的有效生命周期极短,那么你就在承担缓存的运维成本,却无法获取多少实际收益。
正确的决策过程应该是:首先衡量你的流量,计算理论命中率上限,模拟该上限下的成本节约,减去基础设施和维护成本,然后才决定是否构建。对于许多应用来说,仅靠 Prompt/Prefix 缓存(在应用层几乎零运维开销)就能实现语义缓存所能带来的 50–90% 的成本降低。
实用的初始架构
如果你的流量分析显示有相当比例的语义相似查询,那么最小可行实现(MVP)如下:
查找路径: 对输入的查询进行 Embedding,在向量数据库中以约 0.88 的余弦阈值(Cosine Threshold)进行搜索,应用针对租户/用户范围的元数据过滤,命中时返回缓存响应。
存储路径: 在收到 LLM 响应后,运行质量验证,然后将响应连同 Embedding 后的查询、适合内容类型的 TTL(生存时间)以及相关的元数据一起存储。
不要从自定义向量存储开始。GPTCache、Redis LangCache 和 Upstash Semantic Cache 都提供了生产就绪的实现,可以处理 Embedding、存储和检索的底层细节。工程投入应该集中在阈值校准、元数据范围限定和 TTL 策略上——这些是库无法解决的部分。
分别衡量命中率、误报率(返回错误响应的缓存命中)和延迟分布。一个命中率为 40%、误报率为 5% 的系统可能值得运行。而一个命中率为 15%、误报率为 8% 的系统则可能不值得,无论厂商文档中关于准确性的描述多么动听。
- https://redis.io/blog/what-is-semantic-caching/
- https://redis.io/blog/prompt-caching-vs-semantic-caching/
- https://redis.io/blog/10-techniques-for-semantic-cache-optimization/
- https://preto.ai/blog/semantic-caching-llm/
- https://arxiv.org/abs/2411.05276
- https://arxiv.org/html/2504.02268v1
- https://arxiv.org/html/2601.23088v1
- https://github.com/zilliztech/GPTCache
- https://portkey.ai/blog/reducing-llm-costs-and-latency-semantic-cache/
- https://aws.amazon.com/blogs/machine-learning/reducing-hallucinations-in-llm-agents-with-a-verified-semantic-cache-using-amazon-bedrock-knowledge-bases/
- https://www.getmaxim.ai/articles/top-semantic-caching-solutions-for-ai-applications-in-2026/
- https://www.truefoundry.com/blog/semantic-caching
- https://www.ndss-symposium.org/ndss-paper/when-cache-poisoning-meets-llm-systems-semantic-cache-poisoning-and-its-countermeasures/
