RAG 混合检索:RRF 原理与实现

黎 浩然/ 8 10 月, 2026/ 大语言模型/LARGELANGUAGEMODEL/LLM, 机器学习/MACHINELEARNING, 研究生/POSTGRADUATE, 计算机/COMPUTER/ 0 comments

RAG 的关键词检索和向量检索,可能各自找对一部分材料,却给出难以直接比较的分数。RRF(倒数排名融合)用名次合并结果:先保留两路检索各自的排序,再把同一文档的排名贡献相加。它不需要把 BM25 分数和向量相似度硬凑到同一尺度。

文章目录
  1. 为什么不直接把两路分数相加?
  2. RRF 的公式与一个小例子
  3. 用 Python 复算,而不是凭图判断
  4. 候选窗口比公式更容易被忽略
  5. 融合之后还要检查什么?
  6. 参考资料

为什么不直接把两路分数相加?

关键词检索擅长利用词项匹配信号,向量检索利用表示空间里的相似性。某一路得到 12 分,另一路得到 0.8,并不能仅凭数字大小判断谁更相关。直接相加会隐含一个尺度选择;归一化或训练融合是其他路线,RRF 则选择只看名次。

例如搜索具体错误码时,字面匹配可能很重要;换一种说法表达概念时,语义匹配可能有帮助。这是采用混合检索的动机,不是两路必然互补的保证,需要用真实查询验证。

RRF 的公式与一个小例子

对文档 d,RRF(d) = Σ 1 / (k + rankᵢ(d))。排名从 1 开始,只对包含该文档的列表求和;没被某一路召回,就没有那一路的贡献。k 是平滑常数,不是返回结果数。本例使用 k = 60。

RRF融合示意图:关键词列表A B C与向量列表B D A合并为B A D C
原创例子,文档 ID 和排名均为假设。图中不包含真实相关性标签,也不是检索质量测试。
文档 关键词排名 向量排名 融合分数
B 2 1 1/62 + 1/61 ≈ 0.03252247
A 1 3 1/61 + 1/63 ≈ 0.03226646
D 未出现 2 1/62 ≈ 0.01612903
C 3 未出现 1/63 ≈ 0.01587302

B 在两路中都靠前,最终超过关键词列表第一名 A。这种跨列表支持并不证明 B 的内容正确;分数只是融合规则的产物,也不是答案正确概率。

用 Python 复算,而不是凭图判断

from collections import defaultdict
from fractions import Fraction

def rrf(lists, k=60):
    if k <= 0:
        raise ValueError('k must be positive')
    scores = defaultdict(Fraction)
    for ranked in lists:
        if len(set(ranked)) != len(ranked):
            raise ValueError('duplicate document ID in one list')
        for rank, doc in enumerate(ranked, 1):
            scores[doc] += Fraction(1, k + rank)
    return sorted(scores.items(), key=lambda x: (-x[1], x[0]))

results = rrf([['A', 'B', 'C'], ['B', 'D', 'A']])
assert [doc for doc, score in results] == ['B', 'A', 'D', 'C']
assert rrf([]) == []
assert rrf([['Z'], ['A']])[0][0] == 'A'
try:
    rrf([['A', 'A']])
except ValueError:
    pass
else:
    raise AssertionError('duplicates must be rejected')
for doc, score in results:
    print(doc, f'{float(score):.8f}')

代码已执行,输出与表格一致。它用分数做精确累加,只在展示时转为小数;同分时按文档 ID 排序,保证本例可复现。单路重复 ID 被拒绝,避免重复贡献;跨路相同 ID 正是需要融合的对象。空输入也已检查。

设输入名次总数为 N、不同文档数为 U。忽略数值运算位宽变化,字典累加平均 O(N),排序 O(U log U),额外空间 O(U)。这段代码没有运行关键词或向量检索,也不能说明真实系统的延迟。

候选窗口比公式更容易被忽略

每路只取前几个结果,就只有这些候选能进入融合。两路都没找到的文档,RRF 无法恢复。若融合后需要十条结果,却每路只检索极少候选,先检查召回窗口,再讨论排名常数。

增大 k 会缩小靠前与靠后名次之间的相对差异,不能将其当作“越大越准确”的旋钮。窗口大小、检索器质量及列表相关程度都会影响最终排序。Elastic 与 OpenSearch 的参数名称和限制属于各自产品接口,不能把示例函数当成其完整实现。

融合之后还要检查什么?

先统一文档或分块 ID:同一内容若在两路使用不同 ID,会被当成不同候选;不同内容若错误共用 ID,又会被错误合并。对检索结果做权限过滤,不能让融合把用户无权阅读的材料送入模型。

融合后可以再用适合任务的重排器,但它仍只能处理候选。保留检索相关性指标、答案引用是否支持论断,以及端到端延迟的分别测量。搜索质量变好,不等于模型一定忠实使用材料;引用也应对照原文检查。

RRF 面向检索,不改变生成阶段的 KV cache,可结合KV cache 原理与显存估算区分检索与生成的成本。English version。

参考资料

  • RRF 原始论文,SIGIR 2009,本文讨论已有算法,不将其包装成新论文或最新新闻;原论文实验不作为本站性能承诺。
  • Elastic:Reciprocal rank fusion,公式、排名起点与候选窗口的官方说明。
  • OpenSearch:RRF,另一种产品实现的第一手文档。接口以实际使用版本为准。

补发说明:本文实际于2026年10月8日稍后补发,文章日期保留原计划的北京时间09:00。

支持

如果这篇文章对你有帮助,欢迎支持本站。

微信支持二维码,点击查看大图
微信
支付宝支持二维码,点击查看大图
支付宝
Buy Me a Coffee,点击支持本站
Buy Me a Coffee

二维码可点击放大。更多支持方式见支持页面。

Share this Post

Leave a Comment

您的邮箱地址不会被公开。 必填项已用 * 标注

*
*