核心摘要

LLM 语义缓存会为语义相近的请求复用完整答案。只判断相似度并不安全:有效命中还必须满足经过校准的阈值,并确保租户、授权范围、语言、模型、提示词、来源版本、安全策略和新鲜度窗口一致。误命中率应按请求意图和风险类别分别统计;缓存结果不能作为权威数据源,还必须支持主动失效、跳过缓存和运行监控。

区分语义缓存、精确缓存、提示词缓存与 KV Cache

“缓存”这个词对应多种不同机制。

缓存类型 复用对象 匹配方式 是否执行生成
精确响应缓存 完整回答 规范化请求完全一致
语义响应缓存 完整回答 向量相似度 + 过滤
厂商提示词缓存 模型前缀计算 重复 Token 前缀
KV Cache Attention K/V 状态 当前请求或受支持的跨请求 Token
RAG 检索 来源片段 查询相关性

语义缓存更接近“近似响应缓存”。即使都使用向量搜索和嵌入,它也不是 RAG 使用的向量数据库:RAG 检索证据后生成新答案,语义缓存直接返回历史答案。

生产环境命中路径

flowchart LR A["请求"] --> B["规范化与意图分类"] B --> C["租户和策略过滤"] C --> D["生成请求向量"] D --> E["最近缓存候选"] E --> F{"距离与新鲜度通过?"} F -->|是| G["返回已验证缓存答案"] F -->|否| H["执行检索与 LLM"] H --> I["验证回答"] I --> J["写入版本化缓存"]

一次有效查询至少应包含:

json
{
  "tenant": "tenant_42",
  "auth_scope": "support:public",
  "locale": "zh-CN",
  "model_version": "model-family/revision",
  "prompt_version": "support-answer/v12",
  "source_revision": "kb-2026-07-28",
  "safety_policy": "policy-v8",
  "response_type": "public_faq"
}

尽量在向量查询内部执行这些过滤。先查全局最近邻,再在应用层检查租户,会制造不必要的数据泄露和时序风险。

阈值本质上是风险决策

阈值宽松会提高命中率和误匹配,阈值严格会减少错误,却可能让缓存没有经济价值。正确阈值取决于嵌入模型、距离度量、规范化方式、请求分布,以及错误回答的危害。

建立标注请求对:

请求对 预期
“如何退货?”/“退货流程是什么?” 匹配
“最终销售商品能退吗?”/“如何退货?” 通常不匹配
“重置我的密码”/“重置另一个用户的密码” 不匹配
不同租户的相同问题 不能跨分区匹配

扫描阈值并报告:

  • 可缓存请求占比;
  • 命中率;
  • 误命中率;
  • 有害误命中率;
  • 陈旧命中率;
  • p50/p95 查询与总延迟;
  • 每个成功答案的成本。

必须按意图和风险切片分析。整体误命中率 1% 看似可控,但如果集中在账户、医疗或支付问题,就不可接受。

键设计与失效

缓存键不只是向量。任何会改变答案含义的输入都要版本化。

typescript
type CachePartition = {
  tenant: string;
  authScope: string;
  locale: string;
  modelVersion: string;
  promptVersion: string;
  sourceRevision: string;
  policyVersion: string;
};

function canReuse(
  candidate: CachePartition,
  request: CachePartition,
): boolean {
  return Object.keys(request).every(
    (key) =>
      candidate[key as keyof CachePartition] ===
      request[key as keyof CachePartition],
  );
}

TTL 用于时间新鲜度,但不能单独承担失效。以下变化应触发失效或版本轮换:

  • 来源文档更新或删除;
  • 权限或租户归属改变;
  • 提示词、模型或嵌入模型变更;
  • 安全策略改变;
  • 回答被纠正或报告有害;
  • 事故使某类答案全部失效。

优先使用版本化命名空间,不要扫描并修改所有条目。旧条目自然过期,新请求只进入当前版本。

缓存条目应包含什么

保存足以判断可复用性的证据,同时最小化敏感内容。

json
{
  "request_embedding": "<vector>",
  "normalized_request_hash": "sha256:...",
  "response": "<长度受限且已验证的回答>",
  "source_ids": ["policy-v7#returns"],
  "partition": { "tenant": "public", "locale": "zh-CN" },
  "versions": { "prompt": "v12", "model": "rev-8", "policy": "v8" },
  "created_at": "2026-07-28T10:00:00Z",
  "ttl_seconds": 86400,
  "validation": { "status": "passed", "suite": "faq-v4" }
}

不要缓存秘密、原始凭据、一次性 Token、隐藏授权决策或私有模型上下文。静态加密不能让跨租户复用变得安全。

缓存准入策略

不是每个成功响应都应该写入缓存。只有满足显式准入策略的结果才能进入:

  • 请求被分类为可缓存;
  • 不含个性化或秘密字段;
  • 确定性策略与安全检查通过;
  • 回答包含所需引用或证据;
  • 来源修订与过期时间已知;
  • 输出完整,不是拒答或截断;
  • 响应类型适合重复播放。

准入策略可以避免一次低质量回答或攻击结果被持续放大。

主要失败模式

失败 影响 控制
语义误命中 为错误意图返回合理外观答案 标注阈值评估与意图路由
跨租户命中 数据泄露 查询前分区与可信身份
陈旧命中 返回过时政策或事实 来源版本、TTL、事件失效
缓存投毒 恶意答案反复被服务 准入校验与来源
模型或提示词漂移 行为与当前版本不一致 版本化命名空间
热门答案锁定 旧回答抑制新版本改进 抽样绕过与定期刷新
缓存故障 延迟突增或请求失败 将缓存设为非必要依赖,并设置较短超时

缓存必须可安全绕过和重建,不能成为唯一事实系统。

评估与渐进发布

按阶段上线:

  1. 影子模式: 查询缓存但不返回,收集候选请求对。
  2. 只读灰度: 只开放已审查公开意图与严格阈值。
  3. 有限生产: 为选定租户或路由启用,并提供即时熔断。
  4. 阈值调优: 审查误命中切片后再扩大范围。
  5. 持续审计: 抽样绕过命中,与新生成结果比较以发现漂移。

每次命中记录条目 ID、分区摘要、距离、阈值、条目年龄、来源修订、校验版本、绕过原因和结果。当摘要与意图分类足够时,不要记录原始提示词。

何时适合使用语义缓存

它适用于高频、稳定、重复、低个性化的问题,且完整回答可以安全复用。FAQ 助手、公开文档和边界明确的分类是常见候选。

以下场景应避免或严格限制:

  • 账户专属答案;
  • 授权与安全决策;
  • 快速变化的库存、价格和状态;
  • 法律、金融或医疗结论;
  • 写操作与副作用;
  • 对话上下文会改变问题含义的请求。

LLM Gateway 架构指南说明缓存策略在路由和预算中的位置;AI 推理成本指南介绍如何衡量收益而不掩盖质量回归。

常见问题

应该使用完整对话计算语义相似度吗?

通常不能直接使用。对话历史可能包含身份、偏好、已有决策和指令,都会改变含义;只取最后一句也可能错误。应定义任务专用的规范表示。

一个缓存能同时服务多个嵌入模型吗?

不能把不兼容向量空间的结果混在同一索引。应版本化嵌入模型,并在迁移期间重建或双读。

命中后应该刷新 TTL 吗?

取决于新鲜度语义。滑动 TTL 可能让热门旧答案永不过期。政策和知识内容更适合绑定来源修订的绝对过期时间。

流式响应如何使用缓存?

只能缓存完整且通过验证的响应。取消或超时后的部分流不能准入;流式与非流式响应格式可能需要不同条目。

如何防止缓存投毒?

准入前执行与直接响应相同的证据、策略、安全和完整性检查,记录来源,并支持按条目、来源、版本和策略即时失效。

参考资料