Hybrid 检索混合的是字面召回和语义召回两套能力,而不是简单相加两个分数;RRF 和 rerank 各有清晰位置。>
阅读时间:约 6 分钟
很多 RAG 方案都会写"Hybrid = BM25 + 向量 + rerank"。这句话没错,但太短了,短到容易把三件事混成一团。
真正需要问的是:
- BM25 在哪一步工作
- 向量索引在找什么
- 两路分数怎么合
- rerank 为什么不能直接跑全库
把这些问题放进一条检索漏斗里,Hybrid 就清楚了。
图:Hybrid 检索漏斗
先把检索漏斗放正
一次 query 进入 RAG 系统后,通常不会直接交给大模型。它先经过检索层,把知识库里可能相关的片段筛出来。
Hybrid 的完整路径可以压成这张图:
这条链路里有三个容易混淆的位置:
- BM25 是字面召回内部的排序算法
- RRF 负责把两路召回结果合成一个候选列表
- rerank 在漏斗尾部精排,不参与全库召回
所以,"检索"不是单个动作。它是一组接力:先尽量召回,再合并候选,最后用更贵的模型重新排序。
字面路线负责找"词对得上"的内容
字面检索解决的问题很具体:用户输入几个词,系统要快速找到包含这些词的文档或片段,并排出相关性。
它的流水线通常是这样:
分词先解决边界问题。英文天然有空格,中文没有。"中文分词"可以切成 中文 / 文分 / 分词,这类 bigram 切法不依赖词典,覆盖率高,但会产生 文分 这种没有语义的词元。
分词必须和索引对齐
常见切法的取舍如下:
| 方法 | 切法例子 | 主要取舍 |
|---|---|---|
| unigram | 中 / 文 / 分 / 词 | 几乎不漏,误匹配多 |
| bigram | 中文 / 文分 / 分词 | 不依赖词典,索引更大 |
| 词典分词 | 中文 / 分词 | 更贴语义,需要维护词典 |
倒排索引让查询避开全库扫描
倒排索引解决速度问题。普通正排结构是"文档 -> 词",查的时候要扫文档。倒排结构改成"词 -> 文档列表":
"中文" -> [文档 1, 文档 5, 文档 8]
"分词" -> [文档 1, 文档 8, 文档 20]
查"中文分词"时,只要取两个列表的交集,就能直接拿到文档 1 和文档 8。SQLite FTS5、Lucene、Elasticsearch 都建立在这个基本结构之上,区别主要在工程规模和功能完整度。
BM25 只接手命中后的排序
BM25 接在倒排索引后面。它不负责"找出哪些文档命中",只负责"命中的文档谁更靠前"。
BM25 的排序直觉有三条:
- 词频越高,通常越相关,但会有饱和上限
- 越稀有的词权重越高,常见虚词权重低
- 长文档天然更容易命中,需要做长度归一化
覆盖率过滤负责拦掉低命中噪声
最后还会加覆盖率过滤。用户搜"中文分词算法原理",某篇文档只命中"原理",BM25 也可能给它一个低分。覆盖率过滤会看 query 词元命中了多少比例,低于阈值就直接丢掉。
这一整套路线很稳定,尤其擅长专名、型号、错误码和罕见术语。它的短板也明显:用户换一种说法,字面没重合,它就容易漏。
语义路线负责找"意思对得上"的内容
语义检索处理的是另一个问题:query 和文档没有相同词,但表达的是同一个意思。
例如用户搜"怎么让搜索更准",知识库里写的是"检索质量优化"。字面路线可能抓不到,语义路线有机会命中。
它的链路通常这样走:
chunk 是先被迫出现的。长文档直接做 embedding 有两个问题:
- embedding 模型有输入长度上限
- 一篇长文可能同时讲多个主题,压成一个向量会稀释语义
RAG 还要把检索结果塞回 LLM 上下文窗口。窗口有限,系统只能投喂小段内容。所以语义检索通常先切 chunk,再对每个 chunk 建向量。
embedding 把文本压成一串数字,也就是语义坐标。query 和文档片段都用同一个模型编码,坐标越近,语义越接近。
这里用的是 Bi-Encoder 思路:query 和文档分开编码。文档向量可以提前算好,在线查询时只需要算 query 向量,再去库里找最近邻。
向量索引用 ANN(Approximate Nearest Neighbor,近似最近邻)解决速度问题。精确做法是把 query 向量和库里每个向量都算一遍距离,结果最准,但库大以后太慢。ANN 用近似路径快速找一批大概率接近的向量,可能漏掉少数最佳结果,换来查询速度。
语义路线的排序依据通常是余弦相似度。它在这条路线里的位置,和 BM25 在字面路线里的位置相似:候选拿到以后,用一个分数排先后。
Hybrid 的关键动作是融合名次
字面路线和语义路线互补:
| 路线 | 强项 | 盲区 |
|---|---|---|
| 字面检索 | 专名、型号、错误码、生僻术语 | 同义改写和意图表达 |
| 语义检索 | 同义表达、自然语言问题、概念相近内容 | 精确词和罕见实体 |
Hybrid 的做法直接:两路都跑,各自给出候选,然后合并。
问题在合并环节。BM25 分数和向量相似度不是同一种量纲,直接相加没有意义。
常用方法是 RRF(Reciprocal Rank Fusion,倒数排名融合)。它不看原始分数,只看同一个 chunk 在两路列表里的名次。
排名越靠前,贡献越大。同一个 chunk 在两路都靠前,就更容易进入最终候选。
这也是为什么很多系统会让两路检索基于同一批 chunk 建索引。只有候选对象一致,RRF 才能把两个列表里的名次合到一起。
rerank 在最后一段重新判相关性
融合后的候选仍然偏粗。rerank 的职责,是在更小的候选集合上重新判断 query 和文档片段的相关性。
它常用 Cross-Encoder:把 query 和 chunk 拼在一起送进模型,现场算一个相关性分。
Cross-Encoder 比 Bi-Encoder 更准,因为模型能同时看见 query 和候选内容,直接比较二者关系。代价也很清楚:
- 文档不能提前离线编码成可复用向量
- 每个候选都要在线重算
- 成本高,不能对全库逐条跑
所以 rerank 只能放在漏斗末端。前面两路召回负责"快而全",它负责"慢而准"。
这也解释了一个常见困惑:BM25 是排序,rerank 也是排序,二者是否重复?
不重复。排序在不同阶段反复发生:
- BM25 在字面召回内部粗排
- 向量相似度在语义召回内部粗排
- RRF 按两路名次合并
- rerank 对融合候选精排
它们处理的候选范围不同,使用的判断依据也不同。
索引只解决速度,不直接等于质量
理解 Hybrid 前,还要把"算法"和"索引"拆开。
BM25 是打分算法,倒排索引是加速结构。理论上,你可以不用倒排索引,改用全表扫描找出含词文档,再套 BM25 公式打分。结果不变,只是慢得多。
向量路线也一样。余弦相似度是打分方式,ANN 是加速结构。
你可以不用 ANN,改成暴力 KNN,对每个向量逐条计算相似度。结果更精确,但大规模场景跑不动。
对照关系如下:
| 环节 | 字面路线 | 语义路线 |
|---|---|---|
| 打分算法 | BM25 / TF-IDF | 余弦相似度 |
| 加速索引 | 倒排索引 | ANN,如 HNSW / IVF |
| 不用索引时 | 全表扫描或 grep 式匹配 | 暴力 KNN |
| 代价 | 结果不变,速度下降 | 速度下降,精度通常更高 |
索引换来的主要是速度和可扩展性。ANN 甚至会为了速度牺牲一点精度。
这个区分能避免两个误读:把 BM25 等同于倒排索引,把向量检索等同于向量库。算法决定怎么打分,索引决定怎么更快找到候选。
离线建库决定两路能不能并起来
线上 query 能不能顺利走 Hybrid,取决于离线阶段有没有把同一批内容准备成两套索引。
常见建库流程如下:
原始文件(PDF / Word / HTML)
-> 解析抽取纯文本
-> 清理格式、图片和无关排版
-> 切成 chunk
-> 对同一批 chunk 建两套索引:
1. 分词后写入倒排索引,供 BM25 使用
2. 通过 embedding 生成向量,写入向量索引
所以,支持 Hybrid 需要系统主动维护倒排索引。它不是"向量库顺手送了关键词检索"。
这件事有实际成本:建库时间、额外存储、两路查询和融合计算。
Weaviate、Qdrant、Milvus、Elasticsearch 等系统提供 Hybrid 能力,本质也是帮你在一份数据上维护两套检索结构。
如果系统只做传统全文检索,而且结果直接给人看,不喂给 LLM,也不需要 embedding,那么可以直接对整篇原文建倒排索引。chunk 不是 BM25 的硬要求,它主要来自向量模型长度和 LLM 上下文窗口限制。
一旦 RAG 决定按 chunk 工作,BM25 也应该在同一批 chunk 上建索引。这样字面候选和语义候选才有共同对象,后面的 RRF、rerank 和 Top-K 才能接起来。
最后把几个词钉住
retrieval 和 recall 在中文里常被一起叫"召回",但它们不是同一层东西。
- 召回(retrieval)是流水线阶段:用索引找候选,再做粗排
- 召回率(recall)是评估指标:相关结果里有多少被找出来了
BM25 和向量检索都属于召回器。rerank 在召回后面,它可以提高最终 Top-K 的相关性,也可能改变召回率指标的表现,但它不是第一段召回动作本身。
把这几个位置摆正以后,Hybrid 的定义就不再含糊:
Hybrid = 字面召回 + 语义召回 + RRF 融合 + rerank 精排 + Top-K 投喂
它混合的是两套互补的召回能力。BM25 兜住精确词,向量兜住语义相近,rerank 再用更贵的模型做最后判断。
RAG 检索的质量,往往就卡在这些边界上:该快的地方要快,该准的地方再花钱,别把一个环节误当成整条链路。
推荐阅读
DeepSeek Harness 的 Agent 运行时设计
长任务 Coding Agent 的关键不是写代码,而是交付链路