混合检索:BM25 + vectoriel
混合检索针对同一问题,并行执行关键词检索(BM25)和向量检索,然后融合两个排名,最常用的方法是倒数排名融合(Reciprocal Rank Fusion,RRF)。它弥补了嵌入检索失败的情况(标识符、专有名词、罕见术语),同时保留对不同措辞表达同一意思的理解能力。Qdrant、Weaviate 和 Elasticsearch 原生支持混合检索;使用 ChromaDB 时,可以用三十行 Python 代码构建这一功能。
向量数据库按语义检索,而非按字面匹配:像“RG/2024-117”这样的编号或工单号,它就可能找不到。反过来,关键词搜索无法理解“automobile”和“voiture”指的是同一种东西。本指南介绍如何结合这两种搜索、如何选择融合方法、Qdrant 和 Weaviate 各自能做什么,以及一个会在不知不觉中破坏 BM25 部分的法语分词陷阱。
#为什么纯向量检索会在某些问题上失效
向量搜索将每段文本转换为一个概括其整体含义的向量,然后返回向量与问题向量最接近的段落。这种压缩方式很擅长处理同义改写,却不擅长查找必须逐字匹配的内容:标识符、错误码、人名或内部缩写。在嵌入表示中,“PROD-4817”和“PROD-4871”很相似,因此无关工单可能排在正确工单之前。混合搜索弥补了这一缺陷:它增加一个基于词语精确出现情况的独立排序,再融合两种排序,让两种方法互相弥补盲点。
- 标识符与编号
- 工单号、档案编号、合同号、SKU、错误码:这些字符串无法可靠地保留在嵌入向量中,而只要它们出现在文本片段里,BM25 就能检索到。
- 专业领域罕见词汇
- 不常见的医学、法律或技术术语:罕见词在 BM25 中的权重很高,而其嵌入向量表达的含义可能较模糊。
- 非常短的查询
- 像“发票 2024”这样的两个词,能为生成嵌入提供的信息很少;关键词则直接按原样进行比较。
- 改写与重述
- 相反的情况是:“contrat à durée déterminée”(固定期限合同)与“CDD”,或“voiture”与“automobile”(两者均意为汽车),不包含任何共同词语;只有向量搜索才能将它们关联起来。
#BM25:关键词搜索能做什么
你的文档,你的 AI:基于你的 PDF、笔记和邮件的可靠本地 RAG——无需向云端发送任何内容。
- 在线空间,终身可用
- PDF + 文件
- 30 天内退款
BM25 是 Lucene、Elasticsearch 和 OpenSearch 等系统中使用的词法排序函数,SQLite 也在其 FTS5 模块中提供,其文档将 bm25() 函数描述为返回一个表示行与查询匹配质量的值。该函数基于三个原则:稀有词的权重高于常见词,当某个词出现次数超过一定阈值后,其重复出现的权重会逐渐降低,且较长的文本段相对于短段会受到轻微惩罚。参数 k1 用于控制这种饱和效应:根据 Elastic 的说明,它限制了查询中单个术语对文档得分的影响程度。
- 优势
- 罕见术语、标识符、短查询、专业词汇,无需加载或训练任何模型,索引紧凑,结果可解释(可以知道是哪个词让该段落出现在检索结果中)。
- 弱点
- 同义词、换一种说法、释义、拼写错误;如果不进行词形还原,“signé”(已签署)和“signature”(签名)会被视为两个不同的词。
- 前置条件
- 细致的分词处理:转为小写、去除重音符号,必要时进行词干提取。大多数自行编写的实现都在这一环节出错,详见下文。
#混合检索:基本原理
针对同一个问题执行两种检索,每种检索各自返回一份候选列表(20 至 50 个文本片段),然后将两份列表合并为一个统一的排名。效果提升源于一个简单的观察:同时出现在两份列表中的文本片段几乎总是相关的,而且每种方法还会找出另一种方法遗漏的片段。Weaviate 对混合检索的定义是:通过融合向量检索和关键词检索的两个结果集,将两者的结果结合起来,融合方法和相对权重均可配置。具体提升幅度取决于语料库和问题:任何通用数字都不可靠,必须在您自己的文档上进行测量(见下文)。
#无需归一化的融合:倒数排名融合(Reciprocal Rank Fusion)
常见误区是直接把原始分数相加。BM25分数是没有上限的正数;向量相似度分数则是距离或余弦值,处于有界区间内:这两种评分尺度无法直接比较,语料库哪怕发生微小变化,也会使它们偏移。倒数排名融合(Reciprocal Rank Fusion)只使用排名,从而绕过这一问题。Elasticsearch文档将其描述为一种无需调参、且各相关性指标之间无需存在关联的方法。
一个在 BM25 检索中排名第一、在向量检索中排名第五的文本段落,得分为 1/61 + 1/65,约为 0.0318;一个在两个列表中都排名第十五的段落,得分为 2/75,约为 0.0267。常数 k 会减弱第一名的优势:k 越大,靠后排名的相对影响就越大。Elasticsearch 文档将这个常数称为 rank_constant,默认值为 60;窗口大小 rank_window_size 则决定融合前每个列表的长度。文档指出,更大的窗口会提高相关性,但会牺牲性能。
#那么,基于分数的融合呢?
某些检索引擎提供另一种方案:先对每个列表的分数进行归一化,再按权重组合。Weaviate 文档介绍了两种方法:按排名排序和按相对分数融合,后者自 1.24 版本起成为默认方法;使用混合运算符的 autocut 功能时必须采用该方法。Qdrant 提供 RRF 和 DBSF,后者保留原始分数,但在组合前对其分布(均值和标准差)进行归一化。不了解分数分布时,按排名融合更稳健;能够进行测量时,按分数融合则支持更精细的调节。
#工具选择:原生支持混合检索的引擎
| 工具 | 原生混合模式 | 融合 | 权重调整 |
|---|---|---|---|
| Qdrant | 是的,可通过 Query API 实现(自 1.10 版本起可用) | RRF 或 DBSF | 较新版本支持按查询调整权重和常数 k |
| Weaviate | 是,使用 hybrid 运算符 | 排名或相对得分(自 1.24 版本起为默认方式) | 参数alpha:1表示纯向量,0表示纯关键词 |
| Elasticsearch | 是,使用 rrf 检索器 | RRF | rank_constant(默认值为60)和 rank_window_size |
| SQLite FTS5 + 向量扩展 | 待组装 | 需要编写 | 由您决定 |
| ChromaDB + rank_bm25 | 需用 Python 自行组合实现 | 需自行编写(用 6 行代码实现 RRF) | 由您决定 |
根本区别不在于融合本身——这只需几行代码就能实现——而在于索引:原生引擎会同步维护两个索引,使其保持最新,而自建方案将 BM25 索引保存在内存中,每次添加文档时都必须重建。对于只有几千个片段且很少变化的语料库,自建方案完全够用。规模再大一些,或者只要文档每天都在变化,原生引擎就能避免两个索引之间出现不一致。关于 Weaviate 的指南详细介绍了这个工具。
#自研实现:ChromaDB、rank_bm25 和 RRF
以下代码将三个模块组合起来。它修正了示例中一个常见的问题:规范化函数必须先去除变音符号,再进行过滤,否则每个带重音的字母都会把一个单词截成两部分。
有两点实用注意事项。首先,两个索引中的标识符必须一致:这里将列表下标转换为字符串,用作 Chroma 的标识符。其次,内存中的 BM25 索引会在程序停止时消失:请在启动时重新构建,处理数万段文本只需几秒钟;或者将文档列表保存在数据库旁边。
#调节 BM25 与向量检索之间的平衡
默认情况下,RRF 对两份列表赋予相同权重。如果您的语料库包含大量参考资料(判例、工单、目录),请提高 BM25 的权重;如果问题偏向对话式,则保持权重平衡或偏重向量检索。在前述函数中,只需传入 weights=[0.6, 0.4],就能为 BM25 分配 60% 的权重。Qdrant 也支持同样的设置:其文档指出,每条查询的默认权重为 1,此时得到的就是原始 RRF 公式;较新版本还允许调整常数 k。在 Weaviate 中,调整的是 alpha:1 表示纯向量检索,0 表示纯关键词检索。
| 语料库与问题 | BM25/向量初始权重 | 您所监控的内容 |
|---|---|---|
| 参考信息、编号、专有名称(法律、工单、目录) | 60 / 40 | 按标识查询的问题是否会优先返回? |
| 文字形式的文档,以自然语言提出的问题 | 40 / 60 | 换一种说法提问,能否检索到正确的段落? |
| 混合或未知语料库 | 50 / 50 | 在 30 至 50 个真实问题上评估召回率@5 |
| 专业术语和缩略语 | 55 / 45,并在 BM25 检索中使用同义词词典 | 使用缩写和完整名称,能否检索到同一段内容? |
#融合之后:添加重排序器
融合得到的候选集比任何一种方法单独得到的候选集都更多样。重排序器随后可以结合问题阅读每个段落,对这个候选集进行排序:这两种技术可以叠加使用。通常的顺序是先进行混合检索,再融合,然后对前 20 至 50 个结果进行重排序,最后将最好的 3 至 5 个段落放入提示词中。重排序器指南详细介绍了最后这一步,分块指南则解释了为什么段落大小对 BM25 和嵌入的影响都如此之大。
#关于混合搜索的常见问题
RAG 中的混合检索是什么?+
在将 BM25 和向量得分相加前,需要对它们进行归一化吗?+
在 RRF 中应选择多大的 k 值?+
可以使用 ChromaDB 实现混合检索吗?+
混合搜索是否会显著降低查询速度?+
如何判断混合检索对我的文档是否值得采用?+
- 来源:Qdrant,混合查询
- 来源:Weaviate,混合检索技术
- 来源:Elasticsearch,倒数排名融合(Reciprocal Rank Fusion)
- 来源:SQLite FTS5,bm25() 函数
有反馈、发现了错误,或想补充说明?请告诉我们,让这份指南对每个人都更有帮助。