Reranker 重排序模型技术实践:为什么找到了正确答案,AI 却还是答错?

举报
yd_54922183 发表于 2026/10/22 09:06:14 2026/10/22
【摘要】 本文介绍 Reranker 的核心原理,并以 BGE-Reranker 为例,通过企业知识库检索、智能客服意图匹配两个案例,演示如何使用 Python 提升搜索结果的质量。

摘要: 在 RAG 知识库和语义搜索系统中,Embeddings 模型可以快速找出一批相关文档,但排在前面的未必是最有用的。Reranker(重排序模型)就是为解决这个问题而设计的。它通过联合理解用户问题和候选文档,对相关性重新评分。本文介绍 Reranker 的核心原理,并以 BGE-Reranker 为例,通过企业知识库检索、智能客服意图匹配两个案例,演示如何使用 Python 提升搜索结果的质量。

一、为什么已经有了 Embeddings,还需要 Reranker?

假设我们正在开发企业内部知识库。

员工问:

出差回来以后,机票和酒店费用应该怎么报销?

Embeddings 模型检索出以下内容:

原始排名 文档内容
1 员工出差前需要提交出差审批申请
2 公司统一预订机票和酒店的管理办法
3 出差结束后,员工应上传机票、住宿发票和审批单,提交费用报销
4 员工日常交通费及办公用品报销流程

可以发现,这四段内容都与“出差”“机票”“报销”有关。

但真正能回答用户问题的,显然是第三条。

Embeddings 擅长快速找到语义接近的内容,却不一定能准确判断哪段内容最能回答当前问题。

这就是 Reranker 发挥作用的地方。

它不是重新搜索整个知识库,而是对已经召回的候选文档重新评分,把真正有用的内容排到前面。

1.1 Reranker 的核心原理

最常见的 Reranker 使用 Cross-Encoder(交叉编码器)架构。

它与 Embeddings 模型的区别非常直观。

Embeddings:分别理解两个文本。

用户问题 ──> Embedding ──> 向量 A
                              │
                              ▼
                           相似度
                              ▲
                              │
候选文档 ──> Embedding ──> 向量 B

Reranker:把两个文本放在一起理解。

用户问题 + 候选文档
         │
         ▼
     Cross-Encoder
         │
         ▼
     相关性分数

在 Cross-Encoder 中,用户问题和候选文档进入同一个 Transformer,模型能够通过注意力机制直接分析二者之间的关系。

例如:

  • 用户问的是报销流程,文档讲的是出差审批,虽然主题相关,却不能直接回答。
  • 用户问的是退款到账时间,文档讲的是如何申请退款,两者相关,但并不完全匹配。

这样的细微区别,往往是 Reranker 的优势。

1.2 为什么不能直接用 Reranker 搜索?

原因很现实:计算成本。

Embeddings 可以提前计算并保存所有文档的向量。

但 Cross-Encoder 必须针对每一个“问题—文档”组合进行推理。

如果知识库有 100 万段文本,那么一次查询就需要处理 100 万个文本对,通常不划算。

因此,实际系统往往采用两阶段架构:

用户问题
   │
   ▼
Embeddings / 关键词检索
   │
   ▼
快速召回 Top-50
   │
   ▼
Reranker 精细排序
   │
   ▼
筛选 Top-5
   │
   ▼
LLM 生成答案

简单理解就是:

Embeddings 负责海选,Reranker 负责复试。

需要注意:Reranker 只能重新排列已有候选。如果正确文档根本没有被第一阶段召回,它也无能为力。


二、有哪些值得研究的 Reranker 模型?

目前比较有代表性的模型包括:

模型 参数量 主要特点
BGE-Reranker-v2-m3 568M 多语言 Cross-Encoder,支持中文
Qwen3-Reranker-0.6B 0.6B 基于语言模型,支持任务指令
Qwen3-Reranker-4B 4B 更大容量,适合复杂相关性判断
jina-reranker-v2-base-multilingual 278M 多语言,支持较长文本,商用需关注许可证

这里有一个值得注意的变化。

早期 Reranker 主要采用 BERT、RoBERTa 一类的编码器模型。

现在也出现了基于生成式大模型架构的 Reranker。

例如 Qwen3-Reranker,可以将相关性判断组织为一个受约束的 Yes/No 任务,再利用模型对答案的打分构造排序分数。

但这并不意味着参数越多效果就一定越好。对于短文本、固定业务领域和低延迟场景,小型 Cross-Encoder 依然非常有竞争力。

本文选用:

BAAI/bge-reranker-v2-m3

主要因为它支持中文、调用简单,也方便与上一篇介绍的 BGE Embeddings 模型组合。

开源地址:

三、实践案例一:为 RAG 知识库增加重排序

3.1 业务场景

继续使用前面的企业知识库。

我们希望构建一条完整的检索链路:

  1. Embeddings 召回相关文档。
  2. Reranker 对候选文档重新评分。
  3. 选取最相关的文档提供给 LLM。

这个方案正是 Sentence Transformers 官方 Retrieve & Re-Rank 实践所采用的基本思路。

3.2 环境安装

pip install -U sentence-transformers FlagEmbedding numpy

我们使用两个模型:

  • BAAI/bge-small-zh-v1.5:负责初步召回。
  • BAAI/bge-reranker-v2-m3:负责重排序。

以下代码可以在 CPU 上运行,但推荐使用 GPU。首次加载需要下载模型文件。

3.3 Python 完整实现

import numpy as np
from sentence_transformers import SentenceTransformer
from FlagEmbedding import FlagReranker

# 1. 加载模型
embedder = SentenceTransformer(
    "BAAI/bge-small-zh-v1.5"
)

reranker = FlagReranker(
    "BAAI/bge-reranker-v2-m3",
    use_fp16=False
)

# 2. 模拟企业知识库
documents = [
    "员工出差前需要完成出差审批,"
    "审批通过后才能预订机票。",

    "公司为员工提供统一的机票和酒店"
    "预订服务,预订标准按职级执行。",

    "员工出差结束后,需上传机票、"
    "酒店住宿发票及出差审批单,"
    "通过财务系统提交报销申请。",

    "员工购买办公用品后,可以凭"
    "有效发票办理日常费用报销。",

    "差旅费用原则上应在出差结束后"
    "30天内完成报销。",

    "员工可以通过人力资源系统"
    "查询年度休假余额。"
]

query = "出差回来,机票和酒店费用怎么报销?"

# 3. Embeddings 召回
doc_vectors = embedder.encode(
    documents,
    normalize_embeddings=True
)

query_vector = embedder.encode(
    "为这个句子生成表示以用于检索相关文章:" + query,
    normalize_embeddings=True
)

similarities = doc_vectors @ query_vector

# 取前5个候选
indices = np.argsort(similarities)[::-1][:5]
candidates = [documents[i] for i in indices]

print("===== Embeddings 召回 =====")
for i in indices:
    print(round(float(similarities[i]), 4), documents[i])

# 4. Reranker 精排
pairs = [
    [query, doc]
    for doc in candidates
]

scores = reranker.compute_score(pairs)

# 5. 按新分数排序
ranked = sorted(
    zip(candidates, scores),
    key=lambda x: float(x[1]),
    reverse=True
)

print("\n===== Reranker 精排 =====")

for doc, score in ranked[:3]:
    print(round(float(score), 4), doc)

3.4 如何理解结果?

第一阶段根据向量相似度选出五条候选文档。

第二阶段的 Reranker 会分别计算:

问题 + 文档1 → Score 1
问题 + 文档2 → Score 2
问题 + 文档3 → Score 3
问题 + 文档4 → Score 4
问题 + 文档5 → Score 5

然后根据分数重新排序。

我们希望关于“上传机票、住宿发票和审批单”的文档排在前面,因为它最直接地回答了用户问题。

但具体排序和分数必须以真实运行结果为准,并不是每次重排序都会比原始检索更好。

在生产环境中,最终选出的 Top-3 或 Top-5 文档,就可以作为 RAG 系统中的上下文,交给 LLM 生成回答。

这样可以减少不相关文档占用上下文窗口的情况。


四、实践案例二:智能客服的相似问题与意图匹配

4.1 业务背景

第二个案例参考了 Cohere 官方关于 FAQ Reranking 的实践思路,改为中文智能客服场景。

假设用户问:

快递显示已经签收,但我根本没收到东西,怎么办?

系统中有以下几个意图:

意图 描述
物流进度查询 用户想了解快递到哪里了
物流异常 用户遇到丢件、错误签收等异常情况
退款查询 用户关心退款什么时候到账
退货申请 用户想退回购买的商品
修改收货地址 用户希望修改订单配送地址

这些类别都可能出现在客服语义检索结果中。

但它们之间有明显区别:用户不是简单地问快递到哪里了,而是遇到了已签收却未收到的异常情况。

如果我们把每个意图的定义作为候选文本,就可以使用 Reranker 进行匹配。

4.2 Python 实现

继续使用之前加载的 reranker:

# 用户输入
query = "快递显示已经签收,但我根本没收到东西,怎么办?"

# 意图类别
intents = {
    "物流进度查询":
        "用户想了解包裹的运输状态、"
        "快递位置、发货进度或预计送达时间。",

    "物流异常":
        "用户遇到包裹丢失、错误签收、"
        "显示已签收但实际没有收到商品等问题。",

    "退款查询":
        "用户已经申请退款,想了解"
        "退款审核状态或者到账时间。",

    "退货申请":
        "用户收到商品后希望退回商品,"
        "申请退货或者退还货款。",

    "修改收货地址":
        "用户希望修改订单的收货地址、"
        "收件人或者联系电话。"
}

# 构造问题与意图描述的文本对
names = list(intents.keys())

pairs = [
    [query, description]
    for description in intents.values()
]

# 计算相关性分数
scores = reranker.compute_score(pairs)

# 按分数排序
results = sorted(
    zip(names, scores),
    key=lambda x: float(x[1]),
    reverse=True
)

for name, score in results:
    print(f"{name}: {float(score):.4f}")

print("首选候选意图:", results[0][0])

在这个例子中,我们希望“物流异常”得到最高的相关性评分,而不是“物流进度查询”。

这里展示的是可执行代码,没有人为编造模型的实际分数。

4.3 能不能直接用 Reranker 做意图分类?

可以,但需要分清楚使用场景。

如果只有十几个意图,那么直接让 Reranker 对所有意图进行评分,是一种相当直观的零样本分类方法。

如果有几百个二级意图,则可以先用 Embeddings 召回一批候选,再进行精排。

用户问题
   │
   ▼
Embeddings
   │
   ▼
召回 Top-20 意图
   │
   ▼
Reranker
   │
   ▼
精排 Top-5 意图
   │
   ▼
决策模型 / 分类器
   │
   ▼
最终意图

不过,Reranker 的相关性评分并不等于意图分类概率。

尤其在多意图场景下,最高分只能说明它是当前候选中模型最偏好的一个,不能说明其他意图不存在。

例如:

我的快递一直没到,如果今天还不发货,我就申请退款。

这句话同时涉及物流和退款条件。

如果最终需要输出多个意图,建议把 Reranker 当作候选精排器,再使用多标签分类器或决策模型完成最终判断。


五、Reranker 的分数到底能不能当概率?

这是实际使用时很容易踩的坑。

BGE-Reranker 默认返回原始相关性分数,也称 Logit。

例如,官方模型示例中可能出现:

相关文本:5.26
不相关文本:-8.18

分数可以为负,也可以大于 1。

如果希望映射到 0~1,可以设置:

scores = reranker.compute_score(
    pairs,
    normalize=True
)

其底层相当于执行 Sigmoid 变换:

[
P=\frac{1}{1+e^{-s}}
]

但这里有个关键问题:

经过 Sigmoid 得到 0.95,不代表该文档有 95% 的实际概率能够回答用户问题。

它只是在不改变排序顺序的前提下,把原始分数压缩到了 0~1。

如果希望根据分数做自动路由、拒识或人工复核,仍需要使用业务标注数据对阈值进行验证,必要时还要做概率校准。

对于纯粹的 Top-K 排序任务,通常直接使用原始分数就足够了。

六、实际部署时,哪些问题值得注意?

1. 召回数量不是越多越好。

很多系统会先召回 Top-20~Top-100,再交给 Reranker。

但增加候选数量意味着更多文本对需要推理,延迟也可能随之增加。

应该结合 Recall@K 和 P95 延迟,选择合理的召回规模。

2. Reranker 不一定能处理完整的超长文档。

大多数 Cross-Encoder 有输入长度限制。

如果直接把一篇几万字的文章送进去,内容可能被截断,真正相关的段落反而没有进入模型。

更好的办法是先分块,再按文本块召回和重排序。

3. Reranker 可能成为新的性能瓶颈。

Embeddings 文档向量通常可以离线预计算,但 Reranker 要在每次查询时重新计算候选文本对。

因此,实际部署可以考虑 GPU 批量推理、FP16、模型量化,以及限制候选数量。

4. 要衡量它是不是真的改善了效果。

不能只看某个例子中相关文档是否被排到了第一名。

建议用一批真实标注数据比较:

指标 主要用途
Recall@K 第一阶段有没有召回正确文档
MRR@K 第一个正确结果排得是否靠前
NDCG@K 综合评价排序质量
P95 Latency 95% 请求的端到端处理耗时
RAG 答案准确率 重排序是否改善了最终回答

只有确认 Reranker 带来的质量收益大于其延迟和成本,才有必要在生产系统中长期保留这一阶段。

七、总结

Reranker 的作用其实很好理解:它不是负责找到更多信息,而是负责从已经找到的信息中,挑出更值得关注的内容。

本文的两个案例分别展示了:

  • RAG 重排序: Embeddings 负责快速召回,Reranker 负责提升结果排序质量。
  • 客服意图匹配: 利用问题与意图描述之间的相关性,帮助区分语义相近的类别。

把它与前面介绍过的模型放在一起,可以形成一条比较完整的 AI 信息处理链路:

语音 / 图片 / PDF / 文本
           │
           ▼
        ASR / OCR
           │
           ▼
         文本
           │
           ▼
       Embeddings
           │
           ▼
        候选召回
           │
           ▼
        Reranker
           │
           ▼
        精细排序
           │
           ▼
      LLM / 决策模型
           │
           ▼
         业务输出

当然,并不是每套 AI 系统都需要增加 Reranker。如果候选数量很少、原始排序已经足够准确,那么多一次推理反而可能得不偿失。

但在知识库规模较大、文本语义相近、用户问题比较具体的场景中,它往往值得认真评估。

最后记住一个核心区别:

Embeddings 解决“哪些内容可能相关”,Reranker 进一步解决“哪些内容最符合当前问题”。

很多时候,RAG 系统回答不准确,不是因为最后使用的 LLM 不够强,而是因为它一开始拿到的资料就没有排对。

在考虑换一个更昂贵的大模型之前,不妨先检查一下检索和重排序环节。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。