跳到主要内容

检索债务:为何你的 RAG 流水线会悄然退化

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的 RAG 流水线上线六个月后,某些东西悄然改变了。用户没有大声投诉,但对答案的信任度正在下降。反馈评分从 4.2 跌至 3.7,一些支持工单提到了"过时信息"。你的工程师检查日志,没有错误、没有超时、没有明显的回归。检索流水线在你配置的每一个指标上看起来都很健康。

但事实并非如此。它正在腐烂。

检索债务是向量索引中积累的技术性衰退:不再代表当前文档内容的过期嵌入、污染搜索结果的已删除记录产生的墓碑块,以及索引语料库时使用的编码器版本与当前计算查询嵌入的编码器版本之间的语义漂移。与代码腐烂不同,检索债务不会产生堆栈跟踪,它产生的是带有自信引用的微妙错误答案。

60% 的企业 RAG 项目失败,不是因为幻觉或检索逻辑缺陷,而是因为团队无法在规模化场景下维护数据新鲜度。流水线在上线时运作正常,在团队专注于其他功能时悄然退化。

检索债务的三种积累方式

理解不同的机制有助于你针对性地处理每一种问题。它们往往同时出现,这使得诊断更加困难。

文档变更导致的嵌入过期。 语料库中的每份文档都在特定时间点被嵌入,捕获的是当时的语义内容。当文档被编辑——定价页面更新、API 规范修订、政策文件修改——文本发生了变化,但向量却没有,除非你的摄取流水线明确处理了更新。结果是文档实际内容与索引认为其内容之间的差距不断扩大。本应检索到更新版本的查询,反而检索到代表旧内容的向量。LLM 生成的答案自洽且内部一致,只是错了。

已删除内容产生的墓碑块。 向量存储不像文件系统那样物理删除已删除的文档。基于 Postgres 的存储(如 pgvector)将行标记为死行并依赖 VACUUM 来回收它们;专用向量数据库通常用墓碑记录标记删除,在查询时过滤结果。问题在于,在写入密集的工作负载下,墓碑积累速度超过了清理速度。已删除的块仍然是近似最近邻搜索的候选项,有时会穿过过滤器——尤其是当墓碑表在 ANN 阶段之后而不是之前被查询时。即使一个来自已废弃文档的被检索块也会污染发送给 LLM 的上下文窗口。

编码器版本变更引发的语义漂移。 这是最危险的检索债务形式。嵌入模型并非静态不变。提供商发布更新版本,团队在领域数据上微调基础模型,即使是相同的模型权重,如果分词器或预处理流水线发生变化,也可能产生不同的嵌入。当你为新文档更新嵌入模型但不重新嵌入现有语料库时,你创建了一个混合版本索引:单一向量空间中包含两个截然不同的几何世界。使用新模型编码的查询在两个世界中搜索,但只能在新模型子空间中找到可靠的邻居。旧模型的块会向分布边缘漂移,检索不稳定甚至无法检索。

对跨模型版本的相同文本嵌入之间的余弦距离研究表明,稳定系统的距离范围为 0.0001–0.005。当编码器版本出现分歧时,相同文本对的距离可达 0.05–0.10 或更高——增加了 10–100 倍,使得两种表示在最近邻意义上几乎不相关。邻居持久性(衡量文档 top-k 邻居在流水线变更后保留的比例)从稳定系统的 85–95% 下降到活跃漂移系统的 25–40%。

检索债务在实践中的表现

诊断的挑战在于检索债务会伪装成其他问题。一个追查幻觉的团队可能花数周调整生成提示,而真正的问题是索引正在呈现过时的上下文。以下是应引起怀疑的模式。

稳定查询的相关性持续下降。 如果你有一个固定的评估集——带有已知正确答案的标准问题——并且每周追踪检索指标,你会看到检索债务表现为缓慢的下降趋势。三个月内 Precision@5 从 0.82 降至 0.74 并不是剧烈的回归,但它是真实且累积的。没有维护评估集的团队没有检测到这一点的基准线。

检索文档年龄的分布偏移。 如果你为每个块打上摄取时间戳标签,就可以监控检索上下文的平均年龄。一个服务于频繁更新知识库的健康流水线,在回答查询时应检索到最新内容。如果检索文档的平均年龄逐月增加,你的索引新鲜度正在落后于文档更新速度。

语义相似查询检索结果不一致。 一种表述方式的查询返回高度相关的结果;同义的另一种表述却返回关联性弱的块。这种不一致性是混合版本索引的特征。新模型查询能可靠地找到新模型邻居;如果它们恰好落在旧模型聚类附近,检索就会变得不可预测。

来自已删除内容域的虚假检索。 如果你下线了一个产品、停止了一项服务或归档了旧政策,而用户仍然收到引用这些内容的答案,那是墓碑块穿过了你的过滤器。

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