RAG 系统可以缓存哪些内容?
简化版
RAG 可缓存文档解析/Embedding、查询 Embedding、检索候选、rerank 结果、组装上下文和最终答案,但越靠后越容易受权限、索引版本、Prompt 和模型变化影响。Cache key 必须包含租户/权限指纹、索引快照、模型与 Prompt 版本及关键参数;敏感和高时效内容缩短 TTL 或不缓存,并支持按文档反向失效。
详细版
离线层缓存文档解析和向量,命中高且容易版本化;在线中间层缓存 query 向量与检索结果,要随 embedding/index/filters 失效;最终答案缓存收益最大但风险最高,需保证语义等价、权限一致和证据未更新。语义缓存还要校准相似阈值,否定、数字、实体与时间条件不能被近似合并。
每层记录 hit rate、节省延迟、陈旧命中、错误复用和存储成本。采用 versioned key + tag invalidation:文档变更时通过依赖图清理相关候选、上下文和答案;权限变更优先 fail closed。缓存命中后仍复查当前 ACL,引用绑定原证据版本。
文档解析/向量 -> 查询向量 -> 检索 -> Rerank -> Context -> 最终答案
稳定、易缓存 ---------------------------------> 收益高、失效复杂
完整版教学
一、先按流水线拆缓存层
不同阶段的输入依赖不同。文档 embedding 只依赖内容、切块和模型;检索结果还依赖 query、索引和过滤;最终答案又依赖 Prompt、LLM、上下文和解码参数。把所有东西都放一个通用缓存,会产生难以追踪的陈旧结果。
越靠前缓存复用面广且事实风险低,越靠后节省算力多但 key 和失效复杂。设计应从稳定中间产物开始,再评估是否需要答案级缓存。
二、每层 key 需要什么
文档向量 key 至少含 content_hash、chunker_version、embedding_model;查询向量含规范化 query 和模型;检索含 index_snapshot、top_k、filters、ACL;答案还含 Prompt、LLM、参数和上下文 hash。
answer_key = hash(tenant, permission_fingerprint, normalized_query,
index_version, context_hash, prompt_version,
model_version, decoding_profile)
记忆钩子:缓存 key 要覆盖所有“改变结果的变量”;少一个依赖不是提高命中率,而是在偷偷共享错误结果。
三、权限为何必须进入缓存设计
用户 A 的检索结果可能包含 A 可见文档,不能因查询相同返回给 B。最安全是按租户与权限指纹隔离,并在每次命中后对候选文档重新 ACL 检查。权限指纹应由服务端生成,不能接受用户自报角色。
| 缓存层 | 权限风险 | 建议 |
|---|---|---|
| 公共文档向量 | 低 | 可跨用户复用 |
| 私有检索结果 | 高 | 租户+ACL key,命中复查 |
| 组装上下文 | 很高 | 短 TTL、严格隔离 |
| 最终答案 | 很高 | 权限同构且证据可见 |
权限撤销要触发失效,不能只等 TTL。否则缓存成为撤权后的数据泄露通道。
四、语义缓存为什么容易误命中
语义相似不等于答案可复用。“可退款吗”和“不可退款吗”向量很近,数字、时间和实体变化也可能被 embedding 弱化。语义缓存应先做意图和实体解析,要求关键槽位一致,再以相似度召回并用轻量 verifier 判断等价。
阈值不是越低命中越高越好。用正负 query pair 校准,例如同义改写为正,否定/日期/账号变化为 hard negative。高风险、个性化和实时查询直接绕过语义答案缓存。
五、失效策略怎样建立
TTL 只能处理时间,不知道哪篇文档更新。为缓存项记录依赖的 document/version tags,入库变更时从反向索引找到相关检索、上下文和答案并失效。Embedding 或 Prompt 升级则用版本化 key 自然隔离旧数据,后台再清理。
doc-17更新 -> invalidate tags(doc-17@old)
-> retrieval/context/answer keys
ACL变更 -> 立即吊销 permission fingerprint
大规模 fan-out 失效可异步,但在完成前查询应读新快照或绕过缓存,不能继续服务已知陈旧结果。
六、缓存一致性与新鲜度
价格、库存和政策的容忍度不同。为数据源定义 freshness SLA,答案携带 generated_at 与 evidence_version;读缓存时检查年龄和索引状态。stale-while-revalidate 适合低风险内容,不适合权限和交易事实。
如果索引每 10 分钟更新,答案 TTL 设 24 小时毫无意义。TTL 应不超过最短上游时效,或依靠事件失效。时效性查询还可在 key 中加入时间桶,但这会增加缓存数量。
七、如何判断缓存值得做
命中率不是唯一指标。计算命中节省的延迟/Token、缓存查找与存储成本、陈旧错误率和权限风险。一个命中率 80% 但错误复用 2% 的答案缓存,可能不如命中 40% 且安全的 embedding 缓存。
例如原请求 P95 2秒、成本 0.05元;答案缓存命中耗时 50ms,命中率 30%,平均节省约 0.015元/请求。但若失效基础设施每月成本和风险超过收益,应只缓存中间层。通过 A/B 和故障演练验证,而非理论估算。
八、常见误区与追问
- 误区:查询相同就能共享最终答案。 用户权限、索引、Prompt 和时间都可能不同。
- 误区:给缓存设 TTL 就解决一致性。 文档和权限更新需要事件/标签失效。
- 误区:语义相似度高就是同一个问题。 否定、数字与实体近似会造成危险误命中。
- 追问:最安全先缓存什么? 文档解析、embedding 等版本化离线中间产物。
- 追问:Prompt 发布后缓存怎么办? key 包含 prompt_version,新版不命中旧答案,按策略清理。
- 追问:如何防跨租户泄露? 物理/逻辑命名空间隔离、权限指纹 key、命中后 ACL 复查。
九、加强记忆
RAG 缓存可记成“越前越稳、越后越省也越险”。按流水线分层,key 锁住内容、索引、权限、Prompt 和模型版本;用实体约束保护语义缓存,以文档依赖和 ACL 事件主动失效;最终按净节省与陈旧风险评估。缓存的正确性来自完整依赖,不来自更长 TTL。