纯向量检索的盲区
前两篇搭的流水线里,检索只靠向量化(embedding)一路:把查询和文档都映射成向量,语义相近就距离近,「按意思搜」。这套机制对自然语言改写很鲁棒——用户问「怎么省钱」,文档写「降低成本」,照样命中。
它的软肋恰恰在字面上。考虑这几类查询:产品型号(A-2000X)、内部缩写(RAG、SLA)、罕见人名、法条编号、错误码。这类 token 的意义几乎完全取决于逐字符对上,而 embedding 模型对它们的区分度有限。典型翻车现场:库里明明有提到 A-2000X 的文档,用户搜「A-2000X 价格」,回来的却是几段讲「定价策略」的泛泛之谈——因为在向量空间里,「价格」贡献的相似度远大于型号字符串。
反过来,BM25 这类字面检索对同义改写无能为力,但对精确术语一击即中。语义检索与关键词检索是互补关系,不是替代关系——这就是混合检索的出发点。
BM25:字面匹配的老将
BM25 是基于词频的经典稀疏检索算法,可以理解为 TF-IDF 的改进版,思想朴素:查询里的词,谁命中的多、越罕见,谁得分高,同时对过长文档做词频稀释惩罚。它不试图理解语义,只认真做字面匹配。它同时也是工程成熟度最高的检索算法之一——Elasticsearch 等搜索引擎与 SQLite 全文检索(FTS5)默认的打分方案就是它或其变体。这意味着给 RAG 加一路 BM25,很多现成存储直接就能提供,不必引入新组件。
混合检索:两路召回,RRF 融合
混合检索(hybrid retrieval)就是两路同时跑:向量管语义、BM25 管字面,各自返回一份排好序的候选列表,再融合成一份。融合的第一道坎是分数量纲:BM25 的分数没有上限,向量相似度又通常落在 0 到 1 附近的区间里,直接加权等于胡来。
RRF(Reciprocal Rank Fusion,倒数排名融合)的聪明之处是不比分数,只比名次:对每个候选文档,把它在每路结果里的名次换算成 1/(k + rank)(k 是平滑常数,原始论文建议的默认值在 60 上下),跨路累加后重排。直觉很朴素:一个文档若在两路里都排得靠前,说明「两个证据源都点头」,其可信度高于「一路极高、另一路垫底」的偏科生——而只看名次恰好绕开了量纲问题。
Rerank:最后一道精排
召回解决「别漏」,但融合后的排序仍然粗糙:RRF 只看名次,不看查询与文档的真实贴合度。最后一步交给重排序(rerank)。理解它,关键是两种打分结构的差别:
- 双塔模型(bi-encoder,检索用的 embedding 模型就是这类):查询与文档各自独立编码成向量。文档向量可以离线算好存进索引,在线只需编码一次查询再做近邻搜索——毫秒级,快;但两段文本在编码时互不见面,精度有天花板。
- 交叉编码器(cross-encoder):把查询与文档拼成一段送进同一个模型逐对打分,词与词充分交互,判断「这段文字是否真的回答了这个问题」要准得多。代价是每个「查询—文档」对都要在线跑一次模型前向,伺候不了大候选集。
所以标准姿势是三段式:粗召回几百条 → Rerank 精排 → 只把 top-k 交给模型。
三段式流水线全景
graph LR
Q[查询] --> V[向量检索]
Q --> B[BM25 检索]
V --> F[RRF 融合]
B --> F
F --> C[粗排 top-N]
C --> R[Rerank 精排]
R --> K[top-k 给模型]
各段的分工与成本账:
| 阶段 | 职责 | 延迟量级 |
|---|---|---|
| 双路召回 | 别漏:把可能相关的都捞进来 | 毫秒级(索引现成) |
| RRF 融合 | 把两路名次并成一份 | 微秒级(纯计算) |
| Rerank 精排 | 别乱:最相关的顶到最前 | 几十到几百毫秒(在线跑模型) |
Rerank 的延迟随候选数线性增长,候选集要掐在几十条以内;「召回放宽、精排收窄」正是三段式的平衡术——检索侧宁可多召回,把质量判断交给更准也更贵的精排。
实操建议
- **先上混合检索,再决定要不要重排。**两路召回加 RRF 基本是配置级改动,性价比极高;Rerank 则引入在线模型推理的成本与延迟,先证明值得再上。
- 调参先看 badcase 属于哪一类:答案根本不在 top-k 里,是「漏了」——扩召回、查切块(见上一篇);答案在候选里但排得很靠后,是「乱了」——加 Rerank。先把坏例分桶,再对症下药。
要点小结:向量管语义、BM25 管字面,RRF 只比名次不比分数,Rerank 用交叉编码器把「真相关」顶上去。检索层理顺之后,怎么证明它真的变好了?下一篇讲 RAG 评测——检索层与生成层分开打分。
读者留言
COMMENTS 暂无还没有留言,来说第一句?