Reranker重排序实战:让RAG检索准确率大幅提升的那记临门一脚
向量检索召回了对的文档却排在后面?加入重排序模型是提升RAG质量性价比最高的一步。本文讲原理、选型、集成与效果评估。
在 RAG 系统里,有个让人抓狂的现象:正确答案明明被检索到了,却排在第五、第八位,模型上下文里塞满了不相关的文档,最终答案质量一塌糊涂。 增加 top-k 只会让噪声更多。这时候你需要的不是更强的向量模型,而是重排序(Reranking)。
一、为什么需要重排序
RAG 检索通常分两阶段,叫”召回 + 精排”:
- 召回(Recall):用向量相似度从海量文档里快速捞出候选(比如 top-100)。追求”别漏掉对的”。
- 精排(Rerank):对候选做更精细的相关性打分,重排出真正最相关的 top-5。追求”排对顺序”。
向量检索快,但它用的是双塔结构——问题和文档各自编码成向量再比较,丢失了细粒度的交互信息。而重排序模型是交叉编码器(Cross-Encoder),把问题和文档拼在一起送进模型,能捕捉词级交互,相关性判断准得多,代价是慢——所以只用在少量候选上。
二、效果为什么这么明显
向量检索的排序是”近似”的,重排序是”精算”的。把 top-100 的粗排结果交给重排序,取前 5 喂给生成模型,通常能把答案相关性从 60% 出头拉高到 85% 以上。这是 RAG 优化里性价比最高的一步。
三、主流重排序方案选型
| 方案 | 特点 | 适用 |
|---|---|---|
| Cohere Rerank | 托管、效果稳定、按量计费 | 快速上线、无 GPU |
| BGE-Reranker | 开源、中文强、可本地部署 | 私有化、成本敏感 |
| Jina Reranker | 多语言、长文本 | 多语种场景 |
| 自训小模型 | 可控、贴合业务 | 有标注数据 |
中文场景下,BGE-Reranker 系列是常被推荐的平衡之选,可本地跑,无需外发数据。
四、集成示例
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")
def rag_search(query, candidates, top_n=5):
# candidates: [(doc_id, text), ...] 来自向量召回
pairs = [(query, text) for _, text in candidates]
scores = reranker.predict(pairs)
ranked = sorted(zip(candidates, scores),
key=lambda x: x[1], reverse=True)
return [doc for (doc, _), _ in ranked[:top_n]]
要点:重排序吃的是”问题-文档对”,输入顺序无所谓,但要控制候选数量——太少了没得选,太多了延迟高。经验值是召回 50100,精排取 38。
五、三个容易忽视的调优点
1. 候选数量要调。 召回 20 有时比召回 100 效果更好,因为噪声候选可能干扰重排序。要在你的数据上测。
2. 重排序不是万能药。 如果召回阶段根本没捞到正确文档(召回率低),重排序无能为力。先保证召回率,再优化精排。
3. 延迟预算。 交叉编码器比向量检索慢一到两个数量级。若对延迟敏感,考虑用更小的重排序模型,或对候选分批处理。
六、如何衡量收益
建立一套评估集(问题 + 标准答案文档),分别测量:
- 召回率:正确文档是否在前 100;
- MRR / NDCG:正确文档排序的倒数排名与归一化折损;
- 端到端答案质量:人工或模型打分。
只上重排序、其余不动,通常就能看到 MRR 的明显跃升。
结语
RAG 质量的提升有优先级:分块 > 召回 > 重排序 > 生成调优。重排序处在”投入小、见效快”的甜点区。如果你已经做好了分块和召回,却仍被”对的答案排太靠后”困扰,加一个 Reranker,往往就是那记决定性的临门一脚。