1.4 混合检索与重排序工程实战 本节摘要:纯向量检索对精确关键词不敏感,纯关键词检索又不懂语义——生产级 RAG 几乎都会走向"稠密向量加稀疏 BM25"的混合检索,再叠一层 cross-encoder 重排序。本节拆解混合检索的两路召回、分数融合(RRF 与加权融合)、重排序模型的选型与延迟取舍,并给出一套可落地的参数调试顺序,帮你把检索准确率再往上提一截。 先说结论 阅读完本节,你应当能够: 说清稠密检索和稀疏检索各自的盲区,以及混合检索为什么能互补 区分 RRF 融合与加权分数融合的适用场景,并指出各自的坑 解释 cross-encoder 重排序为什么比双塔模型准却更慢 给出一套从零搭生产级检索的参数调试顺序 一、问题与直觉
本节摘要:纯向量检索对精确关键词不敏感,纯关键词检索又不懂语义——生产级 RAG 几乎都会走向"稠密向量加稀疏 BM25"的混合检索,再叠一层 cross-encoder 重排序。本节拆解混合检索的两路召回、分数融合(RRF 与加权融合)、重排序模型的选型与延迟取舍,并给出一套可落地的参数调试顺序,帮你把检索准确率再往上提一截。
阅读完本节,你应当能够:
前面几节我们反复遇到一个尴尬:向量检索懂"意思",却常常搞不定型号、编号、人名这类必须精确匹配的词。反过来,传统的关键词检索(BM25)能精准命中"XQ-7890"这种字符串,但问"怎么处理订单重复"时,它分不清"处理"和"修复"是同义,召回的全是带"处理"二字的文档,把真正讲"修复方案"的文档漏了。
两套机制各有盲区,而它们的盲区几乎不重叠。向量检索的弱项(精确串匹配)恰好是关键词检索的强项,关键词检索的弱项(同义、改写)恰好是向量检索的强项。既然如此,把它们两路都跑一遍,再把结果揉到一起,理论上就能拿到"1加1大于1"的效果。
这就是混合检索(hybrid retrieval)的出发点。再加上一个事实——检索召回的 top-k 里,相似度分数高不等于真正相关——重排序(re-ranking)这一层也就顺理成章地叠了上去。生产级 RAG 的检索链路,最后多半长成"两路召回 → 分数融合 → 重排序精选"三段式。
混合检索的第一步是让两条检索链路各自独立跑一遍。
稠密这一路就是前面讲过的向量检索:查询和文档都编码成向量,算余弦相似度。它擅长抓住"订单重复"和"重复下单"是同一回事这种语义层面的关联。
稀疏这一路通常用 BM25(或者它的变体 SPLADE 等神经稀疏检索)。BM25 本质是个改进版 TF-IDF,它统计词频和文档频率,给稀有词更高权重。所以"XQ-7890"这种只出现在少数文档里的型号,BM25 会给很高分,几乎一定能精确召回。
两路各自返回一个候选集,比如各 top-20,加起来去重后可能有三十多个候选。下一步就是把这些候选按"到底多相关"排个序。
两路返回的分数不能直接比——向量相似度是 0 到 1 的余弦值,BM25 是个没有上界的打分(可能 3 分也可能 30 分),量纲完全不同。直接加权平均会把 BM25 的大数值淹没掉向量的细微差异。所以融合要讲究方法。
最常用的是 RRF(Reciprocal Rank Fusion,倒数排名融合)。它不看分数本身,只看每篇文档在两路结果里的排名,按下面这个简单公式给每篇文档算一个融合分:
RRF_score(doc) = Σ 1 / (k + rank_i(doc)) 路数i k 通常取 60,rank_i 是该文档在第 i 路结果里的名次(第1名 rank=1)
RRF 的妙处在于它只认排名不认分数,天然规避了量纲问题。一篇文档如果在两路里都排得很靠前,它的 RRF 分就会很高;如果只在一路靠前另一路没出现,分数就一般。这恰好符合"两路都认同的才是真相关"的直觉。
另一种是加权分数融合:先把每路分数归一化到 0 到 1,然后按权重加起来,比如 0.7 * 向量分 + 0.3 * BM25分。它的好处是能根据业务调节两路的权重——如果你的查询偏事实型(型号、人名多),把 BM25 权重调高;如果偏理解型(问"怎么办"),把向量权重调高。
| 融合方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| RRF | 无需调参,对分数量纲不敏感 | 忽略分数大小,只看排名 | 通用默认,参数不确定时先用它 |
| 加权融合 | 可按业务调权重,利用分数信息 | 要归一化、要调权重 | 知道自己业务偏哪一路时 |
💡 关键直觉:不知道选哪个融合方式时,先用 RRF。它几乎不用调参,效果通常已经很稳。等你跑出基线、发现某一类查询系统性偏弱,再考虑切到加权融合去针对性加权。
融合完拿到二三十个候选,最后一步是重排序。重排序用的是比双塔模型更重的 cross-encoder。
双塔模型(也就是普通的 embedding 模型)是把查询和文档分别编码成向量再算相似度,速度快(文档向量可以预先算好存库),但它在编码时看不到对方,所以相似度其实是"各自猜的"。
cross-encoder 不一样,它把查询和文档拼在一起送进一个 transformer,让注意力机制在查询词和文档词之间充分交互,最后输出一个相关性分数。因为模型能"同时看到两边",它的判断准得多。代价是慢——每对查询-文档都要跑一次完整的前向计算,没法预计算。
下面这张图把整条链路的各环节、它们的延迟量级和取舍点画在一起,方便你一眼看清瓶颈在哪。

看这张图的重点在最右边的延迟标注:召回和融合都是毫秒级,真正吃时间的是重排序这一段。这也是为什么重排序只对二三十个候选做,而不是对全库做——cross-encoder 对全库跑一遍根本不现实。
重排序模型分几类,选型看的是"准到什么程度、快到什么程度、要不要钱"。
| 类型 | 代表 | 准确度 | 速度 | 部署 |
|---|---|---|---|---|
| 开源 cross-encoder | bge-reranker、jina-reranker | 高 | 中(GPU 快,CPU 慢) | 自部署,免费 |
| 闭源 API | Cohere Rerank、各云厂商 | 很高 | 中(走网络) | 调 API,按量付费 |
| 轻量蒸馏版 | 蒸馏的小模型 | 中 | 快 | 自部署,资源省 |
我的经验是:先用开源的 bge-reranker 这类跑基线,多数业务已经够用。只有当基线的 top-3 相关性仍不达标、且你的查询量能撑得起 API 成本时,再考虑切闭源 API。别一上来就调闭源 API——它准,但贵,而且把数据送出去还有合规问题。
重排序是延迟大头,几个压延迟的手段:
第一是控制重排候选数。重排 30 个还是 50 个,延迟差很多。先用 RRF 融合把候选压到 20 到 30 个再重排,别把 50 个全塞进去。
第二是批量并行。cross-encoder 对每个候选都要跑一次前向,把这些前向做成 batch 并行送进 GPU,比一个个串行快得多。
第三是缓存。同一个查询短时间内重复来,重排结果可以缓存几分钟。高频查询的缓存命中率往往不低。
⚠️ 常见坑:重排序模型和召回用的 embedding 模型最好训练分布相近。用一个中文训练的召回模型配一个英文为主的重排序模型,可能出现召回觉得相关、重排序却打低分的不一致。选同一系列的召回加重排模型(比如都用 bge 系列)能减少这种摩擦。
混合检索的旋钮很多(召回 top-k、融合权重、重排候选数、重排 top-k),一通乱调只会越调越乱。建议按这个顺序来:
1. 固定一套保守默认参数(召回各 top-20、RRF 融合、重排 top-5) 跑出基线,记录一批测试问题的准确率 2. 先调召回:向量召回和 BM25 召回各调 top-k 看准确率天花板有没有提升(召回不全,后面再准也白搭) 3. 再调融合:从 RRF 切到加权融合,扫几组权重 看是否比 RRF 更好(多数情况下 RRF 已经够) 4. 最后加重排序:引入 cross-encoder 重排 看最终 top-3 / top-5 的相关性提升
这个顺序的逻辑是:先保证召回的全,再保证排的准。召回阶段漏掉了正确文档,重排序再强也救不回来。所以调试永远从召回开始往下走,别一上来就纠结重排序模型选哪个。
有个团队上线混合检索后,发现一类查询准确率反而比纯向量检索还低。排查下来是 BM25 那一路的问题:他们的文档里大量出现一个高频词(公司名),BM25 给这个词的权重很高,导致几乎每篇文档都因为含公司名而被召回,BM25 这一路失去了区分度,反而把噪声灌进了融合结果。
修法是对 BM25 这一路的高频词做停用词过滤,或者直接把这种"几乎每篇都有的词"在 BM25 打分时降权。这个案例提醒我们:混合检索不是"加一路就一定更好",两路里任何一路质量太差,都会通过融合拖累整体。
💡 关键直觉:混合检索提不提升效果,取决于两路是不是"互补且各自靠谱"。如果某一路质量很差(召回的全是噪声),把它加进来只会拉低融合结果。上线前务必分别评估每一路单独的准确率,确认它们各有专长再合并。
入门:在你已有的向量 RAG 上加一路 BM25 召回,用 RRF 融合,对比同一批问题准确率有没有变化。先别加重排序,单独体会混合召回的价值。
进阶:引入一个开源 cross-encoder 做重排序,记录加重排序前后 top-3 相关性的变化,以及端到端延迟增加了多少。试着调重排候选数(20 vs 30),看延迟和准确率的权衡。
挑战:把融合方式从 RRF 换成加权融合,扫三组权重(向量 0.5/BM25 0.5、0.7/0.3、0.3/0.7),找出你这批测试数据上最优的权重,并解释为什么这个权重适合你的业务。
回顾一下,混合检索是生产级 RAG 把检索准确率再提一档的关键手段,但它不是无脑加一路就有效,要讲究融合方式和调试顺序。
第一章到这里收尾。我们讲完了"知识怎么检索"。下一章换个主题——怎么用最小代价把通用大模型微调成懂你业务的专用模型。