跳到主要内容

重排序器(Reranker)鸿沟:为什么大多数 RAG 流水线忽略了最重要的一层

· 阅读需 11 分钟
Tian Pan
Software Engineer

大多数 RAG 流水线都有一个隐形的准确率天花板,而构建它们的工程师甚至不知道它的存在。你调整分块策略、升级嵌入模型、更换向量数据库——但系统对于某些顽固的查询,依然返回看似合理但微妙错误的文档。检索看起来很合理。LLM 听起来很自信。但下游准确率已悄然进入平台期,无论进行多少提示工程(prompt engineering)都无法突破。

这个差距几乎总能追溯到同一个缺失的部分:Reranker(重排序器)。具体来说,是在第二个检索阶段缺少了交叉编码器(cross-encoder)。这一层在技术上是可选的,但在实践中跳过它的代价很高,而且在大多数 RAG 流水线所遵循的经典“嵌入、索引、查询”教程中,它往往被系统性地忽略了。

为什么双编码器(Bi-Encoders)不是合适的工具(仅靠它自己)

双编码器(bi-encoder)——每个向量搜索核心的嵌入模型——只擅长做一件事:将文本投影到一个高维空间,使语义相似的内容落在附近。这在大规模场景下非常有用。你在索引时对整个语料库进行一次编码,然后在查询时对查询进行编码,并运行近似最近邻查找。无论语料库大小如何,整个操作的成本都是 O(1)。这就是为什么向量搜索可以在 100 ms 内扫描数百万个文档的原因。

但这种速度来自于一种架构约束,从根本上限制了精度:查询和文档在编码过程中从未“见过”彼此。双编码器在完全隔离的情况下对查询和每个文档进行编码。相似性得分仅仅是两个独立生成的向量之间的余弦距离。

这导致了一种特定的失效模式。模型必须在知道查询是什么之前,将文档的所有可能含义压缩成一个固定大小的向量。领域特定的术语、否定词、量词以及微妙的措辞差异——这些通常决定文档是否真正相关的因素——在压缩过程中会被模糊或丢失。一篇关于“不使用加密的风险”的文档和一篇关于“加密的好处”的文档在向量空间中可能会非常接近。对于双编码器来说,它们看起来几乎一模一样。

结果是:向量搜索检索到的是 看似合理 的文档。但不一定是 正确 的文档。

Reranker 究竟在做什么

交叉编码器(cross-encoder)重排序器采用了一种根本不同的方法。它不是独立地对查询和文档进行编码,而是将它们拼接在一起,并让两者同时通过一个 Transformer。模型在单次前向传递中同时看到完整的查询文本和完整的文档文本,并输出该对组合的相关性得分。

这种联合编码实现了双编码器在结构上无法捕捉的交互信号。注意力机制可以将查询中的特定词语与文档中的特定词语关联起来。查询中的否定词实际上会影响得分。稀有的领域术语会根据其在上下文中的出现情况被赋予权重。Reranker 做的事情比模式匹配更接近于阅读理解。

实际结果是,在处理难题时,其准确率始终优于双编码器。跨生产部署的基准测试显示,与仅使用嵌入的方法相比,重排序将检索精度提高了 15–48%,并且在不同领域中 NDCG@10 都有持续增长。在一个具有代表性的评估中,不使用 Reranker 的 Top-K 准确率在 0.83 处达到平台期。而在相同的初始检索集上使用交叉编码器 Reranker 后,准确率达到了 0.93。

对于 RAG 而言,这种差距会产生复合效应。当你的检索器向 LLM 发送了稍微错误的上下文时,LLM 会为了填补空白而产生幻觉——而且表现得自信、流利且错误。多项生产评估表明,在不改变 LLM、提示词或分块策略的情况下,在流水线中插入 Reranker 后,幻觉率降低了 28–40%。

为什么大家都会跳过它的计算原因

交叉编码器无法预计算任何内容。因为查询直到请求时才确定,所以无法提前批量编码。每个查询都需要为每个候选文档运行一次完整的 Transformer 前向传递。如果你检索了 50 个候选文档并对其进行重排序,你就需要运行 50 次顺序推理。

在 CPU 上,对 30 个候选文档进行重排序需要 100–150 ms。在 GPU 上需要 30–50 ms。如果超过 100 个候选文档,仅重排序步骤就需要超过 300 ms。对于基于 LLM 的重排序器(使用大型生成模型而不是专门的交叉编码器),每次查询的延迟会跳升至 1–6 秒。

这个成本是真实存在的,但作为完全跳过重排序的理由,它经常被高估了。关键的见解是,你不需要对整个语料库进行重排序——你只需要对一小部分候选集进行重排序。两阶段模式是:

  1. 第一阶段:使用具有高召回率的快速双编码器或 BM25 检索。在几毫秒内检索 50–100 个候选文档。优化目标是不遗漏相关文档,接受其中包含一些不相关文档的事实。
  2. 第二阶段:对这些候选文档使用交叉编码器 Reranker。按真实相关性重新排序。将排名前 5–10 的文档传递给 LLM。

对于交叉编码器的情况,第二阶段增加了 100–200 ms。这就是不跳过它所带来的真实成本。

什么时候跳过重排序是合理的

在某些合理的场景下,跳过重排序(Reranking)是有意义的。但如果将这些特例与一般情况混为一谈,往往会导致团队在检索质量上投入不足。

不惜一切代价满足低于 100 毫秒的延迟要求。 如果你的应用处于对延迟极度敏感的关键路径上——例如实时自动补全、具有严格 SLA 的同步 API——且你没有额外的 100 毫秒预算,那么一个调优良好的双编码器(bi-encoder)加上较大的初始检索窗口是一个合理的折中方案。检索 20 个候选文档,取前 5 个,并接受准确率上的损失。

查询本身非常简单。 比如按类别查找文档、针对小型固定语料库的 FAQ 匹配,或者查询词汇与文档词汇高度重合的检索场景——这些情况不会触及双编码器的局限性。那些在跨领域或复杂查询中出现的语义压缩偏差(semantic compression artifacts)并不会显现。如果你的内部评估显示,针对实际查询分布,双编码器的召回率已经超过 0.95,你可能就不需要额外的一层处理。

索引质量才是真正的瓶颈。 如果你的文档分块(chunking)很糟糕、格式不统一,或者包含大量模版噪声,重排序器只会更准确地对糟糕的候选文档进行重新排序,返回给 LLM 的依然是错误的上下文。重排序并不能解决索引质量问题——它假设第一阶段的检索器已经将答案包含在了候选集中。如果你发现系统性地缺失相关文档(而不仅仅是排序错误),那么改进分块方式或使用领域特定的嵌入模型(embedding model)会比增加重排序器带来更多收益。

诊断上的区别至关重要:排序错误是重排序问题;缺失相关文档则是召回率或索引质量问题。

选择重排序器

重排序器的生态已经显著成熟。以下三类涵盖了大多数生产用例:

专用交叉编码器(如 Cohere Rerank、BGE-reranker、Jina Reranker)是专为相关性评分而构建的。相对于基于 LLM 的方法,它们速度很快——在 GPU 上处理每批 50 个候选文档仅需 30–100 毫秒——并且在检索基准测试中始终优于嵌入模型。对于大多数团队来说,这是理想的起点。

新型学习型重排序器(如 zerank-1、RankLLM)融入了指令遵循和领域适应能力。ZeroEntropy 的 zerank-1 在基准测试中比基准检索器提升了 28% 的 NDCG@10,并显示出与降低下游幻觉率的显著相关性。如果你在通用交叉编码器表现不佳的专业领域工作,这些工具值得评估。

基于 LLM 的重排序器(使用大型生成模型对文档对进行评分)在处理复杂查询时能达到最高的准确率,但会增加 1–6 秒的延迟和不菲的成本。在大规模应用中,其单位经济效益通常令人望而却步。它们最适合用于对延迟不敏感的异步流水线中,例如文档预处理、研究工作流或批量数据增强。

MTEB 上的重排序器基准排行榜是比较不同选项在你的查询类型下表现的最可靠参考。不要盲目相信厂商报告的数字,要在你的实际查询和候选集上进行测试。

做出决策

如果你正在构建或审计一个 RAG 流水线,重排序的问题可以通过以下步骤解决:

首先,独立于端到端准确率来衡量检索召回率(recall)。从你的生产分布中抽取 100 个代表性查询,执行常规的 Top-K 检索,并手动验证真正相关的文档出现在该集合中的频率。如果召回率本身就不达标——即正确的文档根本不在候选集中——那么在添加重排序器之前,请先优化检索阶段。

其次,如果正确的文档在候选集中,但经常排在第 3 或第 5 位之后,那么你面临的是重排序问题。在现有的检索候选集之上添加一个交叉编码器,并衡量准确率的变化。这项工作通常只需不到一天的工程量,而准确率的提升通常是立竿见影且可衡量的。

第三,测量前后的延迟。大多数团队发现,在 LLM 生成已经需要 1–3 秒的对话或搜索界面中,100–150 毫秒的重排序延迟对用户来说是感知不到的。如果延迟确实是个问题,可以优化候选集的大小——重排序 20 个候选文档而非 50 个,通常能以一半的延迟成本保留 80% 的准确率收益。

那些因为重排序“增加了复杂性”而跳过它的工程师,实际上是接受了一个永久性的准确率上限,以换取一个更容易解释的简单架构。而那些在衡量了具体的召回率和延迟约束后,因发现其并非必要而跳过它的工程师,则做出了一个合理的权衡。这两类人群之间的差距——无论是在生产环境的准确率上,还是在对系统实际瓶颈的理解上——正如其名所示,就在于那一层“排序”。

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