混合搜索与重排序实战:让RAG的召回精度再上一个台阶
文章目录
- 前言
- 一、为什么纯向量搜索不够用?
- 1.1 向量的盲区:精确匹配
- 1.2 两套系统,各有所长
- 二、混合搜索:并行检索,融合排序
- 2.1 工作流程
- 2.2 怎么融合?——两种主流策略
- 策略一:RRF(互惠排名融合)
- 策略二:加权融合(WeightedRanker)
- 2.3 代码实战:PostgreSQL中实现混合搜索+RRF
- 三、重排序:用交叉编码器做最后一公里精排
- 3.1 为什么需要重排序?
- 3.2 两阶段检索标准流程
- 3.3 主流重排序模型
- 3.4 代码实战:Azure HorizonDB中实现混合搜索+重排序
- 3.5 什么时候该用重排序,什么时候该跳过?
- 四、性能优化要点
- 4.1 控制候选集大小
- 4.2 混合搜索的参数调优
- 4.3 RRF的`k`值调优
- 五、写在最后
前言
前面的文章我们聊了向量数据库的索引调优和高并发架构,把“快”的问题解决了。但生产环境中,光快不够,还得准。
你有没有遇到过这种情况:用向量数据库搜“无线降噪耳机”,返回的Top 1是一篇讲“蓝牙音箱”的文档,因为它们的向量距离确实很近。但如果你想要的是“头戴式降噪耳机”呢?纯向量搜索就有点力不从心了。
这就引出了今天要讲的两大杀器:混合搜索(Hybrid Search)和重排序(Re-ranking)。前者解决“查全”的问题,后者解决“排准”的问题。两者配合,才能让RAG系统的召回质量达到生产级要求。
一、为什么纯向量搜索不够用?
1.1 向量的盲区:精确匹配
向量搜索擅长理解“语义”,但它有个致命弱点——对精确匹配不敏感。
举个例子:
- 用户搜
"ABC-123"(产品型号) - 向量模型可能把
"ABC-123"和"ABC-124"映射到相近的位置,因为它们在语料中经常同时出现 - 但用户要的就是完全匹配
"ABC-123"
这种情况下,关键词搜索(BM25)的表现远超向量搜索。
1.2 两套系统,各有所长
| 检索方式 | 擅长什么 | 不擅长什么 |
|---|---|---|
| 向量搜索(密集检索) | 语义理解、同义词泛化、模糊匹配 | 精确匹配、专有名词、产品ID |
| 关键词搜索(BM25/稀疏检索) | 精确匹配、术语识别、罕见词 | 语义泛化、同义词理解 |
结论:单靠任何一种,都会丢失另一半的召回能力。混合搜索就是让两者并行跑,然后把结果合并——既保语义泛化,又保精确匹配。
二、混合搜索:并行检索,融合排序
2.1 工作流程
用户查询 ↓ ├──────────┬──────────┐ ↓ ↓ ↓ 向量搜索 关键词搜索 [其他检索] (语义) (BM25) (如元数据过滤) ↓ ↓ ↓ 结果集A 结果集B 结果集C ↓ ↓ ↓ └──────────┴──────────┘ ↓ 【融合排序器】 (RRF / 加权平均) ↓ 统一结果集 ↓ 【重排序模型】 (交叉编码器) ↓ 最终Top-K混合搜索的本质是:并行执行多个检索路径,然后用融合算法把多份排名列表合并成一份。
2.2 怎么融合?——两种主流策略
策略一:RRF(互惠排名融合)
RRF是目前最主流的混合搜索融合算法,被Milvus、Azure AI Search、OpenSearch等主流产品采用。
核心思想:不看原始得分(因为向量和BM25的打分尺度完全不一样),只看排名位置。
公式:
RRF_score(d) = Σ 1 / (k + rank_i(d))其中:
rank_i(d)是文档d在第i个检索路径中的排名(从1开始)k是平滑参数,默认60,可调范围10~100
一个具体例子:
假设有两条检索路径:
| ID | 向量搜索排名 | 关键词搜索排名 |
|---|---|---|
| 101 | 1 | 2 |
| 198 | 4 | 1 |
| 175 | 5 | 4 |
| 203 | 2 | 未召回 |
| 110 | 未召回 | 3 |
用RRF(k=60)计算融合得分:
- ID 101:
1/(60+1) + 1/(60+2) = 0.01639→ 排名第1 - ID 198:
1/(60+4) + 1/(60+1) = 0.01593→ 排名第2 - ID 203:
1/(60+2) = 0.01613→ 排名第3
结果分析:ID 101在两个检索中都排在前列,所以最终排第一。ID 203虽然在向量搜索中排第2,但关键词没召回,所以掉到第3。RRF的核心优势是“平衡”——不偏向任何一种检索方式,只有两边都表现好的文档才能排在最前面。
策略二:加权融合(WeightedRanker)
如果你明确知道某一路径更重要,可以用加权平均。
流程:
- 各路径独立检索,得到原始分
- 归一化:把各路径得分映射到
[0,1]区间(Milvus用arctan函数实现) - 加权平均:
final_score = Σ w_i × normalized_score_i - 按最终得分排序
适用场景:多模态搜索中,文字描述可能比图片特征更重要,可以给文字路径更高的权重。
2.3 代码实战:PostgreSQL中实现混合搜索+RRF
以下是用PostgreSQL + pgvector + 全文搜索实现混合搜索的SQL示例:
WITH-- 1. 语义搜索:按向量相似度排名semantic_searchAS(SELECTid,ROW_NUMBER()OVER(ORDERBYembedding<=>'[0.1, -0.2, ...]'::vector)asrankFROMdocumentsORDERBYembedding<=>'[0.1, -0.2, ...]'::vectorLIMIT50),-- 2. 关键词搜索:按文本相关性排名(BM25/ts_rank)keyword_searchAS(SELECTid,ROW_NUMBER()OVER(ORDERBYts_rank_cd(to_tsvector('english',content),query)DESC)asrankFROMdocuments,plainto_tsquery('english','your search terms')queryWHEREto_tsvector('english',content)@@ queryLIMIT50),-- 3. RRF融合:1/(60+rank)combined_resultsAS(SELECTid,1.0/(60+s.rank)asscoreFROMsemantic_search sUNIONALLSELECTid,1.0/(60+k.rank)asscoreFROMkeyword_search k)-- 4. 最终排序:对同时在两个列表中出现的文档累加得分SELECTd.id,d.content,SUM(c.score)asfinal_rrf_scoreFROMcombined_results cJOINdocuments dONc.id=d.idGROUPBYd.id,d.contentORDERBYfinal_rrf_scoreDESCLIMIT10;关键点:
<=>是pgvector的余弦距离运算符@@是全文搜索的匹配运算符- 在数据库层完成融合,只返回最终10条,减少网络传输和中间层逻辑
三、重排序:用交叉编码器做最后一公里精排
混合搜索解决了“召回”问题,但RRF融合后的排名还不一定最优。重排序(Re-ranking)是在混合搜索结果之上再加一层精排,用更精确但更慢的模型,只对候选集(如Top 50~100)重新打分。
3.1 为什么需要重排序?
向量搜索用的是双编码器(Bi-Encoder):查询和文档分别编码成向量,然后算距离。这种方式快,但粗糙——查询和文档在编码时互不“看见”对方,丢失了词级别的交互信息。
重排序用的是交叉编码器(Cross-Encoder):把查询和文档拼在一起作为一个输入,让模型同时“看见”两者,做词级别的交叉注意力计算。
| 对比维度 | 双编码器(向量搜索) | 交叉编码器(重排序) |
|---|---|---|
| 速度 | 快(可预计算索引) | 慢(每次查询实时计算) |
| 精度 | 中等 | 高 |
| 适用阶段 | 第一轮召回(百万→百) | 第二轮精排(百→十) |
| 典型模型 | BGE、OpenAI v3 | Cohere-rerank、BGE-reranker |
一句话:向量搜索是“海选”,重排序是“决赛”。
3.2 两阶段检索标准流程
用户查询 ↓ 【第一阶段】混合搜索(向量+BM25) 从百万级数据中召回 Top 50~100 ↓ 【第二阶段】交叉编码器重排序 对 50~100 个候选逐个打分,选出 Top 5~10 ↓ 【第三阶段】喂给大模型(LLM)生成回答为什么不能直接用交叉编码器检索全量?
因为交叉编码器每次推理都要把查询和每个文档拼接后过一遍模型,100万条数据就是100万次推理,单次查询可能耗时数秒到数分钟,完全不现实。
3.3 主流重排序模型
| 模型 | 特点 | 适用场景 |
|---|---|---|
| Cohere Rerank | 企业级,多语言支持,v3版速度快 | 生产环境RAG |
| BGE-reranker | 开源,中文友好,精度高 | 本地部署,中文场景 |
| MixedBread AI | 轻量,与LanceDB集成 | 原型开发 |
| Azure semantic reranker | 云原生,SQL内调用 | Azure生态 |
3.4 代码实战:Azure HorizonDB中实现混合搜索+重排序
以下SQL示例展示了在数据库内完成BM25 + 向量搜索 + RRF融合 + 交叉编码器重排序的完整流程:
WITHqueryAS(SELECT'wireless noise cancelling headphones'ASq_text,azure_openai.create_embeddings(input=>'wireless noise cancelling headphones')::vectorASq_vec),-- BM25关键词检索(Top 50)bm25AS(SELECTp.id,ROW_NUMBER()OVER(ORDERBYp.description<@>to_bm25query(query.q_text,'idx_products_bm25'))ASbm25_rankFROMproducts p,queryORDERBYp.description<@>to_bm25query(query.q_text,'idx_products_bm25')LIMIT50),-- 向量检索(Top 50)vecAS(SELECTp.id,ROW_NUMBER()OVER(ORDERBYp.embedding<=>query.q_vec)ASvec_rankFROMproducts p,queryORDERBYp.embedding<=>query.q_vecLIMIT50),-- RRF融合(Top 20,送入重排序)fusedAS(SELECTp.id,p.description,(1.0/(60+COALESCE(b.bm25_rank,1000)))+(1.0/(60+COALESCE(v.vec_rank,1000)))ASrrf_scoreFROMproducts pLEFTJOINbm25 bONb.id=p.idLEFTJOINvec vONv.id=p.idWHEREb.idISNOTNULLORv.idISNOTNULLORDERBYrrf_scoreDESCLIMIT20),-- 交叉编码器重排序rerankedAS(SELECT*FROMazure_ai.rank('wireless noise cancelling headphones',ARRAY(SELECTdescriptionFROMfused),ARRAY(SELECTid::textFROMfused)))-- 最终输出SELECTf.id,f.description,r.rank,r.relevance_scoreFROMfused fJOINreranked rONr.document_id=f.id::textORDERBYr.rankASCLIMIT10;3.5 什么时候该用重排序,什么时候该跳过?
| 该用重排序 | 跳过重排序 |
|---|---|
| 搜索质量直接影响用户体验(产品搜索、客服知识库) | 简单的精确匹配查询(产品ID、代码搜索) |
| 查询是带细微差异的自然语言 | 语料库很小,纯向量搜索已经够用 |
| 正在做RAG,需要给LLM最精准的上下文 | 延迟预算无法承受额外的模型推理 |
| 混合搜索返回的Top 10仍然不够理想 | 第一阶段已经筛选到只剩<5条结果 |
四、性能优化要点
4.1 控制候选集大小
重排序的候选集通常在20~50条之间:
- 太少:可能漏掉真正相关的文档
- 太多:延迟和成本线性增长,收益递减
4.2 混合搜索的参数调优
Azure AI Search的实践建议:
| 优化策略 | 操作 |
|---|---|
| 均衡混合(默认) | k=30~50,top=10~20 |
| 召回优先 | 增大maxTextRecallSize(默认1000,上限10000) |
| 精度优先 | 控制k和top适度,加选择性过滤器 |
优化顺序:先调参数,再考虑加副本:
- 减少昂贵的向量搜索设置(如
efSearch从800降到128~192) - 限制语义重排只对真正受益的场景启用
- 压测验证延迟和429限流率
- 只有优化后仍有限流,才考虑扩容
4.3 RRF的k值调优
k是RRF的平滑参数,默认60:
k越大:排名靠后的文档也能获得相对较高的权重,结果更“平滑”k越小:排名靠前的文档权重更大,更强调头部结果- 推荐范围:10~100,具体需根据数据集实测
五、写在最后
把三篇文章串起来,一个生产级RAG检索系统的全貌就是:
【第一篇:索引篇】 FLAT → HNSW/IVF → DiskANN(按数据量选型) ↓ 【第二篇:架构篇】 单机调参 → 量化压缩 → 分布式扩展(按QPS和内存选型) ↓ 【第三篇:精度篇】 纯向量 → 混合搜索(RRF融合)→ 重排序(交叉编码器精排)混合搜索 + 重排序是当前RAG系统召回阶段的“黄金组合”:
- 混合搜索解决“查全”——向量找语义,BM25找精确匹配
- 重排序解决“排准”——交叉编码器对候选集做最终精排
两者结合,才能让大模型拿到最精准的上下文,生成最靠谱的回答。
下一篇,我们来聊聊多模态向量检索——当你的知识库里既有图片、又有文字、还有音频时,怎么做到跨模态的统一检索,欢迎关注。
📌参考说明:本文混合搜索与重排序机制解析参考了Milvus官方文档、Azure AI Search技术文档及Ubuntu博客的技术实践文章,代码示例基于各平台公开的API文档整理。
如需获取更多关于向量数据库选型对比、嵌入模型调优、高并发检索架构、混合搜索策略、向量索引算法详解(HNSW/IVF/PQ)、百万级数据性能压测、多模态向量检索实战等内容,请持续关注本专栏《向量数据库从入门到精通》系列文章。
