当前位置: 首页 > news >正文

RAG 系列(十):混合检索——让召回更全面

向量检索的一个盲区

假设你的知识库里有一篇文档,内容包含这样一句话:

“中文场景推荐使用BAAI/bge-large-zh-v1.5,向量维度为 1024。”

用户问:“BAAI/bge-large-zh-v1.5 的向量维度是多少?”

你以为这是送分题——完全一样的词,向量检索应该能轻松找到。

实际上不一定。向量检索依赖语义相似度,当查询和文档的用词高度重叠时,它并不比 BM25 更有优势,有时甚至更差。BM25 算法是专门为精确词频匹配设计的,处理这类问题是它的主场。

真正的问题是:你的 RAG 系统一定会同时遇到两类查询

  • 关键词查询:包含精确的型号、参数、公式、人名——“BAAI/bge-large-zh-v1.5 维度”
  • 语义查询:换了一种说法的概念性问题——“AI 助手总是给出过时答案,怎么解决”

纯向量检索擅长后者,但对前者力不从心。纯 BM25 恰好相反。

混合检索(Hybrid Search)的思路很简单:两个都用,再融合结果。


BM25 原理速览

BM25(Best Match 25)是搜索引擎领域的经典排名算法,Elasticsearch、Lucene 都在用它。

核心公式:

score(D, Q) = Σ IDF(qi) × (f(qi, D) × (k1 + 1)) / (f(qi, D) + k1 × (1 - b + b × |D|/avgdl))

人话版本:

  • IDF(逆文档频率):一个词在所有文档里越罕见,它在匹配时越有价值。"的"不值钱,“BGE-large-zh-v1.5” 非常值钱。
  • TF(词频):这个词在文档中出现越多,分数越高,但收益递减。
  • 文档长度惩罚:长文档不因词数多而自动获得高分。

BM25 的优势:完全基于词汇,查询词和文档词只要有重叠,就能精准命中。精确型号、产品名、函数名——这是它的主场。

BM25 的劣势:不理解语义。"知识截止问题"和"AI 不知道最新信息"在 BM25 看来毫无关系,尽管它们说的是同一件事。


RRF 融合算法

有了 BM25 和向量检索两份结果,怎么合并?

最简单的思路是把两个分数加权平均,但两种算法的分数尺度完全不同,直接相加没有意义。

RRF(Reciprocal Rank Fusion)的做法更优雅:只看排名,不看分数

公式:

RRF_score(d) = Σ 1 / (k + rank(d))
  • rank(d):文档 d 在某个检索器中的排名(第 1 名、第 2 名…)
  • k:常数,通常取 60,防止最高排名的文档独占分数
  • 对每个检索器的排名求和

举例

文档BM25 排名Vector 排名RRF 分数(k=60)
doc-006131/(60+1) + 1/(60+3) = 0.0164 + 0.0159 =0.0323
doc-003311/(60+3) + 1/(60+1) =0.0323
doc-002241/(60+2) + 1/(60+4) = 0.0161 + 0.0156 =0.0317

RRF 的好处:无论两个检索器的分数范围差多少,都能公平地基于排名融合,不需要手动对齐分数。


实验设计

6 条测试查询,覆盖两种场景:

类型查询期望文档测试点
关键词BAAI/bge-large-zh-v1.5 维度doc-003精确模型名匹配
关键词RRF score sum 1/(k+rank) 公式doc-006精确公式字符串
关键词chunk_size 256 1024 overlap 推荐doc-004精确参数值
语义AI 助手总是给出过时的答案,有什么方法让它了解最新信息doc-001没提 RAG
语义多个团队共用一套问答系统,怎么保证不同团队的资料互相看不到doc-008没提多租户
语义换一种问法,检索结果就完全不同,怎么解决这种不稳定性doc-007没提 Multi-Query

评估指标:MRR(Mean Reciprocal Rank)

RR = 1/rank(正确文档排在第几位) MRR = 所有查询的 RR 均值
  • 每次都排第一 → MRR = 1.0
  • 平均排第二 → MRR = 0.5
  • 全部未命中 → MRR = 0.0

三种检索器实现

BM25 检索器

中文要先做分词,用 jieba:

importjiebafromlangchain_community.retrieversimportBM25Retrieverdefchinese_tokenizer(text:str)->list[str]:returnlist(jieba.cut(text))bm25_retriever=BM25Retriever.from_documents(docs,k=3,preprocess_func=chinese_tokenizer,)

向量检索器

fromlangchain_chromaimportChromafromlangchain_openaiimportOpenAIEmbeddings embeddings=OpenAIEmbeddings(model="BAAI/bge-large-zh-v1.5",api_key=os.getenv("EMBEDDING_API_KEY"),base_url="https://api.siliconflow.cn/v1",)vectorstore=Chroma.from_documents(docs,embedding=embeddings)vector_retriever=vectorstore.as_retriever(search_kwargs={"k":3})

混合检索器(EnsembleRetriever + RRF)

fromlangchain_classic.retrieversimportEnsembleRetriever hybrid_retriever=EnsembleRetriever(retrievers=[bm25_retriever,vector_retriever],weights=[0.5,0.5],# 两者权重相同,内部用 RRF 融合排名)

EnsembleRetrieverweights参数控制的是各检索器在 RRF 中的权重,不是直接加权分数。实际实现里它会对每个检索器的结果排名做加权 RRF 融合。


实验结果

====================================================================== 逐条查询结果 (RR = Reciprocal Rank;Hit@1 = 正确文档是否排第一) ====================================================================== [KEYWORD ] BAAI/bge-large-zh-v1.5 维度 期望文档: doc-003 BM25 [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-003', 'doc-006', 'doc-004'] Vector [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-003', 'doc-005', 'doc-002'] Hybrid [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-003', 'doc-006', 'doc-004'] [KEYWORD ] RRF score sum 1/(k+rank) 公式 期望文档: doc-006 BM25 [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-006', 'doc-002', 'doc-004'] Vector [H@1=✗] RR=0.50 | rank=2 | 召回: ['doc-004', 'doc-006', 'doc-003'] Hybrid [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-006', 'doc-004', 'doc-003'] [KEYWORD ] chunk_size 256 1024 overlap 推荐 期望文档: doc-004 BM25 [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-004', 'doc-003', 'doc-006'] Vector [H@1=✗] RR=0.50 | rank=2 | 召回: ['doc-006', 'doc-004', 'doc-003'] Hybrid [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-004', 'doc-006', 'doc-003'] [SEMANTIC] AI 助手总是给出过时的答案,有什么方法让它了解最新信息 期望文档: doc-001 BM25 [H@1=✗] RR=0.33 | rank=3 | 召回: ['doc-007', 'doc-005', 'doc-001'] Vector [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-001', 'doc-005', 'doc-007'] Hybrid [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-001', 'doc-007', 'doc-005'] [SEMANTIC] 多个团队共用一套问答系统,怎么保证不同团队的资料互相看不到 期望文档: doc-008 BM25 [H@1=✗] RR=0.33 | rank=3 | 召回: ['doc-002', 'doc-007', 'doc-008'] Vector [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-008', 'doc-001', 'doc-002'] Hybrid [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-008', 'doc-002', 'doc-007'] [SEMANTIC] 换一种问法,检索结果就完全不同,怎么解决这种不稳定性 期望文档: doc-007 BM25 [H@1=✗] RR=0.00 | rank=miss | 召回: ['doc-005', 'doc-001', 'doc-003'] Vector [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-007', 'doc-001', 'doc-005'] Hybrid [H@1=✓] RR=1.00 | rank=1 | 召回: ['doc-007', 'doc-001', 'doc-005']

MRR 汇总:

====================================================================== MRR 汇总对比 MRR=1.0 → 每次都排第一;MRR=0.5 → 平均排第二;MRR=0.0 → 全未命中 ====================================================================== 查询类型 BM25 Vector Hybrid 最佳 ──────────────────────────────────────────────────────── 关键词查询 1.000 0.667 1.000 BM25 语义查询 0.222 1.000 1.000 Vector 总体 0.611 0.833 1.000 Hybrid ====================================================================== 结论: ✓ 关键词查询:BM25 MRR 更高(精确词匹配优势) ✓ 语义查询:Vector MRR 更高(语义理解优势) ✓ 混合检索:总体 MRR 最高,兼顾两类查询

数字解读

  • BM25 在关键词查询上达到满分 1.000,但在语义查询上只有 0.222——第三条语义查询(“换一种问法”)完全 miss,排名都没有进前三。
  • 向量检索在语义查询上完美(1.000),但在关键词查询上只有 0.667——有两条 RRF 公式和 chunk_size 的查询排到了第二名而非第一。
  • 混合检索全类型满分 1.000,不仅继承了 BM25 的关键词优势,语义查询也不弱于纯向量。

关键认知:BM25 和向量检索的边界

维度BM25向量检索
擅长精确词匹配(型号、公式、参数)语义理解(同义词、换一种说法)
失效场景查询和文档用词不同精确术语的向量表示不够区分性
典型查询“BERT-base-uncased 层数”“为什么预训练模型需要微调”
适合语言英文效果更好(中文需分词)中英文均可
计算成本低(无需 GPU,无 API 调用)较高(需要 Embedding 调用)

什么时候一定要上混合检索:

  • 知识库里包含产品型号、API 名、参数名、缩写等精确术语
  • 用户查询行为多样(技术用户问精确术语,普通用户问概念)
  • 要求高召回率,不能漏掉任何相关文档

什么时候可以只用向量:

  • 知识库全是自然语言文本,没有精确术语
  • 查询都是语义性的概念问题
  • 资源有限,不想引入额外依赖

完整代码

代码已开源:

https://github.com/chendongqi/llm-in-action/tree/main/10-hybrid-search

核心文件:

  • hybrid_search.py— 三种检索策略的完整对比实验

运行方式:

gitclone https://github.com/chendongqi/llm-in-actioncd10-hybrid-searchcp.env.example .env# 填入 Embedding API Keypipinstall-rrequirements.txt python hybrid_search.py

小结

本文通过代码实验对比了三种检索策略:

  1. 纯 BM25——关键词精确匹配的专家,精确术语场景无敌,但不懂语义
  2. 纯向量检索——语义理解的专家,概念性问法场景强,但精确术语不如 BM25
  3. 混合检索(RRF)——两者融合,MRR 全场景最高

RRF 算法的核心思路值得记住:不比分数,只比排名。这使它能够无缝融合任何两个评分体系完全不同的检索器。

生产环境中,混合检索已经是 RAG 系统的标配。Elasticsearch、Qdrant、Weaviate 都原生支持混合检索模式——向量检索+BM25 不再是可选项,而是默认推荐配置。


参考资料

  • LangChain EnsembleRetriever 文档
  • BM25 算法论文:Okapi BM25
  • RRF 论文:Reciprocal Rank Fusion
  • Qdrant 混合检索文档
http://www.jsqmd.com/news/782063/

相关文章:

  • 哔哩下载姬Downkyi:5步掌握高效B站视频管理方案
  • 抖音内容批量下载终极指南:高效保存无水印视频与直播回放
  • 2026靠谱降AI工具怎么选?实测15款后这几个最实用
  • 哔哩下载姬DownKyi:从零开始轻松下载B站8K超高清视频的完整教程
  • Aurora开源项目:基于Vite+React+TS的现代化前端开发脚手架深度解析
  • 西安外国语大学考研辅导班推荐:排行榜单与选哪家好评测 - michalwang
  • 从零构建命令行TODO管理器:Python实现与开发者工作流集成
  • OpenClaw Monitor 3D:AI智能体3D可视化监控平台设计与实现
  • 基于Groq LPU与Llama 3.1的极速AI聊天工具全解析
  • 自建游戏串流服务器:Sunshine完整部署与优化指南
  • ORCAD原理图整洁秘诀:用属性过滤器隐藏杂乱信息,让你的设计界面清爽10倍
  • FlexServe:安全高效的边缘LLM推理系统架构解析
  • 终极Windows离线语音识别工具:TMSpeech实时字幕完全指南
  • RV1106芯片开发踩坑实录:SPI NAND烧录那些“反直觉”的操作与原理
  • 云原生匿名网络:Kubernetes Operator 实现 Tor 节点与洋葱服务自动化管理
  • 别再被拒了!手把手教你搞定uni-app上架华为/小米/OPPO的隐私合规(附完整配置代码)
  • 从培根到物联网:技术会议策划中的沟通艺术与需求引导
  • 基于HindClaw构建企业级AI智能体记忆管理平台
  • 别把 `SFT`、`DPO`、`RLHF`、`GRPO` 当成后训练四连跳:真正先决定路线的,是数据形状、参考模型和在线采样
  • 普阳兴五金,创新能力强的五金模具钢加工厂排名靠前 - myqiye
  • GPU并行非线性最优控制框架解析与实现
  • 2025苏州门窗行业:解读三大核心发展趋势 - 速递信息
  • 开发者必备:命令行TODO管理工具的设计原理与实战应用
  • 5分钟学会用Mermaid Live Editor:告别拖拽式图表工具的终极指南
  • WeChatExporter:如何零成本导出iOS微信聊天记录?
  • 如何为老旧Android设备打造终极电视直播体验:MyTV-Android的5大核心技术突破
  • 尚泰净化板价格高吗? - mypinpai
  • 佛山铝单板哪家技术强 - 品牌企业推荐师(官方)
  • 雷达液位计国产品牌十大排名:哪些厂家实现高频雷达进口替代? - 陈工日常
  • AArch64指针认证机制与QARMA算法解析