RAG 添加重排序 (Reranking)

AI摘要
这是一篇关于在RAG(检索增强生成)系统中集成重排序(Reranking)功能的技术教程。文章详细介绍了两种实现方案:一是使用智源开源的BGE-reranker模型进行本地免费部署,二是使用Cohere的付费API。内容包含完整的代码示例、安装步骤、集成方法以及调优建议,旨在帮助开发者构建从“改写→检索→重排序→生成”的完整工业级RAG链路。属于【知识分享】。

在你的分层架构中集成重排序(Reranking)同样非常干净——只需在 RAGServiceask 方法中,检索合并后生成前插入一个“重排序”步骤。

我会为你提供两种主流方案的集成代码,你可以根据预算和隐私需求选择:


🔧 方案一:免费、本地部署(推荐个人开发者)

使用 BGE-reranker(由智源开源),效果媲美 Cohere,数据不出网,完全免费。

1. 安装依赖

pip install FlagEmbedding

2. 创建重排序模块

src/infrastructure/rerankers/ 目录下新建 bge_reranker.py

# src/infrastructure/rerankers/bge_reranker.py
from typing import List, Tuple
from langchain_core.documents import Document
from FlagEmbedding import FlagReranker

class BGEReranker:
    def __init__(self, model_name: str = "BAAI/bge-reranker-v2-m3", use_fp16: bool = True):
        """
        BGE 重排序模型
        - model_name: 可选 "BAAI/bge-reranker-v2-m3"(多语言,推荐)或 "BAAI/bge-reranker-large"
        - use_fp16: 节省显存和内存
        """
        self.reranker = FlagReranker(model_name, use_fp16=use_fp16)

    def rerank(self, query: str, documents: List[Document], top_n: int = 3) -> List[Document]:
        """
        根据相关性对文档重新排序,返回 top_n 个最相关的文档
        """
        if not documents:
            return []

        # 构造 (query, document) 对
        pairs = [[query, doc.page_content] for doc in documents]
        # 计算相关性分数
        scores = self.reranker.compute_score(pairs, normalize=True)  # 归一化到 0~1

        # 按分数降序排序,取前 top_n
        sorted_pairs = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)
        reranked_docs = [doc for doc, _ in sorted_pairs[:top_n]]

        print(f"🔄 重排序完成:从 {len(documents)} 个文档中选出 Top-{len(reranked_docs)}")
        return reranked_docs

3. 集成到 RAGService

修改 src/application/services/rag_service.py,在 __init__ 中初始化 Reranker,并在 ask 中调用:

# src/application/services/rag_service.py
from typing import List, Tuple
from langchain_core.documents import Document
from langchain_core.prompts import PromptTemplate
from src.application.services.query_rewriter import QueryRewriter
from src.infrastructure.rerankers.bge_reranker import BGEReranker  # 新增

class RAGService:
    def __init__(self, llm_client, retriever, top_k_rerank: int = 3):
        self.llm = llm_client
        self.retriever = retriever
        self.rewriter = QueryRewriter(llm_client)
        # ✅ 初始化重排序器
        self.reranker = BGEReranker()
        self.top_k_rerank = top_k_rerank

        self.prompt_template = PromptTemplate.from_template(
            "你是一个专业的AI知识助手。请基于以下上下文回答用户的问题。\n"
            "如果上下文不足以回答,请如实说明,不要编造。\n\n"
            "上下文:\n{context}\n\n"
            "用户问题:{question}\n\n"
            "回答:"
        )

    def format_docs(self, docs: List[Document]) -> str:
        return "\n\n".join([doc.page_content for doc in docs])

    def ask(self, query: str) -> Tuple[str, List[Document]]:
        # 1. 查询改写
        rewritten_queries = self.rewriter.rewrite(query)

        # 2. 多路检索 + 合并去重
        all_docs = []
        seen_contents = set()
        for q in rewritten_queries:
            docs = self.retriever.invoke(q)
            for doc in docs:
                content = doc.page_content.strip()
                if content and content not in seen_contents:
                    seen_contents.add(content)
                    all_docs.append(doc)
        print(f"🔍 多查询检索后,去重合并共召回 {len(all_docs)} 个片段")

        # ---------- 3. 新增:重排序 ----------
        if all_docs:
            all_docs = self.reranker.rerank(query, all_docs, top_n=self.top_k_rerank)

        # 4. 构建上下文并生成
        context = self.format_docs(all_docs)
        final_prompt = self.prompt_template.format(context=context, question=query)
        try:
            response = self.llm.invoke(final_prompt)
            answer = response.content
        except Exception as e:
            answer = f"生成失败: {e}"

        return answer, all_docs

4. 在 main.py 中微调(可选)

你可以在 RAGService 的构造中增加参数控制 top_k_rerank(最终送入 LLM 的文档数),例如:

rag_service = RAGService(
    llm_client=llm,
    retriever=ensemble_retriever,
    top_k_rerank=3   # 只取最相关的3个片段给大模型
)

💰 方案二:Cohere Rerank API(企业级,付费,但效果顶级)

如果你预算充足,可以换成 Cohere 的 API(每月有免费额度)。

安装

pip install cohere

创建 cohere_reranker.py

# src/infrastructure/rerankers/cohere_reranker.py
import cohere
from typing import List
from langchain_core.documents import Document

class CohereReranker:
    def __init__(self, api_key: str, model: str = "rerank-multilingual-v3.0"):
        self.client = cohere.Client(api_key)
        self.model = model

    def rerank(self, query: str, documents: List[Document], top_n: int = 3) -> List[Document]:
        if not documents:
            return []
        # 准备文档列表(只传文本内容)
        doc_texts = [doc.page_content for doc in documents]
        response = self.client.rerank(
            query=query,
            documents=doc_texts,
            model=self.model,
            top_n=top_n
        )
        # 按返回的索引重排文档
        reranked_docs = [documents[res.index] for res in response.results]
        print(f"🔄 Cohere 重排序完成,保留 Top-{len(reranked_docs)}")
        return reranked_docs

然后在 RAGService 中替换 BGEReranker()CohereReranker(api_key=settings.COHERE_API_KEY)(记得在 settings.py 中添加该配置)。


🧪 验证效果

运行你的 Gradio 界面,输入一个复杂问题,观察终端输出:

🔍 多查询检索后,去重合并共召回 12 个片段
🔄 重排序完成:从 12 个文档中选出 Top-3

你可以在 respond 中打印重排序后的文档来源,会发现相关性明显提升。


💡 进阶调优

  • 调整检索器召回数量:在你的 ensemble_retriever 中,k 值可以调大(如 k=10),让重排序器有更多候选,然后重排序只取前 top_n=3。这样能在最终答案质量与延迟间取得平衡。
  • 缓存模型:BGE-reranker 首次加载会下载约 2GB 模型,请耐心等待。之后会自动缓存到本地。

现在你的 RAG 机器人已经拥有 “改写 → 检索 → 重排序 → 生成” 的完整工业级链路,距离“企业级智能 Agent”又近了一大步!🚀 如果运行中遇到模型加载慢或内存不足,随时告诉我,我教你用更小的模型。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!