返回笔记首页

RAG中的重排序(Reranking)如何工作?有哪些重排序模型

主题配置

精炼回答

重排序是RAG检索后的精排环节,工作原理是对初筛的候选文档与查询进行精确相关性打分,重新排列顺序,把最相关的内容送给大模型。初筛阶段通常用向量检索快速召回几十上百个候选,但向量模型对语义细节捕捉不够精准,这时重排序模型会对每个<查询,文档>对进行深度交互计算,输出0-1之间的相关性分数,根据分数降序取Top-K。

核心区别在于交互深度:向量检索是分别编码查询和文档再做相似度计算,而重排序模型让查询和文档在模型内部充分交互,通过Cross-Attention机制捕捉细粒度的语义匹配信号,准确性更高但计算成本也更大,所以放在二阶段处理。

常见的重排序模型包括:Cohere Rerank系列(商用API效果好)、bge-reranker(智源开源,中英文支持)、jina-reranker(支持多语言长文本)、以及基于BERT的Cross-Encoder架构模型。实际应用中,比如法律文档检索,初筛可能召回50条相关法条,重排序会精准识别出与案件事实最匹配的3-5条,显著提升RAG答案质量。工程上要平衡精度和延迟,通常初筛召回20-100条,重排后取5-10条送入LLM。

扩展分析

为什么需要重排序:双阶段检索的工程智慧

假设你在做一个企业知识库问答系统,文档库里有10万份技术文档,用户问"如何配置Redis集群的主从切换",这时候该怎么设计检索链路?如果只用向量检索,会遇到明显的精度瓶颈。向量模型采用的是双塔架构,查询和文档分别编码成向量,然后计算余弦相似度。这种设计的好处是速度快,可以提前把10万份文档都编码好存在向量数据库里,查询时只需要编码一次查询文本就能快速匹配。但问题在于,两段文本在编码阶段完全不知道对方的存在,模型无法捕捉到查询词和文档片段之间细粒度的对应关系。

工程上解决这个问题的思路就是召回-精排分离。第一阶段用向量检索快速从10万文档里筛出可能相关的100条候选,这一步追求的是召回率,宁可多召回一些也不能漏掉真正相关的内容。第二阶段用重排序模型对这100条做精确打分,这时候计算量已经从10万降到100,可以用更重的模型做深度语义匹配,最后取Top 5送给大模型生成答案。这个表达就把架构分层的核心逻辑讲透了——用效率换召回,再用精度换排序。

重排序模型的核心技术是交叉编码器(Cross-Encoder),它会把查询和文档拼接成一个序列,比如[CLS] 如何配置Redis集群主从切换 [SEP] 本文档介绍Redis Sentinel的配置方法...,然后送入BERT这类Transformer模型。关键的区别在于,查询中的每个词都能通过Self-Attention机制和文档中的每个词做交互,模型能看到"主从切换"这个查询意图和文档里"Sentinel自动故障转移"这个表达之间的语义对应关系,最后输出一个0到1之间的相关性分数。

最早的重排序模型就是基于BERT的Cross-Encoder,把分类头换成回归头直接预测相关性分数。后来出现了专门针对排序任务优化的模型,比如智源的BGE-reranker系列,它在训练时用了大量的查询-文档正负样本对,还做了难负样本挖掘,在中文场景效果明显更好。Cohere Rerank这类商用服务走的是另一个方向,模型参数规模更大,支持多语言和长文本,但调用需要付费。最近还有人尝试直接用大模型做重排,给GPT-4输入查询和候选文档,让它打分或者直接排序,效果很好但成本高,适合对准确性要求极高的场景。

拿客服工单检索来举例,用户反馈"App闪退打不开",向量检索可能会召回50条包含"闪退"、"打不开"、"App崩溃"等关键词的历史工单。但这些工单里有的是因为版本不兼容,有的是因为内存不足,有的是因为网络异常。重排序模型能结合用户的设备型号、系统版本等上下文信息,精准识别出和当前问题最相似的那几条工单,工程师就能快速定位到解决方案。

工程实践:从参数选择到性能优化

重排序在几类系统中特别关键。问答系统里用户问题往往很短,向量检索容易被表面词汇相似度误导,重排序能真正理解问题意图;文档检索场景下文档长度差异大,有的几百字有的几万字,向量相似度会受文档长度影响,重排序模型能做归一化处理;推荐系统虽然主要用协同过滤,但内容冷启动时需要依赖语义匹配,这时重排序比单纯的向量召回精准得多。

召回阶段的Top-K通常设置在50-100之间,这个数字要保证召回率,宁可多召回也别漏掉相关文档,因为这一步主要靠向量相似度,精度有限。重排序后取的Top-K一般是5-10条,这个数字受两个因素约束:一是大模型的上下文窗口成本,每多塞一条文档就多消耗几百个token;二是注意力稀释问题,文档太多LLM反而抓不住重点。在验证集上跑不同K值组合,看最终生成答案的准确率拐点在哪里,通常会发现K从5增加到10时效果还在涨,但从10到20就不明显了,这时候就选10作为线上配置。

线上服务如果是单条查询,重排序模型每次推理只处理一个query-doc对,GPU利用率会很低。实际会做批处理优化,把一个查询对应的50个候选文档拼成一个batch送入模型,在GPU上并行计算相关性分数。下面是批量重排序的核心逻辑实现:

python
import numpy as np
from typing import List, Tuple

classRerankerService:
def__init__(self, model, batch_size=32):
self.model = model
self.batch_size = batch_size

defbatch_rerank(self, query:str, candidate_docs: List[str], top_k:int=10)-> List[Tuple[str,float]]:
"""
 批量重排序:对候选文档进行深度相关性打分

 Args:
 query: 用户查询文本
 candidate_docs: 初筛后的候选文档列表
 top_k: 返回的Top文档数量

 Returns:
 排序后的文档和对应分数列表
 """
# 构造查询-文档对
pairs =[(query, doc)for doc in candidate_docs]

# 批量编码,充分利用GPU并行能力
scores =[]
for i inrange(0,len(pairs), self.batch_size):
batch_pairs = pairs[i:i + self.batch_size]
batch_scores = self.model.predict(batch_pairs)
scores.extend(batch_scores)

scores = np.array(scores)

# 按分数降序排序
ranked_indices = np.argsort(scores)[::-1]

# 返回Top-K结果
results =[
    (candidate_docs[idx],float(scores[idx]))
    for idx in ranked_indices[:top_k]
]

return results

defrerank_with_filtering(self, query:str, candidate_docs: List[str],
min_score:float=0.5, top_k:int=10)-> List[Tuple[str,float]]:
"""
 带阈值过滤的重排序,过滤掉明显不相关的文档
 """
results = self.batch_rerank(query, candidate_docs, top_k=len(candidate_docs))

# 过滤低分文档
filtered_results =[
    (doc, score)for doc, score in results
    if score >= min_score
]

return filtered_results[:top_k]

性能瓶颈要先定位出现在哪个环节。如果是模型推理慢,可以考虑量化加速或者混合精度;如果是候选集太大,要往前看召回阶段能不能做更精准的预筛;如果是批处理效率低,要检查GPU利用率是不是没打满。如果初筛召回100条但重排序模型处理不过来,可以先用一个轻量规则做预筛,比如BM25分数过滤掉明显不相关的文档,把候选集降到50条再送重排序。

重排序模型通常是BERT系列,可以做INT8量化或者用ONNX Runtime加速推理,实测能把延迟从200ms降到50ms,准确率只掉1-2个点,线上完全可以接受。拿智能客服场景举例,用户提问后如果等待超过500ms就会感觉明显卡顿,这时候就必须做量化或者减少候选集大小,保证整体RT在300ms以内。高峰期如果重排序延迟超过阈值,可以临时退化到只用向量检索结果,保证用户体验不被拖垮。

评估重排序效果不能只看模型分数,要看最终对答案质量的影响。离线评测时会用MRR(Mean Reciprocal Rank),它关注的是正确答案在排序结果中的位置,如果相关文档被排到第3位,这条query的得分就是1/3,所有query求平均。NDCG(Normalized Discounted Cumulative Gain)会考虑位置衰减,排在前面的文档权重更高。但实际线上更关注Top-3准确率,因为通常只把前3条文档喂给大模型,如果相关文档被挤到第5位,再高的NDCG分数也没用。

重排序上线后如果效果提升不明显,通常有两个原因。一是召回阶段已经把相关文档漏掉了,重排序再精准也是巧妇难为无米之炊,这时候要去优化向量模型或者补充关键词召回通道。二是候选文档质量差异不大,都是半相关状态,重排序模型很难拉开区分度,这种情况要么是文档切分粒度有问题,要么是查询改写没做好。拿技术文档检索来说,如果用户问"Python怎么读取CSV文件",召回的50条文档可能都提到了pandas库,但有的讲read_csv方法,有的讲read_table方法,重排序要能识别出read_csv才是标准答案。如果文档切分时把这些内容都混在一个5000字的长文档里,重排序就很难精准定位,这时候应该考虑更细粒度的chunk切分。

模型选型与系统性思考

选重排序模型时要看三个维度的匹配度。延迟敏感的C端场景,比如搜索推荐,要选BGE-reranker-base这种轻量模型,部署在GPU上能做到50ms以内;B端企业服务可以接受200-300ms延迟,可以用Cohere Rerank或者BGE-large,准确率能提升5-10个点;如果是低频高价值场景,比如法律合规审查,甚至可以直接调用GPT-4做重排,成本虽然高但绝对精度有保障。

开源模型像BGE适合自己部署,可以根据业务数据fine-tune;商用API像Cohere适合快速验证效果,但长期成本要算清楚,高并发场景下API调用费可能比自建GPU集群还贵。权衡的核心是看错误的代价有多高。金融合规场景下漏掉关键条款可能导致法律风险,这时候宁可牺牲一些响应速度也要用最精准的重排序模型;但像商品推荐场景,排序稍有偏差用户顶多多翻一页,这时候速度优先,选轻量模型就够了。

重排序模块上线一定要做A/B实验,不能光看离线指标。实际操作中会发现,离线NDCG提升5%,线上用户满意度可能只涨2%,甚至有时候会因为改变了用户习惯的结果顺序反而降低点击率。所以要设计合理的实验指标,比如问答系统看答案采纳率,文档检索看用户停留时长,这些才是真正反映业务价值的指标。

重排序的效果其实受前后环节影响很大,上游如果文档切分粒度不合理,召回的候选质量就差,重排序也救不回来;下游如果大模型的prompt设计不好,就算给它最相关的文档也生成不出好答案。整个RAG链路就像一条流水线,每个环节都要配合好才能发挥最大价值。从查询改写、向量召回、重排序,到最后的生成,需要把它们看作一个有机整体来优化,而不是只盯着某个技术点做局部调优。这种系统性思维,才是真正能在工程实践中发挥作用的能力。