混合检索
向量检索漏掉精确词,关键词检索漏掉语义。把两者合起来,是基础检索管线性价比最高的一次升级。
查询: 怎么修复上传器里的 E4021 错误
- 排查上传失败语义贴近「修复 … 上传器」
- 解决常见上传问题语义相关
向量检索能找到语义——却漏掉那两篇唯一线索是精确串 E4021 的文档。罕见的标识符几乎没有可供 embedding 的语义内容。
两种检索方法,出错的方向正好相反。
向量检索能抓到语义,抓不到精确字符串。你查 E4021,它返回一堆泛泛讲错误处理的文档,因为这种罕见的标识符会被切成子词片段,几乎不带语义。
关键词检索能抓到精确字符串,抓不到语义。你查「how do I stop my app crashing」,它不会把标题为「Handling uncaught exceptions」的文档翻出来——一个词都不重合。
两个都跑再合并,是对基础 RAG 管线性价比最高的一次改进,而且实现起来比大多数替代方案都便宜。
各自擅长什么
向量检索在改写、同义词、概念相似和跨语言匹配上更强。
关键词检索在标识符、错误码、函数名、产品 SKU、专有名词、原文引语和冷门术语上更强。凡是出现得太少、以至于在 embedding 模型的训练里没能形成有意义表示的东西,它也更擅长。
后一份清单不是边角情况。在技术文档和客服语料里,真实查询中有很大一部分恰好包含这些元素。
BM25
关键词这一半几乎都用 BM25,而不是朴素的词匹配。三个想法,都站得住:
**词频,但收益递减。**一个词出现十次的文档比出现一次的相关,但不是相关十倍。BM25 让这份贡献饱和。
**逆文档频率。**每个文档里都有的词说明不了任何问题;稀有的词信息量很大。命中稀有词,得分高得多。
**长度归一化。**长文档天然词多,原始计数会不公平地偏袒它。BM25 做了调整。
几十年前的东西,极快,至今仍有竞争力。它不是个遗留的兜底方案——在它擅长的事情上,它确实比 embedding 好。
合并结果
两种方法给出的分数不在同一个尺度上。BM25 的 14.2 和余弦相似度的 0.83 加在一起没有意义,而把它们归一化也很脆,因为 BM25 没有固定上界。
**倒数排名融合(reciprocal rank fusion)**绕开了整个问题,它用排名而不是分数。每个文档的得分,是它在两个结果列表里各自 1 / (k + rank) 之和,k 是一个小常数。一个在向量里排第 1、在关键词里排第 3 的文档,胜过一个在两路里都排第 8 的文档。
它管用,是因为排名之间永远可比,不需要调参,而且不管两种方法产出什么尺度,它都稳。这是标准做法,也是正确的默认值。
加权分数融合把分数归一化后按一个可调的权重相加。它让你能控制向量与关键词的配比,代价是需要调这个权重,而且对分数分布的漂移很敏感。
先用倒数排名融合。
它在管线里的位置
查询
→ 向量检索 → 前 50
→ BM25 检索 → 前 50
→ 倒数排名融合 → 合并后前 50
→ 重排序 → 前 5
→ 生成混合检索和重排序解决的是不同问题,两者可以叠加。混合检索提升的是召回率——把正确的文档先弄进候选集。重排序提升的是精确率——把这个候选集排对顺序。
两个都做之所以是标配,有它的道理:混合检索找到单靠向量会漏掉的东西,重排序再把融合后只是大致有序的候选集理顺。
实操要点
**大多数向量数据库原生就支持。**自己动手之前先查一下——融合逻辑通常已经内置了。
**PostgreSQL 也能干得很漂亮。**向量用 pgvector,全文检索用内置的,一条查询里 join 起来。语料在几百万个文本块以内,这往往就是全部答案,不需要另起系统。
**同一批文本块,两边都建索引。**同样的分块边界,同样的标识,融合才能拿同类的东西互相比。
**两边都多召回一些。**融合前各取 50 是个合理的默认值。两种方法都没捞出来的文档,融合也救不回来。
**分开度量。**记录正确的文本块是出现在向量结果里、关键词结果里,还是两边都出现。这能告诉你,在你的语料上干活的是哪一半——而且经常不是你预期的那个。
要点回顾
- 向量和关键词检索的失效方向正好相反;标识符和冷门术语是向量掉链子的地方。
- BM25 负责关键词这一半:饱和的词频、逆文档频率、长度归一化。
- 倒数排名融合按排名而不是分数合并,避开尺度不可比的问题——把它当默认做法。
- 混合检索提升召回率,重排序提升精确率;两个都用。
- 量一量正确的文本块是哪一半召回的——答案常常出人意料。
下一篇: 查询改写与扩展