一位用户反馈称,我们的翻译插件对每个请求都返回相同的缓存结果,无论输入内容如何。我们进行了调查,结果发现了一个更严重的问题:我们平台上 95% 的语义缓存命中都是误报。199 个不同的翻译请求,198 个唯一的请求体,却向所有请求提供了同一个缓存响应。
如果您关注长生命周期的 Agent 状态和生产环境请求处理,本文与 《为什么您的 AI Agent 总是在丢失记忆》、《单密钥聊天机器人指南》 以及 《AI API 速率限制指南》 是绝佳的配套阅读。
核心要点
- 平台 95% 的语义缓存命中均为误报,198 个唯一的请求被提供了同一个缓存响应。
- 根本原因是结构化输入。固定的模板文本在 Embedding 向量中占据主导地位,导致内容的变化几乎无法改变余弦相似度。
- 提高相似度阈值无法解决此问题,因为正确和错误的命中分布存在重叠。关于语义缓存可靠性的最新研究证实了这一模式。
- 修复方案分为两层:在 Embedding 之前提取有意义的内容,然后使用快速的 FNV-1a 指纹哈希验证每次命中。这使误报率从约 95% 降至 5% 以下。
- 模型选择会影响暴露程度。无论由哪个模型完成补全,冗长的系统提示词(System Prompt)和 JSON 封装的输入都会加剧该问题;如果您正在决定通过哪些模型路由缓存流量,请查看 TokenLab 的 模型目录(观察日期:2026-07-07)以获取当前模型选项。
错误报告
报告很简单:“我禁用了语义缓存,但每次翻译返回的结果都一样。”
三个请求 ID,三个不同的翻译片段,却有着相同的缓存响应。请求体大小从 1,564 字节到 8,676 字节不等。但所有请求的缓存响应 ID 都是同一个:chatcmpl-DG6J03nhdvcF7Ek0C8rJkjh7lN9pF。
最初的怀疑是:用户的缓存设置未生效。结果发现这是一个独立的数据源同步 Bug(管理面板写入一个表,而 API 网关从另一个表读取)。修复该问题只解决了一半。即使在缓存启用且工作正常的情况下,语义缓存依然匹配到了本不该匹配的请求。
生产环境数据
我们从 ClickHouse 中提取了 24 小时的缓存命中数据。数据表现非常糟糕。
| 模型 | 总请求数 | 缓存命中 | 唯一请求数 | 唯一响应数 | 命中率 |
|---|---|---|---|---|---|
| DeepSeek V4 Flash | 200 | 199 | 198 | 1 | 99.5% |
| glm-4.6-thinking | 100 | 38 | 13 | 1 | 38% |
| gpt-5-nano | 31 | 29 | 28 | 2 | 93.5% |
| gpt-oss-120b | 18 | 17 | 17 | 1 | 94.4% |
| qwen3-vl-flash | 17 | 16 | 16 | 1 | 94.1% |
198 个唯一的翻译请求,全部返回同一个缓存响应。那不是缓存,那是一个返回常量的损坏函数。
所有受影响的模型都有两个共同点:所有请求来自同一个用户,且都使用了固定的系统提示词模板,仅用户内容有所变化。由于产品线经常变动,TokenLab 的 模型目录(观察日期:2026-07-07)是获取平台可用模型列表的唯一事实来源。
如何在您的系统中检测此问题
您不需要我们的日志也能发现是否存在同样的问题。最快的信号是每个模型的响应多样性。如果一个模型有很高的缓存命中率,但几乎没有唯一的响应,那么您就是在用一个答案回答许多不同的问题。
以下是我们使用的 ClickHouse 查询语句(已泛化):
SELECT
model,
count() AS total_hits,
uniqExact(substring(response_body, 1, 200)) AS unique_responses,
round(uniqExact(substring(response_body, 1, 200)) / count(), 3) AS diversity_ratio
FROM request_logs
WHERE cache_hit = true
GROUP BY model
ORDER BY total_hits DESC;
健康的缓存其 diversity_ratio 应接近 1.0,这意味着大多数命中会为不同的输入返回不同的响应。比率接近 0 意味着许多请求都坍缩到了少数几个缓存答案上。对于具有真实输入多样性的模型,任何低于 0.5 的比率都值得调查。
如果您没有记录响应体,一个更廉价的代理方法是:比较唯一请求体的数量与从缓存中提供的唯一响应的数量。当 198 个唯一请求映射到 1 个响应时,缓存匹配的不是语义,而是样板文件。
第二个迹象是用户对结构化工作负载的投诉。翻译插件、摘要工具、表单填充器和 JSON 输入/输出工具通常是罪魁祸首,因为它们将可变内容包裹在固定的模板中。
为什么 Embedding 在结构化输入上会失败
翻译插件发送的请求如下:
System: "Act as a translation API. Output a single raw JSON object only.
Input: {"targetLanguage":"<lang>","title":"...","segments":[...]}"
User: {"targetLanguage":"zh","title":"Product Page",
"description":"Translate product descriptions",
"tone":"formal",
"segments":[{"text":"actual varying content here"}]}
系统提示词在所有请求中都是相同的。用户消息是一个 JSON 对象,其中 targetLanguage、title、description 和 tone 都是固定的。只有 segments[].text 会发生变化。
当我们的语义缓存提取文本进行 Embedding 时,它会连接系统提示词和用户消息。固定模板约占文本的 80%。Embedding 模型(all-mpnet-base-v2,768 维)将其压缩为一个向量,其中模板结构占据主导地位。实际的翻译内容几乎无法改变向量方向。
结果:"translate 'Hello world'" 和 "translate 'The quarterly financial report shows a 15% increase in revenue'" 之间的余弦相似度超过了 0.95。我们的阈值是 0.95。因此,每个翻译请求都会匹配到第一个缓存条目。
深入日志后,我们发现了三种导致此问题的场景:
翻译插件是最严重的违规者。固定的 JSON 键值对淹没了实际的翻译片段。DeepSeek V4 Flash 和 gpt-5-nano 都遇到了这个问题。
上下文摘要助手则出现了另一种变体。它的系统提示词非常长,以至于 5KB 到 47KB 的用户内容在 Embedding 中几乎没有体现。这就是为什么 glm-4.6-thinking 会对每段对话返回相同的摘要。
第三种模式更隐蔽。对于 gpt-oss-120b 和 qwen3-vl-flash,每个请求的前 500 个字符是逐字节相同的。可变内容在后面,但 Embedding 已经被共享的前缀所主导。
研究结论
这不是一个新问题,近期的论文已经对其进行了量化。
加州大学伯克利分校的 vCache 项目发现,正确和错误的缓存命中相似度分布存在严重重叠,这意味着没有固定的阈值可以清晰地将真实匹配与结构相似的误报区分开来。这一发现与我们在生产环境中的观察完全一致:翻译插件的误报集中在 0.95 以上,这恰好也是合法的释义匹配所在的范围。
其他关于语义缓存可靠性的研究也得出了类似的结论:原始的 Embedding 相似度是缓存正确性的必要条件而非充分条件。任何仅依赖此指标的生产系统,在处理结构化、模板密集的流量时,都应预料到会有显著的误报率。
两层修复方案
第一层是内容提取。在 Embedding 之前,剥离固定的系统提示词和模板脚手架,仅对可变负载进行 Embedding:即实际的 segments[].text 内容,而不是周围的 JSON 键和样板文字。仅此一项就能显著提高 Embedding 向量的信噪比。
第二层是指纹验证。即使提取效果更好,近乎重复的内容仍可能产生高相似度得分。在提供缓存命中之前,对传入请求和缓存条目的提取内容计算一个快速哈希(我们使用了 FNV-1a)。如果哈希完全匹配,则提供缓存。如果不匹配,则回退到新的补全请求,或者对于高价值流量,将其路由到廉价的验证调用,以评估语义而非字节。
错误之处在于完全跳过验证并信任原始的余弦相似度。表中的每种方法都优于此做法。从适合您查询类型的最廉价方法开始,只有在衡量到真实的释义遗漏时,再向上升级。
这两层方案共同将我们受影响流量的误报率从约 95% 降至 5% 以下。
何时不应使用语义缓存
缓存并非免费的工程,有些工作负载根本不值得缓存。
- 高基数、低重复的流量。如果几乎每个请求都是唯一的(例如一次性的创意生成),命中率太低,无法证明 Embedding 开销的合理性。您支付了 Embedding 的成本却很少获益。
- 必须保持最新的输出。任何对时间敏感、实时数据、个性化结果或包含“今天”字样的内容,即使匹配在技术上是正确的,也可能从缓存中返回过时答案。答案一小时前是正确的,现在可能就错了。
- 严格正确性领域。对于医疗、法律或金融答案,一次误报的代价可能远超其节省的成本。如果在此类领域使用缓存,验证层是强制性的,而非可选的,且 LLM 级别的检查可能是唯一可接受的方案。
- 模型调用本身就很廉价的小型提示词。Embedding、相似度搜索和验证都有其自身成本。如果底层的补全是在廉价模型上进行的几百个 Token,缓存的成本可能比节省的还要多。
缓存适用于重复性高、模板密集、补全昂贵的工作负载,而这恰恰是误报最容易引入的场景。这种矛盾正是验证层至关重要的原因。如果您的目标主要是成本控制,那么将缓存与更廉价的模型路由结合起来也是值得的。价格对比 和 《2026 年最佳 AI 编程模型指南》 涵盖了每 Token 节省的来源,TokenLab 的 模型目录(观察日期:2026-07-07)展示了当前选项,包括 DeepSeek V4 Flash 和 Gemini 3.5 Flash 等低成本路由选择。在确定路由计划前,请核实链接目录中的当前价格。
为什么不直接提高阈值?
我们的阈值已经是 0.95 了。提高它并没有帮助。问题在于,无论实际内容如何,结构相似的输入产生的相似度得分都会超过 0.95。
vCache 的数据支持这一点:正确和错误命中的相似度分布重叠严重,没有单一的截止点能将它们分开。将阈值推至 0.99,您会扼杀合法的释义匹配,而结构相同的请求(如我们的翻译 JSON 负载)无论内容如何,仍会聚集在 0.99 以上。阈值不是杠杆,输入表示才是。这就是为什么第一层(内容提取)和第二层(指纹验证)有效,而提高阈值无效的原因:它们改变了比较的内容,而不是比较的严格程度。
如果您正在构建或维护语义缓存,请将阈值视为粗略过滤器,而非正确性保证。将其与内容提取配对,使 Embedding 真正代表请求的可变部分,然后添加廉价的验证步骤,这样近乎匹配的 Embedding 永远不会在生产环境中悄无声息地变成错误答案。
在连接缓存验证层之前,请先使用 TokenLab 的 模型目录 来比较前沿模型、编程模型和低成本路由模型的当前价格和基准。无论哪个模型位于您的补全端点之后,“提取+指纹”的方法才是真正修复误报的关键。
常见问题解答
提高相似度阈值能修复语义缓存的误报吗? 不能。来自 vCache 和相关研究的数据表明,正确和错误的命中分布在整个阈值范围内都存在重叠,因此提高截止点会阻碍合法的匹配,而无法可靠地过滤掉结构相似但语义不同的请求。
验证语义缓存命中的最廉价方法是什么? 对提取出的有意义内容进行指纹哈希(FNV-1a 或类似算法)计算,延迟不到一毫秒且计算成本为零。它无法捕捉释义,但能消除此处描述的精确误报,而这正是结构化工作负载中大部分问题的来源。
这个问题取决于执行补全的模型吗? 不,误报问题存在于 Embedding 和匹配层,而不是补全模型。如果缓存将固定的模板文本与可变内容一起 Embedding,那么语义缓存背后的任何模型(无论是 DeepSeek V4 Flash、glm-4.6-thinking 还是更新的模型)都会受到同样的影响。在决定通过哪些模型路由缓存管道时,请查看 TokenLab 的 模型目录(观察日期:2026-07-07)获取当前模型可用性。
来源
价格观测于 2026-07-07
- TokenLab model directory观测于 2026-07-07
- Generative Caching for Structurally Similar Prompts and Responses观测于 2026-07-09
- GPTCache paper观测于 2026-07-09
- RedisVL semantic caching docs观测于 2026-07-09
- GPTCache quick start观测于 2026-07-09



