跳到主要内容

大规模语料库策展:为什么你的 RAG 质量上限取决于你的文档质量下限

· 阅读需 12 分钟
Tian Pan
Software Engineer

在大多数 RAG 架构中都存在这样一种信念:如果检索返回了正确的区块(chunks),LLM 就会生成正确的答案。团队在嵌入模型选择、混合检索策略和重排序流水线方面投入了巨资。然而,在部署到生产环境三个月后,回答质量悄然下降——这不是因为模型变了,也不是因为查询模式发生了剧变,而是因为底层的语料库腐烂了。

企业级 RAG 的实施失败率约为 40%,而从业者最容易低估的失败模式既不是幻觉,也不是检索召回率低,而是文档质量。一项分析发现,通过引入文档质量评分,一个实施方案在不改变嵌入模型或检索算法的情况下,将搜索准确率从 62% 提高到了 89%。语料库是唯一的变量。语料库一直都是变量。

“垃圾进”的问题比听起来更微妙

“垃圾进,垃圾出”意味着糟糕的文档会产生明显错误的答案。而在实践中,失败模式更为隐蔽。糟糕的文档会产生表现得很有把握的错误答案,而这些答案甚至能通过基础的检索指标。

考虑一下嵌入相似度的工作原理:一份 18 个月前的文档,如果涵盖了与当前用户查询相同的主题,其在余弦距离上的得分与当前的文档几乎完全相同。向量空间没有时间、准确性或权威性的概念。一个过时的 API 引用和它的替代版本,与查询“我该如何使用此服务进行身份验证”之间的距离本质上是相同的。检索器没有依据来优先选择其中之一。

这同样适用于作者身份。一份编写良好的内部文档和一份带有两个冲突政策修改的陈旧副本会产生类似的嵌入。检索会同时将两者呈现出来。LLM 尝试进行综合处理,最终产生一个折中的答案,其错误方式很难归因。

这是“垃圾进”原则更精准的版本:低质量文档不会按比例产生垃圾输出;它们会引入系统性的、低方差的错误,这些错误看起来像是正确的行为,直到有人拿答案与事实真值(ground truth)进行比对。

为检索适用性进行文档评分

在文档进入你的索引之前,应该从多个维度对其进行评估。并非所有这些维度都同样容易处理,但完全跳过它们正是团队最终得到“中毒”语料库的原因。

信息密度是单位文本中语义清晰的断言比例。一份包含分步说明的 2000 字程序运行手册得分很高;而一份 500 字的 FAQ,其中 400 字是介绍性的废话,则得分很低。稀疏的文档会浪费上下文窗口空间,并稀释检索区块的信噪比。你可以通过压缩比来近似评估这一点——高信息密度的文本比重复的废话压缩率更低。

**分块敌对性(Chunking hostility)**描述了文档结构在分块过程中的保存程度。PDF 是典型的例子:PyPDF 根据字符的存储顺序而非阅读顺序解析文档,这会为任何多列或包含大量表格的文档产生混乱的输出。当一个表格在行中间被切分时,生成的区块包含一个没有任何可解释含义的碎片。带有 OCR 错误的扫描版 PDF 会使问题复杂化——20% 或更高的 OCR 错误率仍然很常见,当这些错误进入分块流水线时,它们不会均匀分布,而是聚集在边界处,破坏了与检索最相关的文本。

作者身份模糊性很重要,因为 LLM 无法权衡它不知道存在的来源。如果你的语料库混合了权威的一手来源和对这些来源进行轻微编辑的二手摘要,检索器有时会优先返回摘要而不是原文。摘要可能包含微妙的重述或错误。在摄取时跟踪来源(provenance)——原始来源 URL、最后验证日期、作者或团队——让你能够在检索时应用权威性权重。

跨文档冲突是摄取时最难评分的问题,因为它需要将一份文档与索引中已有的其他文档进行比较。一个实用的启发式方法是:在重新索引更新的文档时,对语义最相似的前 5 份现有文档运行基于 NLI 的冲突检测。标记出蕴含分数低于阈值的配对。这能捕获到最危险的情况——即一个更新与其所替换的文档相矛盾,而两个版本都保留在索引中。

保持语义覆盖的去重

朴素的去重本能是删除近乎重复的内容。对于同一信息存在于多种文档类型中的语料库来说,这是错误的。产品规格书、源自该规格书的支持文档以及面向客户的 FAQ 可能说的几乎是同一件事,但它们服务于不同的查询意图。如果删除到只剩一个,就会导致那些与被删除文档中特定表述相匹配的查询失去覆盖。

正确的框架是保持覆盖的去重:删除相对于现有索引内容增加零边际覆盖的文档,同时保留那些以不同词汇、框架或详细程度涵盖同一主题的文档。

在大规模场景下,这需要近似方法。MinHash LSH 是检测数千万文档近乎重复的标准方法——它在不计算成对比对距离的情况下估计 Jaccard 相似度,从而降低了精确比对的二次方成本。对于严格去重,实际阈值约在 0.85 Jaccard 相似度;低于此值,文档通常差异大到足以证明保留两者的合理性。

对于语义去重(同义转述和重写,而非近乎复制),稠密嵌入(dense embedding)比较更准确,但计算成本昂贵。一种常见的生产模式是将 MinHash LSH 作为第一轮过滤,然后仅在 MinHash 识别出的候选对中应用基于嵌入的比较。在语料库大小超过 1000 万份文档时,朴素的成对嵌入比较是不可行的——MinHash 过滤正是让嵌入比较变得可行的关键。

一个重要的细节:在摄取时做出的去重决定会一直持续到下一次全量重新索引。如果你删除了一个在摄取时显得冗余的文档,而后来与之冗余的那个文档被删除了,你就永久丢失了覆盖。记录去重决策,并引用导致每次删除的文档,以便在主文档被移除时可以恢复它们。

质量加权索引

大多数向量数据库都支持元数据过滤,但团队很少将其扩展到检索时的质量加权。一种始终优于纯相似度排名的模式是,在检索时将语义相似度得分与质量权重结合起来。

一个简单的版本:final_score = (semantic_similarity × 0.7) + (freshness_score × 0.3)。对于在其新鲜度类别窗口内更新的文档,新鲜度得分为 1.0;随着时间超过该窗口,得分线性衰减至 0。这不需要改变你的嵌入模型或检索架构 —— 这是一个在将分块传递给 LLM 之前应用的检索后重排序步骤。

文档的新鲜度类别需要在摄入(ingest)时根据内容类型进行分配。API 参考文档在几周内就会失效;架构设计文档会在几个月内失效;而基础概念可能在数年内保持有效。统一应用单一的新鲜度窗口,要么会丢弃仍然有效的旧内容,要么会保留极其陈旧的新内容。分类应尽可能粗略,以保持可维护性:涵盖大多数文档类型的三到五个类别就足够了。

质量加权还可以结合作者权威度、来源权威性(这是主要来源还是派生的摘要?)以及检索历史(被持续检索但在最终答案中被 LLM 忽略的文档可能需要较低的权重)。最后一种信号仅在部署后可用,但对检索浪费具有高度的预测性。

团队在出问题前不会构建的语料库清理工作流

大多数团队发现自己需要语料库清理工作流,是因为在他们一直监控的问题上检索质量下降了,且无法识别出任何可以解释这一现象的模型或基础设施变化。到那时,语料库已经累积了数周或数月的陈旧内容、重复和隐性冲突。

防止这种情况的运营模型将语料库健康视为可靠性问题,而非清理任务。具体而言,它由以下部分组成:

  • 摄入时的质量门禁 (Ingest-time quality gates):最小信息密度阈值、分块适用性检查、必需的元数据字段。未通过质量门禁的文档会进入审核队列,而不是索引。
  • 按类别的定期陈旧度审计:快速衰减类别的文档(发布说明、变更日志、支持文章)每两到四周获得一次重新验证触发。慢速衰减类别的文档每六个月触发一次。无法重新验证的文档在其元数据中被标记为未验证,该标签用于在检索时应用新鲜度惩罚。
  • 留存评估集的漂移检测:每月针对实时语料库运行一组固定的代表性查询。跟踪答案质量得分随时间的变化。与模型更改无关的下降就是语料库信号。
  • 更新时的冲突监测:当任何文档被更新或添加时,将其与语义相似的现有文档进行对比以寻找冲突。标记但不自动删除 —— 被标记的文档对会进入人工审核队列。

所有权问题与技术架构同样重要。数据新鲜度的腐烂主要不是技术失败 —— 而是所有权失败。如果没有为每个文档类别指定明确的所有者,新鲜度监控就会变成无人负责的工作,而卫生状况也会退化为周期性的紧急清理循环,而不是持续的稳定状态。

指标到底告诉你了什么

团队通常跟踪的指标 —— 检索 Precision@K、NDCG、答案相关性 —— 对于诊断语料库质量问题是必要但不充分的。它们会告诉你检索器是否返回了相关内容,但不会告诉你相关内容是否准确。

补充标准检索指标的有用的语料库健康指标:

  • 陈旧率 (Staleness ratio):超过新鲜度类别阈值的索引文档百分比。警报阈值为 15%,严重阈值为 30%。
  • 冲突对计数 (Contradiction pair count):过去 30 天内被标记为冲突内容的文档对数量。
  • 质量门禁拒绝率:尝试摄入的文档中未通过质量检查的百分比。拒绝率激增是上游信号(源头开始生成较低质量的内容),而不不仅仅是摄入失败。
  • 单条查询的答案漂移:长期跟踪一个固定的评估集。在模型未更改的情况下,时间敏感问题的答案质量下降 5 分,即表明语料库陈旧。

一个值得记住的数据点:管理 1,000 个文档的语料库可以以极低的运营开销维持亚小时级的新鲜度,但在没有明确新鲜度管理的情况下,同样的架构在 100,000 个文档时默认会以 12 小时的陈旧度运行。语料库卫生的债务随语料库规模呈超线性(superlinear)增长。

文档始终是底线

RAG 的改进往往遵循一个可预测的路线图:更好的嵌入模型、混合检索、交叉编码器(cross-encoder)重排序以及越来越复杂的查询变换。每一步都会产生可衡量的收益。但每一步都受限于同一个底层约束:如果语料库包含陈旧、矛盾或结构上不可用的文档,每一次检索改进都会更有效地暴露出这些文档。

检索系统的任务是找到可用的最佳文档。它无法将坏文档变好。RAG 系统的质量上限在摄入时就已设定,取决于你接受进入索引的文档以及你附加给它们的元数据。构建维护该上限的运营工作流 —— 质量门禁、新鲜度跟踪、冲突监测 —— 并不是光鲜亮丽的基础设施工作。但正是这项工作决定了检索的改进是转化为准确性的提升,还是仅仅更快地找出了同样的烂文档。

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