大规模语料库策展:为什么你的 RAG 质量上限取决于你的文档质量下限
在大多数 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 过滤正是让嵌入比较变得可行的关键。
一个重要的细节:在摄取时做出的去重决定会一直持续到下一次全量重新索引。如果你删除了一个在摄取时显得冗余的文档,而后来与之冗余的那个文档被删除了,你就永久丢失了覆盖。记录去重决策,并引用导致每次删除的文档,以便在主文档被移除时可以恢复它们。
- https://www.banandre.com/blog/enterprise-rag-implementation-challenges-revealed
- https://glenrhodes.com/data-freshness-rot-as-the-silent-failure-mode-in-production-rag-systems-and-treating-document-shelf-life-as-a-first-class-reliability-concern-3/
- https://medium.com/@reliabledataengineering/rag-systems-have-a-dirty-secret-your-context-window-is-poisoned-7c4a29a3bb32
- https://ragaboutit.com/the-knowledge-decay-problem-how-to-build-rag-systems-that-stay-fresh-at-scale/
- https://dev.to/kuldeep_paul/ten-failure-modes-of-rag-nobody-talks-about-and-how-to-detect-them-systematically-7i4
- https://labelstud.io/blog/seven-ways-your-rag-system-could-be-failing-and-how-to-fix-them/
- https://milvus.io/blog/minhash-lsh-in-milvus-the-secret-weapon-for-fighting-duplicates-in-llm-training-data.md
- https://glenrhodes.com/critique-of-rag-at-scale-the-curse-of-dimensionality-and-why-retrieval-engineering-is-being-skipped/
- https://www.morphik.ai/blog/retrieval-augmented-generation-strategies
- https://arxiv.org/abs/2510.15782
