跳到主要内容

过时检索:你的 RAG 管道正在隐藏的数据质量问题

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 RAG 系统正在向你撒谎。当用户询问当前价格、有效安全策略或上季度发布的功能时,检索管道返回的是索引中语义最相似的文档——而不是最新鲜的文档。一份 18 个月前的定价页面和今天早上的更新,对余弦相似度来说看起来毫无差异。标准 RAG 架构中没有任何组件能判断所检索的文档是否仍然正确。

这就是过时检索,它的失败方式与幻觉截然不同。模型没有凭空捏造任何内容,它准确地概括了真实存在过的内容。标准评估指标——忠实度、有根据性、上下文精确度——全部通过。系统对一个几个月前就已失效的事实表现出充分的自信。

发现这个问题的团队,通常都是用最惨痛的方式发现的:客服机器人引用了已停产的产品,合规助手引用了已废止的政策,技术聊天机器人满怀自信地描述一个已废弃的 API 方法。等到那时,损失已经造成。

为什么基于 Embedding 的检索在时间维度上是盲目的

这个架构问题的根源很简单。Embedding 模型将文本映射到一个几何空间,含义相近的内容在空间中彼此靠近。这个映射本身并不编码文档的写作时间、是否已从源头删除,或者是否已被更新版本取代。

这制造了一类在离线评估中很容易被忽视的故障:你用一组精心挑选的查询集进行测试,参考文档是最新的,各项指标看起来良好。但检索语料库在生产环境中悄然漂移,而你的评估套件根本没有追踪这种漂移。六个月后,核心问题类型的准确率崩塌了三分之一,而你还在把它当作模型问题去排查。

语料库漂移的速度往往超出团队的预期。对 LangChain 文档语料库的研究追踪发现,它在一年内缩减了 67%——从超过 11,000 份文档降至不到 3,700 份。内容不会干净地消失,它会被移动、拆分、重新组织。你的索引保存着旧的结构,而源头早已面目全非。

标准评估遗漏的三种失败模式

1. 僵尸文档

当一份文档从源系统中被删除——无论是 CMS、S3 存储桶还是数据库表——没有任何机制会通知向量数据库。Embedding 依然存活在索引中,与活跃内容无从区分,在每次相关查询时都可被检索到。

这不只是质量问题。因 GDPR 原因从源头删除的文档,可能在向量数据库中继续存活数月。同一安全手册的多个版本——当前版和已废止版——可能同时存在于索引中,检索系统会返回在任意给定查询中得分最高的那个,没有任何机制能优先选取当前版本;它只是返回 Embedding 空间中最近的那个。

修复方案是构建删除管道,而不只是调用删除 API。现代向量数据库支持按 ID 或元数据过滤进行删除。运营层面的差距在于:当源文档发生变更时,需要有一个能可靠调用删除 API 的事件管道。可选方案包括:针对数据库支持的语料库使用 CDC 连接器(Debezium、基于 Kafka 的方案),针对 CMS 平台使用 Webhook 集成,以及针对更新频率较低的语料库使用定期对账任务。正确的选择取决于源内容的变更速度以及你能接受的过时程度。

2. 无声的版本漂移

你的定价页面更新了。你的政策文档进行了季度修订。你的 API 文档随着一次重大版本升级而重构了。如果你没有将更新同步到向量数据库,那么两个版本都会被索引。检索系统会返回在该查询中得分更高的那个——而没有任何信号告诉你哪个版本才是最新的。

这个问题最险恶的地方在于它的复合效应。第一周,新旧版本的语义相近,新版本通常能胜出。到第三个月,随着额外的漂移,存储的 Embedding 与源内容之间的关系已经发生了足够大的偏移,导致检索在恰恰最需要准确性的那些查询上,表现变得不可预测。

内容哈希是一种可靠的检测模式:在摄取时存储源文档内容的哈希值,然后运行定期对账,重新抓取源文档并比较哈希值。任何不匹配都会触发重新 Embedding 和更新插入。这种方式不如实时变更传播优雅,但对于无法对源系统进行事件埋点的语料库来说同样有效。

3. Embedding 模型版本不匹配

还有一种更隐蔽的变体:你升级了 Embedding 模型。查询的 Embedding 现在使用新模型生成,而索引中的文档 Embedding 仍然使用旧模型。它们处于不同的几何空间,两者之间的余弦相似度不再有意义。

这种失败模式尤为危险,因为它是渐进式降级,其症状看起来像是模型问题。经历过这类故障的团队——从一个 OpenAI Embedding 模型迁移到更新版本——报告说余弦相似度得分从 0.85 以上跌至约 0.65,精确度指标在开发环境中看起来尚可接受,在生产环境中却会失败。排查往往需要数天而非数小时,因为标准架构中没有任何组件会在查询和文档 Embedding 的模型版本不一致时发出警报。

修复方案很直接:在摄取时为每份文档记录 Embedding 模型版本,并在查询路径使用的模型版本与存储的文档版本出现分歧时发出告警。将完整重新索引作为一项一等公民的迁移操作,而不是事后补救。

过时检索与幻觉的区别

这种区别对诊断很重要,而不仅仅是标签上的差异。当模型产生幻觉时,它捏造了训练数据或检索上下文中不存在的内容。当检索过时时,模型准确描述了所检索到的源内容——但那个源已不再是当前状态。

实际影响是:你无法通过改进生成模型或优化提示词来修复过时检索,也无法通过忠实度指标或有根据性检查来捕获它。生成的回答是有根据的;问题在于,这个"根据"本身已经过时。修复它需要在检索管道层面着手,而不是在 LLM 层面。

关于自我纠正检索系统(CRAG)的研究发现,在检索和生成之间加入一个轻量评估器——对检索文档质量进行评分,并在检测到低质量或过时内容时触发新的网络搜索——在不同领域的真实问答任务中,准确率提升了 14–37%。核心洞察是:在检索和生成之间进行干预,比在生成阶段事后弥补更有效。

将新鲜度作为一等元数据属性

最持久的修复方案是在摄取时就将文档新鲜度视为一等模式属性,而不是事后追加。

每份摄取到向量数据库的文档都应携带:

  • indexed_at:Embedding 的创建时间(注意,不是源文档的创建时间——两者是不同的)
  • source_last_modified:来自源系统的修改时间戳
  • ttl_days:该文档有效期的预期天数
  • content_hash:索引时文档内容的哈希值
  • embedding_model_version:生成此 Embedding 所用的模型

ttl_days 的值应因内容类型而异。定价信息可能七天后就过期。API 参考文档可能两周后过期。合规政策可能六个月后过期。归档内容可能没有过期时间。合适的 TTL 是一个产品决策,而非技术决策——但工程师需要将其作为可配置属性暴露出来,而不是埋进部署配置里。

查询时的时间感知评分

一旦新鲜度元数据存在,就可以在查询时应用时间衰减。最简单的形式是对检索分数应用半衰期衰减:

score(query, doc) = α · cosine_similarity + (1 - α) · 0.5^(age_days / half_life)

在 α 约为 0.7、半衰期为 14 天的情况下,研究表明这个公式在时效敏感查询上能实现近乎完美的检索准确率——而纯语义检索的准确率接近于零。时间信号的作用不容忽视;在较低的 α 值下,提升效果尤为显著。

Ragie 等生产系统采用了更简单的分桶方式:根据时间段对文档赋予不同权重(最近一小时得 1.0,最近一天得 0.9,最近一周得 0.8,以此类推),然后在每个桶内计算混合分数。这种方式在数学上不如连续衰减精确,但更易于理解和调优。

对于时效要求最高的领域,预过滤比评分更合适。在数据库层面应用时间窗口 WHERE 子句——在排序发生之前——可以将过时文档完全从候选集中剔除,而不是让它们以较低权重进入排序管道。一份语义相似度排名第一的废弃合规文档,如果在查询时被过滤掉,就永远不会进入结果集。

CI/CD 中的检索回归测试

大多数 RAG 团队针对语料库的静态快照运行离线评估。这样的方式遗漏了语料库漂移所引入的那类故障。检索回归套件应包含一个新鲜度切片:一组精心挑选的时效敏感查询,附带已知的标准答案,在每次部署时针对生产语料库运行。

这需要:

  • 正确答案已知且具有时效性的查询(定价、政策引用、API 版本)
  • 当源头真实情况发生变化时同步更新的预期答案
  • 一旦新鲜度召回率降至阈值以下就阻断构建的自动化比较
  • 追踪随时间推移所检索到的文本块年龄分布的仪表盘

年龄分布指标尤为有用。如果你绘制检索文本块年龄的第 25/50/75 百分位随时间的变化曲线,就能在语料库漂移影响到面向用户的指标之前提前发现问题。检索内容逐渐向更老旧内容偏移,是新鲜度降级的先行指标。

完整的可观测性架构应在每次检索时记录文本块来源——文档 ID、索引时间戳、源修改时间戳、Embedding 模型版本——以便你能在监控系统中查询这些字段,并对"所服务结果中超过 180 天的检索内容占比超过 20%"等条件发出告警。

高风险内容类别

并非所有内容的时效性相同,并非所有的过时都会带来相同的影响。以下是过时检索危害最大的高风险类别:

  • 定价与商业条款:具有法律敏感性;引用过时定价会带来法律风险
  • 安全公告与 CVE 引用:几个月前的公告可能把已修复的漏洞描述为当前存在的问题
  • 合规与监管政策:在受监管行业,过时的政策内容本身可能构成合规违规
  • 在售产品文档:在索引日期之后新增、废弃或重命名的功能
  • 组织信息:人员、组织架构和汇报关系持续变化

对于这些类别,更严格的处理方式是合理的:更频繁的重新索引周期、更短的 TTL、查询时预过滤,以及专项新鲜度监控,而不是依赖聚合指标。

构建管道

生产级新鲜度管道的运营模式包含四个组件:

携带元数据的摄取:在摄取时存储新鲜度字段。将 ttl_days 设为必填字段,而非可选。在摄取 API 边界强制执行。

变更传播:构建事件管道,在源文档发生变更或被删除时通知向量数据库。合适的架构取决于你的源系统——数据库用 CDC,CMS 平台用 Webhook,静态文件存储用定期对账。

查询时评分或过滤:根据文档年龄和内容类型,在查询时应用时间衰减或预过滤。让衰减参数无需代码部署即可配置。

新鲜度监控:在每次检索时记录文本块年龄。对年龄分布偏移发出告警。在 CI/CD 管道中加入新鲜度回归套件。

这些组件都不难构建。失败在于把它们视为可选项,以为检索语料库无需主动维护就能保持准确。事实并非如此。语料库漂移是任何未被明确设计为防止漂移的生产系统的默认状态。

一个不对时间建模的 RAG 系统是不完整的。语义相似度让你接近正确的文档;时间感知告诉你那份文档是否仍在正确地描述这个世界。

References:Let's stay in touch and Follow me for more thoughts and updates