向量检索负责"快",Reranker负责"准"
1.为什么有了向量检索,还需要Reranker?
在RAG系统中,向量检索(Vector Retrieval)负责从海量知识库中快速召回 Top-K条相关文本。它的优势是快------即使数据库里有上亿条文档,也能在毫秒级完成检索。
但"快"的代价是"精度有限"。向量检索(双塔模型)把文本压缩成固定长度的向量,这个过程必然会损失信息。所以Top-K的顺序不一定完全准确,可能:
- 真正相关的文档被排在了第11位,没有被召回
- 排名靠前的文档其实和问题关系不大
Reranker(重排器) 的任务就是:在候选集已经很小(比如Top-50)之后,再仔细判断"当前问题与每条候选文本是否真正相关",用更强的模型重新打分,重新排序。
一句话总结: 向量检索是"海选",Reranker是"决赛评委"。
2.两种主流模型框架:Bi-Encoder和Cross-Encoder
要理解Reranker为什么比向量检索更精准,需要先理解两种模型架构的区别:
| 架构 | 代表模型 | 特点 |
|---|---|---|
| Bi-Encoder(双塔) | Sentence-BERT、OpenAI Embedding | 快,适合召回 |
| Cross-Encoder(交叉编码器) | BERT-Reranker、Cohere Rerank | 准,适合重排 |
2.1 Bi-Encoder(双塔编码器)
Bi-Encoder有两个独立的编码器(或共享权重的同一个编码器):
- 问题塔: 将用户问题编码成一个向量
- 文档塔 将每条候选文档分别编码成向量
💡之前的Embedding模型就是Bi-Encoder架构。
工作流程:
- 所有文档的向量可以离线预先计算好,存入向量数据库
- 用户提问时,只需要实时计算问题向量(1次推理)
- 用向量相似度计算,从数据库中快速检索Top-K
优点: 速度快,因为文档向量可以提前算好,在线只需要算一个问题向量。 缺点: 问题和文档在编码过程中互相看不到对方,各自独立编码成向量,会丢失交互信息。
2.2 Cross-Encoder(交叉编码器)
Cross-Encoder只有一个编码器,但它同时接收问题和文档 ,把[问题 + 文档]拼接在一起作为一个整体输入:
text
输入:[CLS] 今天天气怎么样? [SEP] 明天北京晴转多云,气温15-25℃[SEP]
↓
Cross-Encoder 模型
↓
输出:相关性分数(0-1)
关键区别: 在Cross-Encoder的注意力机制(Self-Attention)中,问题可以看到文档的每一个词,文档也可以看到问题的每一个词。它们之间可以进行充分的语义交互,从而更准确判断相关性。
优点: 精度高,能捕捉细粒度的语义关系 缺点: 无法预先计算,每判断一对[问题 + 文档]都要做一次完整的模型推理,速度慢。如果有50条候选文档,就需要推理50次。
3.Bi-Encoder vs Cross-Encoder:直观对比
| 对比维度 | Bi-Encoder(向量检索) | Cross-Encoder(Reranker) |
|---|---|---|
| 输入方式 | 问题和文档分开编码 | 问题和文档拼接在一起输入 |
| 交互程度 | 无交互,只能通过向量相似度间接比较 | 充分交互,注意力机制互相可见 |
| 在线推理次数 | 1次(只算问题向量) | N次(N = 候选文档数) |
| 速度 | 极快(毫秒级) | 较慢(取决于模型大小) |
| 精度 | 一般 | 更高 |
| 适用场景 | 第一阶段召回(海量)→几百/几千 | 第二阶段精排(几百 →Top-K) |
| 能否离线预处理 | ✅文档向量可以提前算好 | ❌必须在线实时计算 |
4.Reranker的典型工作流程
在实际的RAG系统中,两个阶段配合使用:
text
用户提问
↓
1.Bi-Encoder 向量检索(海量知识库 → Top-50)
↓
2.Cross-Encoder Reranker精排(Top-50 → 重新打分排序)
↓
3.返回最终的Top-5给LLM生成答案
为什么不能只用Cross-Encoder? 因为Cross-Encoder太慢了。如果知识库有100万文档,每条都和问题做一次完整推理,延迟会高达数秒甚至数十秒,用户无法接受。
为什么不能只用Bi-Encoder? 因为Bi-Encoder精度有限,可能把真正相关的文档排到后面。用Cross-Encoder在少量候选集上做二次筛选,能显著提升最终结果质量。
这就是经典的 "粗排 + 精排" 思想,在推荐系统,搜索引擎中已经应用了很多年,RAG只是沿用了这套成熟架构。
5.代码示例
python
from sentence_transformers import CrossEncoder
import numpy as np
# 加载 Cross-Encoder 模型(轻量级,适合入门)
model = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
# 用户问题
query = "入职需要准备什么材料?"
# 向量检索召回的 Top-5 候选(来自知识库)
candidates = [
"新员工入职需携带身份证、学历证书复印件、一寸照片。",
"公司每周三下午举办新人培训会。",
"离职手续需要提前一个月提交书面申请。",
"入职材料包括劳动合同、体检报告、工资卡信息。",
"公司食堂午餐时间为12:00-13:00。"
]
# 构建 (问题, 候选文档) 对
pairs = [[query, doc] for doc in candidates]
# Cross-Encoder 推理,得到相关性分数(0~1 之间)
scores = model.predict(pairs)
# 按分数从高到低排序
ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
# 输出结果
print("Reranker 排序结果:")
for idx, (doc, score) in enumerate(ranked, 1):
print(f"{idx}. (score: {score:.4f}) {doc}")
预期输出:
python
#Reranker 排序结果:
#1. (score: 0.98) 新员工入职需携带身份证、学历证书复印件、一寸照片。
#2. (score: 0.95) 入职材料包括劳动合同、体检报告、工资卡信息。
#3. (score: 0.45) 公司每周三下午举办新人培训会。
#4. (score: 0.12) 公司食堂午餐时间为12:00-13:00。
#5. (score: 0.08) 离职手续需要提前一个月提交书面申请。
可以看到,Cross-Encoder 成功把真正相关的文档排到了前面,把不相关的文档(食堂、离职)压到了最后。
6.Reranker的使用建议
- 候选集大小适中: Reranker的候选集通常建议在50~200条 之间。太少的话,召回可能遗漏好结果,太多的话,Reranker推理太慢。
- 模型选型: 中文场景优先选
BAAI/bge-reranker-v2-m3;英文场景可以用ms-marco-MiniLM系列。 - 延迟敏感场景: 如果对响应速度要求极高,可以: 用更小的 Reranker 模型(如 MiniLM-L-2) 减少候选集数量(如 Top-20) 甚至跳过 Reranker,直接用向量检索结果
- 评估效果: 建议用一些测试用例对比"加 Reranker"和"不加 Reranker"的最终回答质量,验证在你的业务场景中是否值得增加这一环节。
一句话记住:向量检索解决"找得到"的问题,Reranker 解决"排得准"的问题。两者配合,才能在速度和精度之间取得最佳平衡。