跳到主要内容

检索单一化:为什么你的 RAG 系统存在系统性盲点

· 阅读需 12 分钟
Tian Pan
Software Engineer

你的 RAG 系统评估看起来还不错。NDCG 尚可接受,演示也能运行。但有一类故障是单一指标评估无法捕捉的:那些你的检索器从未接近过的查询——持续如此,因为你的整个嵌入空间从一开始就没有能力处理它们。

这就是检索单一化。一个嵌入模型、一种相似度度量、一条检索路径——因此也是一套系统性盲点,这些盲点看起来像模型错误、幻觉或用户困惑,直到你真正检查检索层才会发现真相。

解决方法不是更大的模型或更多数据,而是理解不同的查询结构需要不同的检索机制,并构建一个能够停止将一切都路由到同一漏斗中的系统。

没人谈论的几何问题

密集嵌入检索的工作原理是将文档和查询投影到共享的高维空间中,然后寻找最近邻。在小型语料库中,这很有效。但随着语料库规模增长,一种叫做"维度诅咒"的数学特性开始发挥作用:在拥有 1536+ 维度的空间中(OpenAI 嵌入的标准),最近邻和最远邻之间的相对距离会坍缩。在有 50,000 个文档时,语义搜索的精度可能比小型索引下降 87%,因为一切都变得几乎等距。

这不是任何特定嵌入模型的失败——这是高维几何的基本特性。但检索系统通常是在小型、精心策划的测试语料库上设计的,这些问题不会在那里出现。等到你到达生产规模时,几何结构已经改变了,而没有人在监控它。

第二个几何问题是训练偏差。神经嵌入模型有系统性盲点:由于训练数据没有充分代表某些实体和文档类型,它们被映射到嵌入空间中无法访问的区域。2024 年的一篇论文("With Argus Eyes")正式将这些定性为语义差距——即文档与查询真正相关但与查询向量的余弦相似度很低的情况。这些差距是一致且可预测的,并非随机的。它们表现为你的系统季复一季都会可靠失败的查询集群。

为什么结构不同的查询需要不同的检索路径

检索单一化的根本原因是将所有查询视为语义等价的,而它们在结构上实际上是不同的。考虑一个嵌入模型实际上被要求处理什么:

事实性查询,如"法国的首都是什么?"依赖精确术语和特定命名实体。嵌入相似度有所帮助,但词法匹配通常表现同样好或更好。

概念性查询,如"解释分布式共识算法的权衡"需要跨越与查询不共享词汇的文档进行语义泛化。这正是密集嵌入大显身手的地方。

导航性查询,如"找到关于退款资格的部分"更多受益于关键词精度和文档元数据,而不是语义相似性。

错误代码和技术查询,如"AWS error 503 service unavailable during spot interruption"需要精确的词元匹配。嵌入模型会对这些字符串进行分词,并经常丢失关键的区分信息——特定的错误代码很重要,而不是其语义邻域。

比较性查询,如"BERT 和 GPT 架构有什么区别?"需要检索从可能使用完全不同词汇的文档中呈现比较双方。

一个嵌入模型能够充分处理其中一些,而在另一些上则会主动失败。当你将所有查询类型都路由到同一路径时,你不是在错过边缘情况——你是在规模上错过整个查询类别。

基准数据证实了这一点:结合 BM25 和密集向量的混合检索召回率达到约 0.91,而仅 BM25 约为 0.72,精度从约 0.68 提高到约 0.87。这 19 个百分点的召回差距不是噪声——它代表着你的纯向量系统将可靠错过的查询。

如何审计你的检索盲点

在增加检索管道的复杂性之前,你需要衡量你的单一化在哪里失败。三种在实践中有效的审计方法:

**检索质量日志记录。**最常见的错误是只监控生成质量(用户评分、幻觉率),而不单独跟踪检索质量。为每个查询添加明确的日志记录,记录检索到的块是否实际包含回答问题所需的信息。"返回了文档"的检索分数与"返回了相关文档"不是同一回事。

**语义覆盖映射。**将你的完整语料库和最近的查询日志嵌入到同一向量空间中。对文档进行语义聚类。然后绘制你的查询相对于这些集群的落点。这揭示了哪些查询集群一致地落在语料库集群之间——你的用户在询问的主题,但你的语料库没有很好地准备好回答,或者即使文档存在,你的检索器也会错过的主题。

**查询类型分解。**从日志中手动将 200-500 个查询分类为类型(事实性、导航性、概念性、技术精确匹配、比较性)。在每个类别上分别运行检索并测量 precision@k。跨查询类型的失败率分布将准确告诉你你的单一化在哪里花费最多。

这次审计的典型发现:在纯向量系统中,错误代码和版本字符串查询的失败率为 60-70%,而高层次概念性查询的失败率仅为 15-20%。每个类别的解决方法是不同的。

打破单一化:三层方法

能够很好地处理多样化查询类型的生产系统并不是在检索策略之间做选择——它们是在分层使用它们。

第一层:混合检索(BM25 + 密集向量)

BM25 使用倒排索引和词元频率统计。密集向量使用语义相似性。它们在相反的查询类型上失败:BM25 在释义和同义词变体上失败,密集向量在精确词元匹配和罕见术语上失败。将它们与倒数排名融合(RRF)结合——RRF 对每个系统的分数进行归一化并合并结果列表——在所有查询类型上始终优于单一方法。

大多数生产向量数据库(Weaviate、Qdrant、Milvus、Elasticsearch)原生支持混合查询。如果你的数据库不支持,并行运行 BM25 和向量查询然后合并结果也很简单。RRF 公式很简单:对于每个文档,计算 1/(rank_bm25 + k) + 1/(rank_vector + k),其中 k 通常为 60。

第二层:查询扩展

在到达检索层之前,生成用户查询的多种表达方式。使用 LLM(用于此目的的廉价快速模型)产生 3-5 种重新表述——同义词、相关概念、不同的具体程度。并行对所有这些运行检索并对结果去重。

这特别有助于解决词汇不匹配问题:询问"如何修复我的 API 超时"的用户可能有关于"连接超时配置"、"请求截止时间超出"和"响应延迟调优"的文档——这些文档都与查询不共享词汇,但都是相关的。扩展弥合了这一差距。

多查询方法在生产设置中对复杂查询显示出 15-25% 的准确率提升。延迟影响是可管理的:在可以并行化的情况下,并行运行扩展查询增加 50-100ms,缓存处理重复模式问题(对反复出现的查询类型延迟减少 70%)。

第三层:交叉编码器重排序

宽泛检索(返回 50-100 个候选)后跟交叉编码器重排序(在一次过中对每个候选相对于完整查询进行评分)现在是标准实践。交叉编码器直接编码(查询、文档)对并产生考虑其交互的相关性分数——远比独立比较它们的嵌入更准确。

基于 SPLATE 和 ColBERT 的重排序器可以在 10ms 内对 50 个文档进行评分,同时显著提高精度。这使你在检索层可以撒一张大网(最大化召回率),而不会在生成层牺牲精度。

监控差距

大多数团队间接监控检索:如果模型给出错误答案,检索一定失败了。这混淆了两种不同的故障模式,使得高效修复任何一种都变得不可能。

检索失败和生成失败从外部看起来是一样的——两者都会产生错误或无用的响应——但需要完全不同的干预。检索失败意味着正确的文档没有被返回。生成失败意味着正确的文档被返回了,但模型无法正确使用它们。

直接对检索质量进行仪器化:

  • 检索覆盖率分数:对于每个查询,top-k 块中有多少比例包含与正确答案的任何词元重叠?这很粗糙,但计算便宜。
  • 查询集群失败率:按查询类型分段跟踪检索质量。聚合指标会将精确匹配查询上 60% 的失败率隐藏在概念性查询的 15% 失败率后面。
  • 语料库覆盖差距:监控落在文档嵌入集群之外的查询比率。上升的差距意味着你的语料库相对于用户意图变得陈旧。

添加了这种仪器化的团队通常会发现,他们的"检索问题"实际上是 3-4 个不同的问题,每个都有针对性的修复。没有这种分段,每次检索改进看起来都像是噪声。

实际实施顺序

在打破检索单一化时,顺序很重要。从高杠杆、低风险的变更开始:

  1. **首先是混合检索。**将纯向量搜索替换为带有 RRF 的 BM25+向量混合。这是一个经过充分理解的变更,大多数数据库原生支持它,并且在不引入回归风险的情况下改善每种查询类型。在做其他事情之前先做这个。

  2. **为已识别的失败集群添加查询扩展。**使用你的审计结果,将扩展针对你失败的特定查询类型。不要扩展所有查询——从召回率最弱的类别开始。

  3. **如果精度是瓶颈,则添加重排序。**如果你的召回率很好(检索返回了正确的文档)但精度很低(太多不相关的结果到达生成),则添加交叉编码器重排序。如果召回率本身很低,先修复检索多样性。

  4. **仪器化覆盖率指标。**一旦你进行了检索更改,你就需要测量来确认改进并捕获回归。在下一轮更改之前添加查询集群日志记录。

何时不要多样化

并非每个 RAG 系统都需要完全多样化。如果你的查询集确实是同质的——一个客户支持系统,其中每个查询都是"我如何用产品 Y 做 X"的变体——一个调优良好的嵌入模型可能就足够了。

检索单一化正在积极伤害你的信号:

  • 有技术/精确匹配查询的用户报告结果比有一般性问题的用户持续更差
  • 你的检索质量审计显示各查询类型的失败率相差超过 20 个百分点
  • 错误日志或用户反馈显示特定主题区域持续失败,无论文档覆盖如何
  • 召回指标在聚合上看起来可以接受,但隐藏着完全失败的长尾

如果这些都不适用,增加检索复杂性是没有回报的开销。目标是将检索机制与查询结构相匹配,而不是最大化系统复杂性。

更大的意义

检索单一化是一个系统设计问题,伪装成模型质量问题。当多样化的查询产生不一致的结果时,本能反应是微调模型、添加更多数据或通过提示工程绕过失败。这些都无法修复结构性检索不匹配。

构建可靠优于单模型设置的 RAG 系统的工程师有一个共同模式:他们将检索视为需要针对不同查询类型使用不同机制的异质组件,而不是一个需要调整的单一旋钮。混合检索、查询扩展和重排序不是在系统工作之后添加的优化。它们是你在设计时做出的结构性选择。

80% 失败的企业 RAG 项目失败的原因不是它们的嵌入模型太小,而是它们构建了一个在一种查询类型上表现出色、而在所有其他查询类型上悄无声息地出错的管道——而且他们从未对检索层进行足够充分的仪器化来发现这一点。

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