【学习记录】RAG优化系列:重排序(Reranking)——让检索结果更精准

在 RAG 系统中,初次检索(如向量相似度或 BM25)会召回一批候选文本块,但这些候选的排序可能不够精确。重排序(Reranking) 使用更精密但计算成本更高的模型(通常是 Cross-Encoder)对候选块进行重新打分和排序,从而将最相关的块排到前面。本文从原理、代码实现、效果评估到面试问答,全面解析重排序技术,助你提升 RAG 系统的检索质量。


📌 目录

  1. 为什么需要重排序?
  2. Bi-Encoder vs. Cross-Encoder
  3. 重排序在 RAG 中的工作流程
  4. 完整代码实现(LlamaIndex + 中文/英文模型)
  5. AI 评估:量化重排序的提升效果
  6. 面试官怎么问 & 怎么答
  7. 总结与最佳实践

一、为什么需要重排序?

传统的向量检索(Bi-Encoder)将问题和文档独立编码为向量,然后通过点积或余弦相似度快速匹配。这种方式虽然效率高,但缺乏细粒度的语义交互,容易导致:

  • 相关文档被排在后面
  • 不相关文档因为向量偶然相似而排在前面

重排序(Reranking) 使用 Cross-Encoder 模型,将问题与每个候选文档拼接后共同编码,输出一个相关性分数(如 0~1)。这种深度交互能捕捉更精确的语义匹配,从而修正初排顺序。


二、Bi-Encoder vs. Cross-Encoder

特性 Bi-Encoder(如 Sentence-BERT) Cross-Encoder(如 ms-marco-MiniLM-L-6-v2
编码方式 问题和文档独立编码 问题和文档拼接后联合编码
计算复杂度 O(1) 次编码 + 点积(适合大规模检索) O(N) 次编码(N 为候选数),较慢
精度 中等 较高(捕捉深层次交互)
典型用途 初筛(召回 Top-K) 精排(重排序 Top-N)

最佳实践两阶段检索

  1. 用 Bi-Encoder 快速召回 Top‑K(如 K=100)候选。
  2. 用 Cross-Encoder 对这 100 个候选打分,重排后取前 N(如 N=5)作为最终上下文。

三、重排序在 RAG 中的工作流程

用户问题

向量检索 Bi-Encoder

Top‑K 候选块

Cross-Encoder 重排序

Top‑N 精排块

LLM 生成答案

LlamaIndex 中的实现:通过 SentenceTransformerRerank 后处理器,直接添加到查询引擎。

from llama_index.core.postprocessor import SentenceTransformerRerank

reranker = SentenceTransformerRerank(model="cross-encoder/ms-marco-MiniLM-L-6-v2", top_n=3)
query_engine = index.as_query_engine(similarity_top_k=10, node_postprocessors=[reranker])

四、完整代码实现(LlamaIndex + 中文/英文模型)

以下示例演示如何构建索引、添加重排序器、执行查询,并使用 LLM 评估效果。

4.1 环境准备

pip install llama-index-core llama-index-embeddings-openai llama-index-llms-openai sentence-transformers openai

4.2 完整代码(英文模型示例)

import os
from llama_index.core import VectorStoreIndex, Document, Settings
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.core.postprocessor import SentenceTransformerRerank
from openai import OpenAI as OpenAIClient

# 配置 OpenAI(可替换为其他兼容接口)
os.environ["OPENAI_API_KEY"] = "your-api-key"
Settings.embed_model = OpenAIEmbedding(model="text-embedding-ada-002")
Settings.llm = OpenAI(model="gpt-3.5-turbo", temperature=0)

# 准备文档(模拟数据)
documents = [
    Document(text="机器学习是人工智能的一个子领域,它使计算机能够从数据中学习。"),
    Document(text="深度学习是机器学习的子集,使用多层神经网络。"),
    Document(text="强化学习通过与环境交互获得奖励信号。"),
    Document(text="自然语言处理是人工智能的重要分支。"),
    Document(text="计算机视觉让机器理解和分析图像。"),
]

# 构建索引
index = VectorStoreIndex.from_documents(documents)

# 1. 无重排序的查询引擎(直接取 top-3)
query_engine_no_rerank = index.as_query_engine(similarity_top_k=3)

# 2. 有重排序的查询引擎(先取 top-5,再重排序取前2)
reranker = SentenceTransformerRerank(
    model="cross-encoder/ms-marco-MiniLM-L-6-v2",
    top_n=2
)
query_engine_with_rerank = index.as_query_engine(
    similarity_top_k=5,
    node_postprocessors=[reranker]
)

query = "什么是深度学习?"

print("【无重排序】")
response_no = query_engine_no_rerank.query(query)
print(f"答案:{response_no}\n")

print("【有重排序】")
response_with = query_engine_with_rerank.query(query)
print(f"答案:{response_with}\n")

4.3 中文模型替换

若需支持中文,可以使用 BAAI/bge-reranker-base

from llama_index.core.postprocessor import SentenceTransformerRerank

reranker_zh = SentenceTransformerRerank(
    model="BAAI/bge-reranker-base",
    top_n=2
)

注意:bge-reranker-base 需要 sentence-transformers 库,首次运行会自动下载。


五、AI 评估:量化重排序的提升效果

我们可以使用 LLM 对检索到的 top-1 块进行相关性打分(0~10),比较有无重排序的差异。

def score_relevance(query: str, chunk_text: str) -> int:
    client = OpenAIClient(api_key=os.environ["OPENAI_API_KEY"])
    prompt = f"""请根据以下文本块与用户问题的相关程度,给出 0 到 10 之间的整数分数。
0=完全不相关,10=完全回答了问题。只输出数字。

用户问题:{query}
文本块:{chunk_text}
分数:"""
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.0,
        max_tokens=2
    )
    try:
        return int(response.choices[0].message.content.strip())
    except:
        return 0

# 获取检索到的第一个节点
def get_top_chunk(query_engine, query):
    retriever = query_engine._retriever
    nodes = retriever.retrieve(query)
    return nodes[0].text if nodes else ""

top_no = get_top_chunk(query_engine_no_rerank, query)
top_with = get_top_chunk(query_engine_with_rerank, query)

score_no = score_relevance(query, top_no)
score_with = score_relevance(query, top_with)

print(f"无重排序 top-1 相关性得分: {score_no}")
print(f"有重排序 top-1 相关性得分: {score_with}")

预期输出

无重排序 top-1 相关性得分: 7
有重排序 top-1 相关性得分: 9

说明重排序将更相关的文档推到了第一位。


六、面试官怎么问 & 怎么答

Q1:为什么要使用重排序?传统向量检索有什么不足?

:向量检索(Bi-Encoder)速度快,适合从海量文档中召回候选,但由于问题和文档被独立编码,它们之间的语义交互较少,排序可能不够精确。重排序使用 Cross-Encoder,将问题和文档拼接后共同编码,能捕捉更细粒度的相关性,显著提升排序质量。尤其在初次召回候选较多时,重排序可以精排前几名,提高最终答案的准确性。


Q2:Cross-Encoder 模型通常比 Bi-Encoder 慢,如何在实际系统中应用?

:采用两阶段策略:第一阶段用 Bi-Encoder 快速召回 Top‑K 候选(例如 K=100),第二阶段用 Cross-Encoder 对这 100 个候选精排,取前 N 个(如 N=5)。这样既保证了效率,又提升了精度。K 和 N 可根据延迟要求调整。


Q3:你选择的模型 ms-marco-MiniLM-L-6-v2 有什么特点?有没有中文替代模型?

:该模型是在 MS MARCO 数据集上训练的轻量级 Cross-Encoder,参数量小(约 22M),推理速度快,适合英文。对于中文,可以使用 BAAI/bge-reranker-basemoka-ai/m3e-reranker 等模型,它们支持中文且性能良好。


Q4:重排序器的 top_n 参数和检索器的 similarity_top_k 有什么关系?

similarity_top_k 是初次检索返回的候选数(例如 10),top_n 是重排序后保留的数量(例如 3)。通常 similarity_top_k 应大于 top_n,以便重排序有足够的候选进行精排。如果两者相等,重排序仅改变顺序,不减少数量。


Q5:如何评估重排序对 RAG 系统的实际提升?你会设计什么实验?

:我会使用一个包含 100 个查询及对应标准答案的测试集。分别运行无重排序和有重排序的 RAG 系统,对每个查询记录:

  • 检索到的 top-1 块的相关性得分(由 LLM 评分 0-10)。
  • 最终生成答案的忠实度(Faithfulness)和答案正确性。
    对比平均相关性得分和忠实度,如果提升显著(如 >10%),则表明重排序有效。同时监控延迟增加是否可接受。

Q6:在 LlamaIndex 中,如何添加多个后处理器(如先去重、再重排序)?

:可以在 node_postprocessors 列表中按顺序添加处理器,例如:

from llama_index.core.postprocessor import DuplicateFilter
node_postprocessors = [DuplicateFilter(), reranker]
query_engine = index.as_query_engine(node_postprocessors=node_postprocessors)

前一个处理器的输出将作为下一个的输入。


Q7:重排序是否总是能提升效果?有没有可能反而变差?

:如果 Cross-Encoder 模型与你的领域不匹配,或者初次召回质量极差(例如没有相关文档),重排序可能无法改善甚至引入偏差。因此,建议先在小规模数据集上验证模型的有效性,必要时在领域数据上微调 Cross-Encoder。


七、总结与最佳实践

维度 说明
核心思想 两阶段检索:Bi-Encoder 快速召回 → Cross-Encoder 精排
实现方式 LlamaIndex 中通过 SentenceTransformerRerank 后处理器
模型选择 英文:ms-marco-MiniLM-L-6-v2;中文:BAAI/bge-reranker-base
参数调优 similarity_top_k(召回数) > top_n(精排保留数)
评估方法 对比有无重排序的 top-1 相关性得分(LLM 打分)
适用场景 对检索精度要求高、初次召回候选较多(≥10)的系统
注意事项 重排序会增加延迟(约 10~50ms per candidate),需权衡性能

重排序是 RAG 系统中性价比很高的优化手段。它不需要改变索引结构,只需在查询时增加一次轻量级精排,就能显著提升最终答案的质量。掌握本文的原理和代码,你就能在自己的 RAG 项目中快速落地这一技术。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐