LLM 面试备考
首页/RAG

混合检索与重排序 Hybrid Retrieval & Reranking

所属:01-fundamentals / 03-rag
面试权重:★★★
来源:通用领域知识(B站「RAG 面试」合集高频);未做一手考证

一句话#

BM25 与稠密向量是互补的:前者擅长精确词面(专有名词、型号、代码、数字),后者擅长语义改写(同义词、换一种说法);hybrid 检索用融合把两路结果并起来解决「召回」,reranker(cross-encoder) 再对候选池做精细排序解决「精度」—— 前者保 recall,后者保 precision,两者不是替代关系而是流水线的两级。

为什么重要(面试角度)#

这是 RAG 里最能体现「你真的调过系统」的一题。面试官想听的是分工逻辑:

  • 为什么单靠稠密向量会输给 BM25(举得出具体形态:把 RFC 7231 或 Qwen2.5-4B 这类字符串 embed 到语义空间里就失真了)
  • 为什么单靠 BM25 也不行(用户说「笔记本发烫怎么办」,文档写「设备过热处理」,没有共现词)
  • RRF 为什么好用(只用排名、不用分数,绕开两路分数量纲不可比的问题)
  • bi-encoder 与 cross-encoder 的本质差别:一次编码 vs 联合编码,以及由此决定的「能不能预建索引」
  • 什么时候 reranking 不值得它的延迟(这点最能区分有没有算过账)

核心内容#

为什么互补:两种「相似」的定义#

稀疏检索(BM25) 稠密检索(dense)
匹配依据 词面重合(term overlap) 语义距离(embedding 相似度)
词表 显式倒排,精确 隐式,模糊
强项 专有名词、型号、代码、错误码、数字、长尾术语 同义改写、口语→术语、跨语言
弱项 无法处理同义词/改写、词形变化 罕见串/ID 会被「平均」掉语义、无法保证精确命中
可解释性 高(能指到具体词) 低
需要训练 几乎不需要(统计) 需要(且域不匹配就得微调)

典型互补场景:用户问「RFC 7231 里的 503 是什么」—— BM25 精确命中 RFC 7231 与 503,稠密向量反而因为把这些稀有串投进语义空间而丢失精确性;用户问「网站被爬太狠了怎么保护」—— 文档写「设置 rate limit」,两者零词面重合,只有稠密向量能救。

BM25 的两个旋钮#

BM25 打分(按查询词求和,直观理解即可):

score(q,D)=∑iIDF(qi)⋅f(qi,D) (k1+1)f(qi,D)+k1 (1−b+b ∣D∣/avgdl)\text{score}(q, D) = \sum_{i} \text{IDF}(q_i) \cdot \frac{f(q_i, D)\,(k_1 + 1)}{f(q_i, D) + k_1\,(1 - b + b\,\lvert D\rvert / \text{avgdl})}
  • IDF:稀有词权重高,「的」「是」这类高频词几乎不贡献分数 —— 这是它天然抗噪声的地方。

    ⚠️ 这里此前只写了泛化的 IDF(qi)\text{IDF}(q_i),被追问「BM25 的 IDF 具体是什么形式」时会答不上。 两条要知道:

    ① BM25 的 IDF 与 TF-IDF 的 IDF 不是同一个公式。 BM25 常用的平滑形式是 ln⁡ ⁣(N−df+0.5df+0.5)\ln\!\big(\frac{N - df + 0.5}{df + 0.5}\big)(NN 是文档总数,dfdf 是含该词的文档数)。 ② 它有负值区间:当 df>N/2df > N/2(词出现在超过一半文档里)时,这个 ln⁡\ln 取负值 —— 也就是「这个常见词反而让文档减分」。实际实现里必须给它设下限(常见做法是把负的 IDF 截到 0 或一个很小的正数),否则一个高频词会把整篇文档的分数拉低。这是一个知道就知道、不知道会被问住的实现细节。

    另有一条脉络值得有:稀疏检索的演进是 BoW → TF-IDF → BM25,而倒排索引(词 → 含该词的文档列表)是它们共享的底层数据结构 —— 理解倒排索引就理解了稀疏检索为什么「精确匹配强、语义弱」。

  • k1k_1:控制词频饱和 —— 一个词出现 10 次不该比出现 3 次线性地更重要。

  • bb:控制长度归一化强度,防止长文档仅因「长」而累计高分。

BM25 无需训练、极致可解释,在长尾/精确匹配上是很难打败的基线 —— 先上 BM25 再谈向量,是很常见的工程判断。

融合:RRF 与加权求和#

两路检索各自返回一个排序,融合方式主要有两类:

① 加权分数融合:把两路分数归一化后加权求和 α·s_dense + (1-α)·s_sparse。 问题:两路分数量纲不可比(BM25 无上界、cosine 在 [-1,1]),归一化方式会显著影响结果,α 还得调。工程上能用但脆弱。

② RRF(Reciprocal Rank Fusion):

RRF(d)=∑r∈retrievers1k+rankr(d)\text{RRF}(d) = \sum_{r \in \text{retrievers}} \frac{1}{k + \text{rank}_r(d)}

只用排名、完全不管分数,所以天然规避了量纲问题,也天然鲁棒(不同检索器、不同数据集都免调参)。原论文取 k = 60,实践中也常用其他取值 —— k 越小、靠前的排名权重差异越大。这是面试里最该主动说出的融合方案:简单、无超参、效果好。

RRF 的局限:它丢弃了分数信息,若某一路的分数分布本身很有信息(比如 reranker 的分数),加权融合或直接串联更合适。实践中常见架构是「BM25 ∪ dense → RRF → reranker」。

Bi-Encoder vs Cross-Encoder#

这是全题的核心,用一张表说清:

Bi-Encoder(召回用) Cross-Encoder(重排用)
编码方式 query 与 doc 各自独立编码 query+doc 拼在一起送入同一模型
交互 无(最后才算相似度) 全交叉自注意力,逐 token 交互
精度 较低(信息交互被压缩到向量里) 高(能捕捉细粒度匹配与否定)
速度 极快,doc 向量可离线预建索引 慢,每个 query 都要对每个候选现算一次前向
可扩展性 支持百万~十亿级语料 只能对几十~几百个候选做
经典用法 粗筛(retrieve) 精排(rerank)

关键洞察:不是「cross-encoder 更强所以用它检索」,而是成本结构决定分工 —— cross-encoder 无法预建索引,对全语料打分是 O(N) 次前向,完全不可行;但对 50–100 个候选打分是完全可接受的。所以流水线必然是「廉价高召回 → 昂贵高精度」。

ColBERT(late interaction) 是中间路线:每篇文档预先编码成多向量(每个 token 一个向量),查询时做 MaxSim 的 token 级最大匹配 —— 介于 bi-encoder 与 cross-encoder 之间,可预建索引但索引体积大(多条向量/文档)。知道它存在、以及它的取舍即可。

什么时候 reranking 值得它的延迟#

Reranking 不是免费的:它引入额外延迟(对 50–100 个候选跑一次 cross-encoder 前向,量级在几十到几百毫秒,具体取决于模型大小与硬件,需实测)和额外部署。值得投入的判断:

  • ✅ 召回没问题、排序有问题:正确 chunk 已在 top-50 里但排不进 top-5 —— 这是 reranker 最典型的用武之地(也正是 pipeline.md 决策树第 3 步)。
  • ✅ 注入的上下文槽位稀缺:只能给 LLM 5 个 chunk,把最相关的挑出来直接影响 Faithfulness。
  • ✅ 查询类型多样,单一检索器覆盖不全,需要统一的精排层收口。
  • ❌ 检索本身召回就低:正确文档压根不在候选池里,reranker 无中生有不了 —— 先修检索(chunking / hybrid / query rewriting)。
  • ❌ 延迟极度敏感的场景(如实时语音助手),几十到几百毫秒可能是不可接受的,需权衡或用更小的 reranker。
  • ❌ 语料小且问题简单:top-5 已经对了,加 reranker 只是徒增延迟。

工程做法:把 reranker 做成可开关 / 可降级的一层,用 A/B 或离线评测对比「有 rerank vs 无 rerank」在端到端质量与 P95 延迟上的净收益,再决定压在哪个查询集合上。

一条典型流水线#

query
  ├─ BM25         → top-50 ┐
  └─ dense (ANN)  → top-50 ┤
                           ├─ RRF 融合 → top-20~50
                           └─ cross-encoder rerank → top-5
                                                     ↓ 注入 prompt

规模上注意:RRF 融合后的候选池越大,reranker 越慢 —— 候选池大小就是这一步的主要延迟旋钮。

常见面试问法#

Q:已经有向量检索了,为什么还要 BM25? A:两者匹配的东西不同。BM25 匹配词面,稠密向量匹配语义。专有名词、型号、错误码、数字这类稀有串在 embedding 里会被「平均」掉,向量检索不可靠;而同义词改写又只有向量能处理。hybrid 是把两类失败互补掉,而不是谁取代谁。

Q:两路分数怎么融合? A:首选 RRF,因为只用排名不用分数,绕开了 BM25 与 cosine 分数量纲不可比的问题,且几乎免调参(原论文 k=60)。加权分数融合也可行,但要先归一化且要调 α,脆弱一些。实践中常见 BM25 ∪ dense → RRF → reranker。

Q:bi-encoder 和 cross-encoder 的区别? A:bi-encoder 分别编码 query 和 doc,交互被压缩到向量点积里,精度低但可离线预建索引,能扛百万级语料;cross-encoder 把两者拼起来做全交叉注意力,精度高但每个候选都要现算一次前向,只能对几十~几百个候选做。所以流水线是「bi-encoder 粗筛 + cross-encoder 精排」。

Q:加 reranker 一定更好吗? A:不一定。前提是正确结果已经进了候选池 —— 召回本身低的话 reranker 无能为力,先修检索。其次看延迟预算:对几十个候选跑一次 cross-encoder 会增加延迟,实时性敏感的场景可能不划算。所以它应该是可开关的一层,用评测决定是否净收益。

Q:top-k 怎么定? A:分两段看。候选池要够大以保住召回(几十量级),注入 LLM 的 top-n 要小以控噪声与成本(几个量级)。中间靠 reranker 把信号挑出来 —— 用「宽召回 + 精排序 + 窄注入」替代「一个 k 打天下」。

易错点#

  • ❌ 说「有向量检索就不需要 BM25 了」—— 忽略了精确词面匹配这一类硬需求。
  • ❌ 用未归一化的分数直接加权融合,或声称「权重怎么定都行」。
  • ❌ 混淆 bi-encoder / cross-encoder 的成本结构,说出「用 cross-encoder 建索引」这种不可能的事。
  • ❌ 把 reranking 当默认必开,不评估其延迟与「召回不足时无效」的前提。
  • ❌ 用一个大 k 同时当候选池和注入数,导致噪声与成本双高。
  • ❌ 把 RRF 的 k=60 当唯一正确取值(它是原论文取值,实践中有别的用法)。

自测#

1. 举一个稠密向量必定输给 BM25 的例子,和一个 BM25 必定输给稠密向量的例子。

答案

BM25 胜:查「RFC 7231 的 503 状态码」「错误码 E5023 怎么解决」「Qwen2.5-4B 的显存占用」—— 这类稀有字符串/编号/型号在稠密空间里会被语义平滑掉,词面精确匹配才可靠。稠密胜:用户问「网页被爬虫抓爆了怎么办」而文档写「配置 rate limit 与 WAF 规则」—— 零词面重合,只有语义向量能建立联系。这正是 hybrid 检索的动机。

2. 为什么 RRF 比「加权分数求和」更受工程欢迎?

答案

因为 RRF 只用排名,不用分数:BM25 分数无上界、cosine 在 [-1,1],两路量纲不可比,加权求和必须先归一化并调 α,脆弱且换数据集就失效。RRF 的形式 Σ 1/(k+rank) 天然把不同检索器的输出统一到可比尺度,几乎免调参,对新增检索器也鲁棒。代价是丢弃了分数本身的信息。

3. 为什么不能直接用 cross-encoder 做检索?

答案

成本结构不允许。cross-encoder 把 query 与 doc 拼接后做全交叉注意力,无法为文档预计算并复用的向量(表示依赖具体的 query),所以对全语料是 O(N) 次前向,百万级语料完全不可行。它只能对已由廉价检索器缩小的候选池(几十~几百)做精排。这就是「粗筛用 bi-encoder、精排用 cross-encoder」这一流水线结构的根本原因,而不是精度差异本身。

4. 你的 RAG 已经用了 hybrid + reranker,端到端还是不准。你会怎么判断 reranker 是不是有用?

答案

先验证前提:用 gold chunk 算 Recall@候选池,确认正确答案确实进了 reranker 的输入池。若没进,reranker 无从发挥,问题在检索侧(chunking / 召回 / 查询改写)。若进了,再看 reranker 输出里 gold 的排名 —— 排不进 top-n 说明重排无效或候选池存在大量强干扰;做「开/关 reranker」的离线对照(Recall@n、Faithfulness)和延迟对照(P95),若质量提升不显著、延迟增加明显,就该关掉或只对特定查询启用。

5. 「宽召回 + 精排序 + 窄注入」为什么比「一个 top-k 打天下」好?

答案

因为召回与注入的最优粒度方向相反:候选池要大才能保住 recall(正确结果可能在 50 名开外),注入 LLM 的上下文要小才能控噪声、控 token 成本、避免 lost-in-the-middle。一个统一的 k 必然在某一端妥协 —— 小了召回不足,大了噪声与成本失控。中间用一个便宜的粗排 + 一个高质量的精排把「大池」收敛成「小集」,是同时满足两端约束的结构。