如何用混合检索与重排序提升生产环境 RAG 的召回质量
demo 阶段”向量检索 top-5 拼 prompt”能跑通,一到生产环境就露馅:专有名词搜不到、口语问句匹配不上书面语、top-5 里混进看似相关实则无用的段落。召回质量 = 检索召回率 × 排序精度,单靠向量检索两个都拿不满。本文用”BM25 + 向量双路召回 → RRF 融合 → rerank 精排”三段式把召回质量做上去。
graph TD
Q[用户问题] --> V[向量检索 top-20]
Q --> B[BM25 关键词检索 top-20]
V --> R[RRF 融合去重]
B --> R
R --> K{融合后是否够 10 条}
K -- 够 --> RR[rerank 模型精排]
K -- 不够 --> E[告警并检查索引覆盖]
RR --> T[取 top-3 拼入提示词]
前置准备
在已有向量库 RAG 的基础上加装关键词检索与重排模型:
pip install rank-bm25 flag-reranker |
- rank-bm25:纯 Python 的 BM25 实现,小规模语料足够
- reranker:交叉编码器,把”问题-段落”拼在一起打分,比双塔向量更准但更慢,所以只排候选集
步骤一:双路召回
向量检索抓语义,BM25 抓字面(型号、错误码、人名这类词向量经常糊掉):
from rank_bm25 import BM25Okapi |
两路各取 top-20 而不是 top-5:召回阶段宁可冗余,把裁减交给后面的精排。
步骤二:RRF 融合
不用加权分数相加(两路分数量纲不同),用 Reciprocal Rank Fusion 只认排名:
def rrf_fuse(vec_hits, kw_hits, k=60): |
同时出现在两路的段落分数叠加,自然浮到前面——这就是”混合”的价值。
步骤三:rerank 精排
交叉编码器对融合后的 10 条逐一打分,取 top-3 进 prompt:
from FlagEmbedding import FlagReranker |
注意设置分数阈值(如 >0):全部低分的宁可不注入,避免”没有依据硬答”变成”拿着错依据答”。
步骤四:上线召回率回归
准备 50~100 条”问题-应命中段落”标注集,对比改造前后:
def recall_at_k(pred_docs, gold_doc, k=3): |
recall@3 提升 10 个百分点以上再上线;同时记录端到端延迟增量(rerank 约 +50~150ms)。
常见问题
- BM25 路毫无贡献:中文没分词或停用词没处理,检查 jieba 分词与索引一致性
- rerank 成为延迟瓶颈:候选从 10 砍到 8、换 reranker-small、或 fp16/INT8 推理
- 融合后反而变差:某一路噪声太大(如 BM25 命中大量模板句),给该路加 IDF 过滤或降 k
- 阈值怎么定:在标注集上看 rerank 分数分布,取”金标准段落最低分”再留 10% 余量
小结
生产级召回是一条漏斗:双路宽召回保覆盖,RRF 融合做无量纲合并,rerank 精排保精度,最后用 recall@k 标注集做上线门禁。每一段都可独立替换(换向量模型、换 ES、换 reranker),但”宽召回 → 融合 → 精排 → 度量”的骨架不变——召回质量的提升来自漏斗设计,而不是某个单点模型。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus








