跳到主要内容

当 Embedding 不够用时:混合检索架构的决策框架

· 阅读需 14 分钟
Tian Pan
Software Engineer

大多数 RAG 实现都以同样的方式开始:启动一个向量数据库,使用一个不错的模型嵌入文档,在查询时运行余弦相似度,然后发布。演示效果看起来很棒。相关性感觉出奇地好。然后你将其部署到生产环境,发现“Error 221”检索到了关于“Error 222”的文档,搜索特定的产品 SKU 会出现语义相似但错误的条目,而添加日期过滤器会导致检索质量大幅下降。

向量搜索是一个真正强大的工具。但在大多数生产环境的检索工作负载中,仅靠它是不够的。在 2025 年,通过 RAG 获胜的团队并不会在稠密嵌入(dense embeddings)和关键词搜索之间做选择——他们会刻意同时使用两者。

这是一个决策框架,用于判断混合检索何时值得增加复杂性,以及如何在不破坏延迟预算的情况下构建每一层。

纯向量搜索失效的场景

稠密嵌入将文档压缩为高维向量,语义相似的文本会落在附近。这正是你希望在用户查询“我们的退款政策是什么”而文档显示“我们接受 30 天内退货”时发生的情况。语义桥梁解决了词汇不匹配的问题。

它在以下三类情况下的失败是可以预见的:

精确匹配查找。 错误代码、产品 ID、API 方法名称、序列号——这些都需要精确或近乎精确的文本匹配。嵌入对所有维度取平均值意味着“Error 221”和“Error 222”在向量空间中非常接近,因为它们都是同一系统中的错误。如果检索系统在用户报告 Error 221 时显示 Error 222 的缓解步骤,那它比没用更糟糕;它在误导用户。

罕见词汇和专业术语。 在训练语料库中出现频率较低的词汇,其嵌入是不稳定的。在一个混合文档集中搜索“HNSW”,可能会检索到关于近似最近邻搜索的通用文章,而不是使用该确切术语的特定文档。技术文档、医疗记录和法律文本中充满了这类术语。

布尔和结构化查询。 向量搜索没有“关于主题 A 且具有属性 B 的文档”这种原生概念。通过元数据(日期范围、作者、类别、产品线)进行过滤,要么作为后处理步骤发生(降低召回率),要么被集成到 ANN 遍历中,但这需要仔细实现,以避免检索质量彻底崩溃。

向量搜索和 BM25 之间的差距在于它们以正交的方式失败。BM25 漏掉的(语义衔接、同义词匹配),稠密嵌入可以捕捉。嵌入漏掉的(精确术语、罕见词汇、结构化约束),BM25 可以很好地处理。这种正交性是混合检索的核心论点。

BM25:为什么这个拥有 30 年历史的算法依然重要

BM25 (Best Match 25) 是一种概率排名函数,它根据词频(查询词出现的频率)和逆文档频率(这些词在整个语料库中的罕见程度)为文档评分。它运行在倒排索引上,在通用硬件上即可处理数十亿文档,并在不需要 GPU 的情况下以个位数毫秒级的速度返回结果。

在基准测试中,BM25 在 BEIR 基准测试(18 个信息检索数据集的平均值)上实现了 43.4 的 NDCG@10。稠密检索模型通常在通用语义查询上表现更好,但在技术和特定领域的语料库(API 文档、法律文件、医疗记录)中,BM25 往往能赶上甚至超过稠密检索器,因为精确的词汇比语义相似性更具预测性。

BM25 的更新也非常简单。添加文档,更新倒排索引。无需重新嵌入,无需重建向量索引,不需要 GPU。对于更新频率高的语料库,这一点至关重要:正如一个生产团队所说,在添加或更改文档时保持向量索引的新鲜度是一个“巨大且众所周知的难题”。增量 ANN 索引更新非常复杂,以至于许多团队干脆不更新,在过时的嵌入上运行数周而不知情。

结合 BM25 和稠密检索

标准方法是独立运行两个检索器,从每个检索器中获取前 k 个结果,然后将它们合并。合并策略的重要性比大多数团队意识到的要大。

互惠排名融合 (Reciprocal Rank Fusion, RRF) 已成为主流的组合方法。RRF 不是组合原始相似度分数(不同检索器的分数级数通常不兼容),而是组合排名位置。每个文档从每个检索器获得 1 / (rank + k) 的分数,其中 k 是一个平滑常数(通常为 60),防止排名靠前的项目占据主导地位。出现在两个排名列表中的文档会同时获得两个分数,这使得它们在合并结果中升至顶部。

RRF 的核心优势在于它与分数无关。你不需要归一化或对齐不同检索器的评分函数,这意味着你可以组合任意检索系统而无需额外校准。经过多个团队的大量测试,RRF 的表现始终优于更复杂的加权方案。它的简洁性降低了过拟合风险,并使其在不同查询分布中保持稳健。

加权分数组合(归一化原始分数并使用调整后的权重进行混合)是一种替代方案,但它需要特定领域的权重校准,并且对分布偏移很敏感。对于大多数团队来说,RRF 是正确的默认选择。

在比较精确匹配和语义查询的混合查询基准测试中,使用 RRF 的混合检索实现了 0.85 的 NDCG,而任何单一检索器的性能都较低。IBM 研究院发现,在 BM25 + 稠密组合中添加稀疏向量(通过 SPLADE,一种学习型稀疏扩展模型)会产生另一个有意义的提升——IBM 报告称这种三路混合是最佳的。Pinecone 的基准测试数据表明,三路混合检索比单一方法检索的检索质量提高了约 48%。

成本是运行两条或三条检索路径带来的额外检索延迟。对于大多数生产系统来说,这是值得的。与 LLM 调用所花费的时间相比,并行运行 BM25 和稠密检索带来的延迟增加很小,而且召回率的提高通常允许你传递更少的文档给 LLM——从而降低下游的 Token 成本。

元数据过滤:阿喀琉斯之踵

元数据过滤是生产环境检索中最令人头疼的地方。用户可能想要“过去 6 个月内关于产品 X 的技术文档”。向量搜索可以找到相关内容,但它没有强制执行日期范围或精确类别匹配的原生机制。

目前有三种过滤策略,各有利弊:

后置过滤 (Post-filtering) 先运行 ANN 搜索,然后丢弃不符合过滤条件的搜索结果。这种方法简单,适用于过滤条件较宽松、大部分结果都能通过的情况。当过滤条件非常苛刻时——例如日期范围仅覆盖语料库的 2%——后置过滤需要海量的过采样(oversampling)来确保有足够的结果留存,这既增加了延迟又增加了成本。

前置过滤 (Pre-filtering) 先应用元数据过滤器生成候选集,然后在该集合内运行向量搜索。这保证了准确性——你只搜索符合条件的文档——但当过滤条件极具选择性且 ANN 索引无法高效遍历小子集时,性能可能会退化为近线性搜索。

算法内过滤 (In-algorithm filtering) 将过滤器集成到 ANN 遍历过程中,在图遍历期间跳过被过滤掉的节点。如果实现得当,这是最有效的方法,但存在一种失效模式:随着过滤比例接近 1.0(几乎所有文档都被排除),召回率会崩溃,因为没有足够的候选节点可供遍历。

性能开销数据令人警醒:类别过滤会增加约 3 倍的延迟,数值范围过滤则会增加高达 8 倍。最佳的生产方案是构建元数据感知子图(metadata-aware subgraphs)——围绕常见过滤值组织的独立 ANN 索引——因此,带有 category = "support-docs" 的查询会使用由支持文档构建的子图,而不是遍历整个语料库。生产环境中的向量数据库(Weaviate、Milvus、Qdrant 均支持变体)提供了这种功能,但需要预先进行模式(schema)设计,并增加了运维复杂性。

实践原则:如果你的过滤条件较宽松(过滤掉的文档少于 50%),后置过滤最简单。如果过滤条件很苛刻且你的向量数据库支持,那么使用元数据感知索引的算法内过滤值得一试。

重排序器 (Rerankers):何时承担延迟成本

BM25 + 稠密混合检索提高了召回率——你会捕获更多相关的文档。但仅靠召回率并不能决定生成质量。精确度(Precision)至关重要:将无关文档放入发送给 LLM 的前 5 个上下文窗口中,会通过引入噪声主动降低回答质量。

交叉编码器重排序器(Cross-encoder rerankers)解决了精确度问题。稠密检索器使用双编码器(bi-encoder)架构——查询和文档分别编码,通过点积计算相似度;而交叉编码器则在单个 Transformer 前向传播中同时处理查询和候选文档。每个查询 token 都会关注每个文档 token,从而实现双编码器无法做到的细粒度语义比较。

标准的生产流水线如下:

  1. 双编码器检索:约 5ms 内获取前 100 个候选结果
  2. 交叉编码器重排序:约 50ms 内对前 100 个结果重新评分
  3. 将前 5 或前 10 个结果输入 LLM 上下文

延迟开销是真实存在的:对 100 个候选结果进行重排序会给总查询时间增加 100–200ms。除非你只对极少量的候选集(20–30 个文档)进行重排序,否则这使得交叉编码器重排序不适用于低于 100ms 的延迟要求。在 MS MARCO 基准测试中,交叉编码器重排序比纯检索方法提升了高达 10 个 NDCG 点数,这在生产环境中能显著转化为生成质量的提升。

增加重排序的决定是一个伪装成技术决策的业务决策。如果你的应用领域需要高精确度——法律研究、医学文献、金融合规——那么延迟成本是值得承担的。如果你正在构建一个通用聊天机器人,其中模糊的相关性即可接受,那么增加的复杂性可能并不划算。

一种正在兴起的替代方案是 ColBERT 等延迟交互模型(late interaction models),它们保留每个 token 的表示,而不是将文档压缩成单个向量。ColBERT 在检索时实现了交叉编码器的大部分语义精确度,且没有针对每个候选结果的逐次查询编码成本。权衡之处在于存储:多向量表示比单向量嵌入需要大得多的空间。

决策框架

考虑到每一层的复杂性,这里有一个关于何时添加各组件的务实框架:

在以下情况下,请从 BM25 + 稠密混合检索开始:

  • 你的查询包含精确术语、产品 ID、错误代码或专业词汇
  • 你在技术或特定领域的语料库(法律、医学、代码)中操作
  • 你的文档更新频繁,且向量索引的滞后是一个隐忧

在以下情况下,请添加元数据前置过滤:

  • 用户期望通过日期、类别、作者或其他结构化属性进行约束
  • 超过 30% 的查询包含过滤器
  • 当你的向量数据库支持时,优先选择算法内过滤而非后置过滤

在以下情况下,请添加交叉编码器重排序:

  • 生成质量(而不仅仅是检索召回率)至关重要
  • 你的延迟预算可以承受 100–200ms 的开销
  • 精确度失效正导致明显的回答质量问题

在以下情况下,请考虑 SPLADE 或 ColBERT:

  • 三路混合检索(three-way hybrid)带来的质量提升足以支撑其运维复杂性
  • 你需要更好地泛化到新的或低资源领域
  • 多向量表示的存储成本是可以接受的

在以下情况下,请坚持使用纯向量搜索:

  • 你的查询是真正的语义搜索,没有精确匹配要求
  • 你的文档语料库是同质的,且被你的嵌入模型很好地覆盖
  • 检索延迟是硬性约束,且你经过测量发现混合检索的收益并不足以抵消成本

无人提及的陈旧性问题

一种不会出现在基准测试中的失效模式是:随着语料库中文档的变化,检索质量在无声无息地下降。当源文档被更新、删除或添加时,未刷新的向量索引会继续基于陈旧的嵌入(Embeddings)返回结果。

与几乎实时进行增量更新的 BM25 倒排索引不同,ANN 索引通常需要从头重建。许多团队直到用户投诉后,才发现他们的向量索引已经过时数周了。检索系统在正常运行,LLM 在自信地回答,但答案却是基于已经不再反映现实的信息。

对于生产系统而言,新鲜度架构(Freshness Architecture)与检索架构至少同样重要。在文档更新时触发重新嵌入的变更数据捕获(CDC)流水线、支持原子交换(Atomic Swaps)的版本化向量存储,以及跟踪真相源与向量索引之间延迟的新鲜度监控,这些都是混合检索架构无法自动提供的必要基础设施。

结论

向量搜索是大多数 RAG 实现的正确起点。错误在于将其视为已经足够。生产环境下的检索工作负载具有精确匹配需求、结构化约束和精度要求,而纯稠密检索(Dense Retrieval)无法可靠地处理这些问题。

通往生产级检索的路径并不复杂,但它具有层次感:BM25 用于词法精度,稠密检索用于语义桥接,RRF 用于在不依赖脆弱的分数归一化的情况下结合两者,元数据感知索引用于结构化约束,以及当精度失效影响回答质量时使用的交叉编码器(Cross-encoder)重排序。每一层都会增加复杂性和延迟——但每一层也都能处理前一层无法解决的失效模式。

基准测试的提升是真实的:混合方法比单一检索方法的检索质量提高了 20–48%。这种提升是否值得工程投入,取决于你的查询分布以及在特定领域中检索失败的代价。对于大多数构建生产级 RAG 系统的团队来说,这是值得的。

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