跳到主要内容

针对幻影库存的 RAG:当你的语料库描述产品已删除的功能时

· 阅读需 12 分钟
Tian Pan
Software Engineer

一位客户询问你的支持代理如何执行某项操作。代理检索到了三个相关性评分很高的文档分块,合成了一个自信的答案,并引导客户完成一个五步操作流程。然而,这个流程的最后一步是一个在四个月前就已经不存在的按钮。客户提交了工单。值班工程师调出评估套件,发现结果是绿色的;调出检索追踪,发现结果也是绿色的——模型没有产生幻觉,它忠实地引用了描述产品团队在上个季度发布中重命名的功能的文档。

这就是我想命名的失败模式:不是幻觉,也不是检索未命中,而是幻影库存 (phantom inventory) 问题。你的检索语料库是已不存在的产品界面的快照。向量存储不知道产品发生了变化。评估套件也不知道。唯一能持续捕捉到这一点的系统是支持工单队列,而当工单提交时,客户已经被告知去点击一个并不存在的按钮了。

这个问题隐藏在显而易见的地方,因为每个组件在根据其自身的契约进行衡量时都在正确运行。索引器索引了提供给它的文档。检索器找出了与查询语义最相似的分块。模型严格按照指示,将其回答建立在检索到的上下文之上。评估套件检查了“模型是否忠实地使用了检索到的上下文”,并得到了肯定的答案。每个契约都得到了履行。而那个没人写的契约——“检索到的上下文描述了当前存在的产品界面”——则悄无声息地失效了。

为什么语义相似性不在乎你的产品发生了变化

向量检索没有时间的概念,也没有产品状态的概念。一个 18 个月前编写的、关于你在上个季度删除的功能的文档分块,在今天与相关查询的评分,可能与该功能刚发布时一样高。更糟糕的是,被弃用的文档得分往往比当前的文档更高,因为被弃用的内容是在该功能作为营销重点时编写的,包含了丰富的、与查询对齐的语言(如“分步操作”、“点击此处”、“要启用此功能”),而替代功能的文档较新、较短,且使用了用户自然语言查询无法匹配的不同术语。

来自生产系统的研究表明,这个问题的严重程度令人沮丧:大约 73% 的企业 RAG 部署在第一年内会出现明显的性能下降,知识陈旧被一致认为是首要原因。一份已发表的分析报告追踪了一家银行的合规机器人,该机器人检索了 2022 年的一份《巴塞尔协议 III》文档,并自信地引用了自索引以来已更新过两次的资本充足率门槛——模型没有产生幻觉;语料库是一个时间胶囊。

银行的案例很有启发性,因为这种失败模式可以推广到任何运行产品文档 RAG 的人:语料库是上游状态的派生产物,而上游状态的变化节奏是语料库所有者观察不到的。向量搜索的行为就像一个图书管理员,他没有检查书架上的书是否仍在描述一座现存的建筑。按照规定,图书管理员的工作是提取匹配查询的书籍。契约得到了履行。客户得到的却是对装修团队已经拆除的侧翼的导览。

当功能被废弃时,依然通过的评估

这种失败在生产环境中持续数月的原因是,标准的 RAG 评估维度并不针对现实进行评分。看看大多数评估套件实际检查的内容:

  • 检索相关性:检索器是否找出了与查询语义相关的分块?是的,关于已弃用功能的分块与如何执行该功能所实现的操作高度相关。
  • 忠实度 (Groundedness):模型的回答是否引用了检索到的分块而不是捏造事实?是的,该回答是对已弃用文档的忠实总结。
  • 基于标注集的回答准确性:模型的回答是否与评估测试用例中的预期答案相匹配?是的,因为这些评估用例是在六个月前、该功能还存在时标注的。

当客户点击一个幻影按钮时,团队监控的每个指标都是绿色的。评估套件评分的是模型在检索上下文中的行为,而不是检索上下文的当前有效性。这是不同的问题,而团队只回答了其中之一。

更糟糕的是,评估集本身比语料库更陈旧。在 3 月构建了标注评估集的实践者,不会在 11 月回去询问:“鉴于产品自 3 月以来已经发生了三次变化,此评估集中的预期答案是否仍然正确?”评估用例变成了过时产品界面的冻结快照,团队衡量的是“系统在多大程度上重现了编写评估时正确的答案”,而不是“系统在多大程度上描述了今天存在的产品”。

相比之下,支持工单队列根据现实进行评分的,因为客户正在与实际的产品交互。信息就在那里——它在 Zendesk 中,而不是在评估仪表板中。阻止支持工单反馈到评估套件的组织缝隙,与阻止语料库根据产品路线图进行调整的缝隙是相同的。两者都是不同形式的同一个问题:能够纠正系统的数据存在于一个工具链与运行系统的团队相脱节的团队中。

为什么“更频繁地重新索引”并不能解决问题

工程团队的直觉反应通常是“我们会每晚重新索引”或“我们会监控文档库的提交并在推送时重新索引”。这只解决了一个狭义的问题——嵌入过时(embedding staleness),即文档已更改但索引尚未同步。这对于真正的失效模式(failure mode)毫无作用,因为:

  1. 已弃用的文档往往没有被删除。 它仍然存在于文档网站上,可能位于稍微不同的 URL,或者带有一个写着“此功能已被 X 取代”的小横幅,而分块器(chunker)并没有保留这个横幅。重新索引它并不会让它从语料库中消失;它只是重新索引了相同的过时内容。

  2. 删除是不对称的。 产品团队在一个冲刺(sprint)中移除了该功能。文档团队收到一个更新文档的工单,并将其与另外 20 个工单按优先级排序,于是弃用页面在发布任务之后排队等待。语料库反映的是文档团队的待办列表(backlog),而不是产品团队的发布说明(release notes)。

  3. 分块流水线会剥离弃用横幅。 即使文档团队在页面上添加了“已弃用”横幅,将页面拆分为 500 个 token 窗口的分块器通常会在第 2 到第 8 个分块中丢弃该横幅。检索器召回了第 4 个分块,横幅消失了,模型没有任何信号表明该内容已过时。

  4. 隐式过期是常态。 大多数描述功能的文档不会说“此内容有效期至 2025-08-15”。它们描述功能时就像它会永远存在一样。索引器无法读取任何标量值来得知内容正在腐烂。过期信息存在于另一个系统中——产品路线图、弃用日志、工程 Jira——而语料库并没有订阅这些系统。

一个通过调整重新索引频率来应对“幻影库存(phantom-inventory)”错误的团队,实际上是在用旋钮校准来处理结构性缺陷。重新索引的频率是更严重问题的下游:语料库与关于当前存在哪些功能的真相源(source of truth)脱节了。

组织层面需要做出哪些改变

修复方法主要不是一个检索工程项目。修复的关键在于认识到 RAG 语料库是产品状态的派生伪像(derivative artifact),并像对待任何其他生产数据依赖项一样对待这种依赖——明确所有权、签订合同,并在合同上设置告警。

语料库的所有权属于产品工程,而不是 AI 团队。 掌握“本周有哪些功能”这个答案的团队,也应该掌握“索引中有什么”这个答案。在当今的大多数组织中,文档归技术写作负责,索引归 AI 团队负责,产品路线图归 PM 负责,而它们之间的对齐则无人负责。修复方法是指定一名负责人,其绩效评估中包含这样一个问题:“语料库是否描述了已不存在的功能?”

每次功能弃用都会触发语料库对账步骤。 在弃用工单模板中添加一个复选框:“已从 RAG 语料库中移除,并通过检索探测(retrieval probe)确认”。检索探测是一个保存好的查询,在弃用后不应返回有关已弃用功能的结果——团队运行探测,确认结果为零或仅显示横幅,然后签字确认。这把弃用从一个仅限文档的任务转变为一个文档加索引的任务。

在提示词(prompt)中体现新鲜度,而不只是在索引中。 当一个分块被检索到时,模型应该看到一个新鲜度标签——last_verified: 2026-03-12, freshness_class: fast_decay——并且系统提示词应指示模型在新鲜度低于阈值时表现出不确定性(“我引用的文档可能描述了一个已弃用的功能;请在当前产品中进行验证”)。这不能捕捉到所有情况,但它能将无声的失败转化为客户可以质疑的对冲答案。

增加一个弃用功能评估切片(eval slice)。 维护一份过去 12 个月内产品移除的功能清单,并针对每一个功能运行评估查询:“我该如何执行 X?”其中 X 是已弃用的功能。预期的回答是“X 不再受支持;这是替代方案”。这个切片的通过率是一个被跟踪的指标。当该切片得分回落时,你就知道语料库中包含了本应被移除的内容。

从产品团队而非索引中获取新鲜度。 分块上的新鲜度标签应该源自底层功能最后一次被核实存在于产品中的时间,而不是文档最后一次被编辑或索引最后一次被刷新的时间。这需要一个流水线,使产品团队工具中的弃用事件能够传播到索引中的分块级元数据——这是一个真正的集成,而不是每季度的审计。

架构层面的认知

这种失效模式之所以如此持久,是因为工程团队有一种强烈的直觉,认为搜索索引是一个自成一体的系统:字节入,字节出,按计划刷新。RAG 语料库并不是那样的系统。它是上游产品状态到可索引形式的一种投影(projection),而投影的实时性取决于更新它的事件。如果上游状态通过 Notion 文档中的路线图决策发生了变化,而没有人将该决策接入语料库流水线,投影就会发生偏移。偏移会累积。模型会自信地提供它。客户会跟着它撞墙。

修复它需要两个架构上的心态转变。第一个是将语料库建模为一个具有上游依赖关系的派生数据集,就像你在数据库中建模物化视图(materialized view)一样——并以同样的方式对依赖关系设置告警。第二个是认识到“此功能是否存在?”的真相源存在于产品工程中,而不是 AI 团队中,任何不订阅产品工程弃用事件的流水线都将无法通过这项测试。这两项转变都将工作移到了失效浮现处的上游,这虽然不舒服但很必要。

不进行这些转变的团队将继续交付一个准确率上限受限于他人路线图陈旧程度的功能——他们永远无法通过调整检索栈来提高这个上限,因为检索栈并不是问题所在。而做出这些转变的团队则会获得一个系统,其中“语料库是否为最新”这一问题有明确的负责人、可跟踪的指标和确定性的告警。这是唯一能在产品发布后生存下来的 RAG 版本。

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