从一次失败的多路召回说起
假设你负责一个企业知识库问答系统,用户会问“CVE-2024-3094 影响哪些版本”,也会问“服务突然挂了怎么排查”。你为文档建了向量索引,也接入了 Elasticsearch 的 BM25,两路召回都返回了 Top 20,然后把结果直接拼在一起丢给大模型。结果模型上下文里塞满了重复甚至互相矛盾的片段,回答质量没提升,token 成本倒是先涨了。
问题出在哪?向量检索给的是余弦相似度,取值在 0~1 之间;BM25 分数理论上没有上界,可能是 3.2,也可能是 27.8。两套评分体系量纲不同,直接拼在一起排序,就像拿厘米数跟公斤数比大小,谁排前面全看运气。
要解决这个问题,需要先理解为什么单路检索不够,再引入一种不依赖原始分数的融合方法——倒数排名融合(Reciprocal Rank Fusion,RRF)。本文以企业知识库问答为贯穿场景,讨论 RRF 的原理、参数敏感性,以及它与重排序(Rerank)的关系和部署取舍。
单路检索的盲区:为什么需要多路召回
向量检索的盲区:精确匹配失效
向量检索擅长捕捉语义相似,但对精确字符串匹配不敏感。以开头提到的 CVE 查询为例,用户问的是“CVE-2024-3094 影响哪些版本”,向量检索会召回一堆讲“漏洞影响范围”“安全补丁”的相关文档,但很可能漏掉那篇标题里精确写着 CVE 编号、内容却是纯表格、几乎没有语义描述的公告文档。原因是向量模型对编号、型号这类字符串的语义表达能力天生较弱,编号在向量空间中几乎接近噪声。
关键词检索的盲区:同义替换与口语化提问
反过来,BM25 这类关键词检索依赖词项匹配,遇到同义替换或口语化表达就失效。用户问“服务突然挂了怎么排查”,文档里写的是“进程异常退出后的故障定位方法”,两句话意思一致,但共同出现的关键词几乎为零,BM25 难以召回。在实际客服场景中,用户很少使用文档中的“官方措辞”提问,纯关键词检索的 Top-5 召回率往往明显低于向量检索。
向量检索管“意思相近”,关键词检索管“字面精确”,两者互补而非互相替代。这也是混合检索成为生产级 RAG 系统常见配置的原因。
混合检索架构:并行召回与结果融合
确定采用混合路线后,需要设计召回架构。常见的做法是并行分发查询:向量检索(如 Milvus)召回 Top N,BM25 检索(如 Elasticsearch)召回 Top N,两路并行执行,避免串行带来的延迟累加。之后将两路结果汇总,进入融合排序阶段。
融合阶段的目标是把两路排名合成一个统一的排序。业界常用的方法是 RRF,它不关心原始分数,只看文档在各路结果中的排名。排名天然可比:第 1 名不管在哪一路都是“最好”,不存在量纲问题。
RRF 的原理与参数敏感性
数学公式与直觉
RRF 的公式如下:
RRF_score(d) = Σ 1 / (k + rank_i(d))
其中 rank_i(d) 是文档 d 在第 i 路召回结果中的排名(从 1 开始),k 是平滑常数,通常取 60。对文档 d,把它在每一路召回结果里的排名取倒数再累加。排名越靠前,1/(k+rank) 越大,贡献越高。一篇文档如果同时在向量检索和关键词检索中都排前几名,它的融合分数会明显高于只在一路中靠前的文档——这正是期望的效果:两路都认可的结果,可信度更高。
代码实现非常简单,不需要任何机器学习组件,以下为示意逻辑:
def rrf_fusion(vector_results, bm25_results, k=60):
scores = {}
for rank, doc_id in enumerate(vector_results, start=1):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
for rank, doc_id in enumerate(bm25_results, start=1):
scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + rank)
return sorted(scores.items(), key=lambda x: x[1], reverse=True)
k 值的影响
k 值的选择直接影响融合分数的分布。k 太小(比如 k=1),排名第 1 和第 2 之间的分数差会被放大,几乎变成“赢家通吃”;k 太大(比如 k=1000),所有文档的分数差都被压平,融合结果接近随机排序。业界经验值 k=60 来自信息检索领域的大量实验,没有特殊场景不建议改动。
RRF 的局限
RRF 本质上是在“拼凑”两个独立打分系统的排名,它并不真正理解 query 和文档之间的语义关系,只是在做“投票”。两路都排前面的文档得票高,但两路排名本身的质量无法被校正。例如,如果向量检索对某个 query 整体表现不佳,其排名靠前的文档可能并不相关,RRF 无法识别这种偏差。
RRF 与 Rerank 的对比:为什么还需要重排序
RRF 能改善融合排序,但它不引入新的相关性判断。Rerank(重排序)则不同:它使用一个专门训练的模型,直接吃 query 和候选文档的原始文本,输出精确的相关性分数,不依赖召回阶段的排名信息。
Bi-Encoder 与 Cross-Encoder
向量检索使用的 Embedding 模型是 Bi-Encoder 架构:query 和文档分别独立编码成向量,之后计算余弦相似度。这种架构可以预计算文档向量,检索时只需编码 query,速度极快,能支撑百万级候选集的检索。但代价是 query 和文档在编码阶段互相看不到对方,模型只能靠两个独立向量的几何距离近似相关性,精度有损失。
Reranker 采用 Cross-Encoder 架构:query 和文档拼接成一个整体输入模型,让自注意力层充分交互,逐词判断相关性,精度更高。代价是无法预计算,每个候选文档都要与 query 现场拼接推理,计算量比 Bi-Encoder 高一个量级,因此 Reranker 只能用于粗筛之后的少量候选。
对比表格
下表总结了 RRF 与 Rerank 的关键差异,帮助你在实际系统中做决策:
| 维度 | RRF | Rerank |
|---|---|---|
| 核心机制 | 基于排名倒数融合 | 基于 Cross-Encoder 模型打分 |
| 是否理解语义 | 否,只做投票 | 是,直接判断相关性 |
| 计算开销 | 极低,无模型推理 | 较高,需对每个候选对推理 |
| 适用阶段 | 多路召回后的粗融合 | 粗融合后的精排 |
| 部署成本 | 几乎为零 | 需要额外模型与 GPU 资源 |
| 对召回质量的校正能力 | 弱,无法修正单路排名偏差 | 强,可纠正排名错误 |
从表格可以看出,RRF 适合作为第一阶段的融合,Rerank 则用于进一步提升精度。两者不是互斥的,而是可以串联使用。
RAG 流水线中的部署位置与性能开销
典型流程
在企业知识库问答中,混合检索 + RRF + Rerank 的完整流程可以表示为:
flowchart TD
A[用户查询] --> B[并行分发]
B --> C[向量检索 Top 30]
B --> D[BM25检索 Top 30]
C --> E[RRF融合]
D --> E
E --> F[Rerank精排]
F --> G[Top 5 送入LLM]
用户查询首先被并行分发到向量检索和 BM25 检索,两路各返回 Top 30。然后 RRF 融合两路排名,得到粗排结果。接着 Rerank 对粗排的候选(通常取前 20 或 30)进行精排,最后将 Top 5 送入大模型生成答案。
性能开销与优化
Rerank 的延迟是主要性能瓶颈。如果对每个候选文档单独调用模型,20 个候选串行推理,单次请求延迟可能飙到 1.6 秒。解决方法是批量推理:把 20 个 query-doc 对拼成一个 batch 一次性输入模型。批量推理后,20 个候选的延迟可以从 1.6 秒降到 85 毫秒左右,差距近 20 倍。
另一个优化是语义缓存。对于高频重复问题(如客服场景的“怎么退款”),可以基于 query 的 Embedding 做近似去重,语义相似度超过阈值(如 0.95)时直接复用之前的 Rerank 结果。缓存命中率在客服场景可达 35% 左右,能明显降低 GPU 负载。
端到端效果
根据实际项目数据,从单路到“双路 + RRF + Rerank”,Recall@5 可提升约 20 个百分点,MRR 提升近 40%,代价是延迟从 45 毫秒涨到 155 毫秒。对大多数知识库问答场景,多出的 110 毫秒用户几乎无感知,但检索质量的提升会直接反映在大模型回答的准确率上。
常见失败模式与适用边界
混合检索 + RRF + Rerank 并非万能。以下情况可能导致方案退化:
- 召回不足:如果第一阶段的向量检索或 BM25 本身没有召回相关文档,后续的 RRF 和 Rerank 都无法补救。因此,切片策略和索引质量是前提。
- Rerank 模型分布偏移:如果 Rerank 模型在训练数据上偏向某种语言风格,而业务 query 风格差异大,精排效果可能下降。用业务数据微调可以缓解。
- 缓存失效:语义缓存依赖相似度阈值,如果 query 多样性高,阈值设置不当会导致命中率低,反而增加额外查询开销。
- 超低延迟场景:语音实时交互等场景对延迟要求极高,可能无法接受 100 毫秒以上的检索环节,此时需要牺牲部分精度换取速度。
此外,RRF 的 k 值如果设置不当,会导致融合结果偏向单路或趋于随机。生产环境需要监控融合后的排序质量,例如通过人工抽检或离线评估指标(如 Recall@K、MRR)来验证。
结论:混合检索是 RAG 质量的基石,但需要正确的融合策略
回到最初的问题:为什么多路召回能提升 RAG 检索质量?因为单路检索各有盲区,向量检索擅长语义但忽略精确匹配,BM25 擅长精确但不懂同义替换。混合检索让两路互补,但直接拼接结果会因量纲不一致而失效。RRF 通过排名融合解决了量纲问题,代价低、效果好,但它不引入语义理解。Rerank 作为第二阶段精排,能进一步提升精度,但需要额外计算资源。
在部署时,建议遵循“先混合检索解决召回,再 Rerank 解决精度”的顺序。如果知识库超过几千篇文档、用户提问方式多样,混合检索 + Rerank 几乎是必选项。需要权衡的是延迟与精度的取舍,以及是否值得为 Rerank 投入微调成本。最终,混合检索的价值不仅体现在命中率数字上,更在于“两路都同意”的文档往往更可靠,能减少大模型基于错误上下文生成答案的风险。