在RAG检索链路中,Rerank常常被低估,但其实它的价值极高。它不仅能在不损失查询精度的前提下实现实质性的查询加速,还能进一步提升最终结果的准确性。核心逻辑非常直观:从召回的上下文中过滤掉无关的噪声节点,然后对剩余的相关内容重新排序。这样一来,LLM获得的上下文更加纯净,回答质量自然随之提高。

Rerank在RAG中的核心价值与实现背景
二阶段检索器(Two-stage Retriever)
检索模块对RAG最终问答的准确率和用户体验影响巨大,可以说,谁在这个环节下功夫,谁就能在效果上拉开差距。要实现高效、准确的任务,一个优秀的检索器至少需要满足三个条件:
- 尽可能充分地召回与用户问题相关的文本片段;
- 将相关性更高的片段排在更靠前的位置;
- 果断过滤掉质量低劣的文本片段。
"离线Embedding" 搭配 "在线Reranker"
二阶段检索包含召回和精排两个环节,这是当前RAG流程中一种非常务实的组合方案,很好地平衡了检索的「性能」与「效率」这两个看似矛盾的目标。
在召回阶段,通常采用密集向量检索技术,即Embedding模型(典型的是dual-encoder架构)。首先将知识库离线处理成语义向量,然后利用用户问题的向量进行匹配,快速找出候选片段。这种方法效率高、响应快,但有一个天然短板——用户问题和知识库内容之间缺乏实时交互,因此匹配精度可能会有所下降。
到了精排阶段,就需要引入cross-encoder架构了,例如Reranker模型。它让用户问题和知识库语料进行在线语义交互,核心逻辑很简单:判断问题与你这个文档是否真的「相关」,从而更精准地判定语义关联,提升检索质量。不过,这是用响应时间换来的效果。
两个阶段协同配合:先通过召回快速筛选出一批候选,再利用精排进行精准排序并剔除低质内容,一个负责「广撒网」,一个负责「精过滤」。这种策略对于大型知识库尤其重要,数据量越大,信息质量越容易参差不齐,而二阶段检索正好能化解这一难题。利用Reranker在Embedding初筛基础上进行精细优化,就能在知识库扩容的同时,确保问答质量不降反升。
为了优化问答体验,召回环节应当尽可能多地召回可能相关的片段,即使包含一些杂音也没关系;而精排环节则要专注于提升结果相关性、过滤无关内容。以BCEmbedding方案为例,Embedding负责离线广泛召回,Reranker则负责在线精确排序与过滤。
总的来说,Embedding(双编码器)在离线处理时,query和passage之间缺乏信息交互,这限制了它的表现上限。而Reranker的交叉编码器由于支持在线交互,灵活性和性能潜力更高,自然更适合复杂度高的精排任务。为了减轻Embedding的负担并优化其表现,合理设计标签分配策略至关重要——这不仅要贴合业务目标,还需考虑实际应用场景与学术研究设定之间的差异,确保模型特性与任务需求相匹配。
评测数据与效果分析
BCEmbedding项目设计思路
BCEmbedding的设计逻辑非常清晰:让Embedding尽可能多地召回潜在相关的片段,然后将精排这种「高难度任务」交给上限更高的Reranker。bce-embedding-base_v1和bce-reranker-base_v1的组合拳,正是为了实现最佳的检索效果。
评测配置
本次评测基于纯中文、多领域的LlamaIndex RAG评测。流程如下:首先由Embedding召回top10片段,接着用Reranker进行精排,最后取top5片段计算各项指标。
评测结论
- 本次评测覆盖了多领域、中英双语以及跨语种场景。如果对中文或英文单语种评测感兴趣,可以专门查阅【中文RAG评测['zh']】和【英文RAG评测["en"]】的相关数据。
- 从竖排对比来看,bce-embedding-base_v1的表现一直非常稳健,语种支持和领域覆盖都很出色。openai-embed-3和bge-m3在这种场景下同样展现出强劲的性能,多语种和跨语种能力毋庸置疑,领域泛化性也相当好。Cohere和e5的多语种embedding模型表现也不错。相比之下,某些单语种embedding模型就略显乏力(其中JinaAI-v2-Base-zh和bge-large-zh-v1.5稍好一些)。
- 横向对比则非常直观:Reranker模块能够显著改善检索效果。CohereRerank和bge-reranker-large效果相当,而bce-reranker-base_v1的精排能力比前两者更强。
- 总结一下:bce-embedding-base_v1与bce-reranker-base_v1的组合,确实实现了当前最好的效果(93.46/77.02),比目前最佳的开源闭源组合(bge-m3-large+bge-reranker-large,89.94/70.17)高出不少——hit rate提升3.53%,mrr提升6.85%。
- 评测是在["en", "zh", "en-zh", "zh-en"]的设定下进行的。
- 在不使用Reranker的情况下(竖排对比),bce-embedding-base_v1的表现持续领先其他Embedding模型,无论是开源还是闭源。
- 在固定Embedding模型后,对比不同Reranker的效果(横排对比),bce-reranker-base_v1的表现全面优于其他Reranker,同样涵盖开源和闭源。
- 结论很明确:bce-embedding-base_v1 + bce-reranker-base_v1这个组合,当前已达到SOTA水平。
Reranker模型的核心意义
Reranker与Embedding最本质的区别在于输入输出的形式:它直接接收「问题+文档」作为输入,然后输出它们之间的相似度分数,而非向量表示。训练时使用交叉熵损失函数进行优化,产生的相关性分数没有范围限制,能更直观地反映真实的语义关联强度。以bge-reranker-base_v1为例,它特别适合中英文双语环境下的精排任务,输出的分数不仅能辅助段落排序,还能有效识别并过滤掉低质量内容,为LLM的问答系统(如RAG)提供更纯净的上下文,从而提升回答质量。
至于EmbeddingModel,它具备中英双语及跨语言检索能力;而RerankerModel更进一步,单一模型就能处理中英日韩四种语言的跨语种语义精排,在多语言环境下的表现力更强。这两个模型组合起来,覆盖的领域非常广泛,包括教育、医疗、法律、金融、科研、客服和通用问答——为RAG系统的多样化场景打下了扎实的基础。
官方使用指南
这套开源的二阶段检索模型(bce-embedding-base_v1和bce-reranker-base_v1)可以免费商用,同时配套的算法库BCEmbedding使用起来非常方便:
- EmbeddingModel和RerankerModel都支持BCEmbedding、transformers、sentence-transformers框架来推理模型;
- 提供了LangChain和LlamaIndex的集成接口,可以方便地集成到现有的RAG产品中,改造成本很低。
- RerankerModel的rerank方法支持长片段(512~32k tokens)的精排,这个范围非常实用。
- rerank方法会输出0到1之间的语义相关分数,该分数不仅可以用作排序依据,还能用来过滤低质量片段(推荐的rerank分数阈值在0.35~0.4之间)。去除这些无关上下文后,LLM的问答质量会有明显提升。
调研实践
1.创建一个conda环境并激活
conda create --name bce python=3.10 -y
conda activate bce
2.项目源码安装
git clone git@github.com:netease-youdao/BCEmbedding.git
cd BCEmbedding
pip install -v -e .
3.本次实践以 [基于BCEmbedding实现Rerank] 为例。
通过 BCEmbedding调用 EmbeddingModel。pooler默认是 cls。
通过 BCEmbedding调用 RerankerModel可以计算句子对的语义相关分数,也可以对候选检索结果进行排序。
实际部署时,可以直接使用Flask编写一个简单的服务,暴露一个rerank接口,将查询和候选片段传入即可拿到排序后的结果:
from flask import Flask, request, jsonify
import time
import logging
from BCEmbedding import RerankerModel
import json
app = Flask(__name__)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Load the model once when the application starts
start_time = time.time()
model = RerankerModel(model_name_or_path="models/maidalun/bce-reranker-base_v1")
end_time = time.time()
logging.info(f"Model loaded in {end_time - start_time:.2f} seconds")
@app.route('/rerank', methods=['POST'])
def rerank():
start_time = time.time()
# Get data from the POST request
data = request.get_json()
query = data.get('query', '')
slices = data.get('slices', [])
if not query or not slices:
return jsonify({"error": "Both 'query' and 'slices' are required."}), 400
try:
# Calculate scores or rerank slices
rerank_results = model.rerank(query, slices)
end_time = time.time()
logging.info(f"Reranking completed in {end_time - start_time:.2f} seconds")
return jsonify(rerank_results), 200
except Exception as e:
logging.error(f"Error during reranking: {e}")
return jsonify({"error": "An error occurred during reranking."}), 500
if __name__ == '__main__':
app.run(host="0.0.0.0", port=5000, debug=False)
注意事项:
在RerankerModel.rerank方法中,我们提供了一个query与passage的拼接方法(实际生产服务中可直接使用),该设计对passage较长的情况非常友好,无需担心长度限制问题。
