本文深入探讨了RAG系统中重排序(Reranking)的关键作用,即通过RRF、RankLLM、CrossEncoder等策略对初始检索结果进行二次排序,提升大模型接收到的上下文质量。文章详细解析了三种主流重排序方法的原理、优劣势及适用场景,并通过代码实例展示了具体实现过程,旨在帮助开发者构建更精准、高效的检索系统。


**“**通过重排序策略对初始检索的候选文档重新打分排序,将最相关的内容提升到前列,从而提升大模型的上下文质量。”

RAG 系统中的重排序(Reranking)是对初始检索结果进行二次排序的核心后处理环节。初始检索通常依赖向量相似度或关键词匹配,返回的结果在语义相关性上存在噪声,重排序通过更精细的相关性判断模型对候选文档重新打分,将最相关的内容提升到前列,从而提高大模型接收到的上下文质量。

下面我们就来分享下重排序的几种方法

01

RRF重排

RRF(Reciprocal Rank Fusion)是一种简单而有效的多检索结果融合算法,它通过将多个检索查询的结果进行排名合并,来提高检索的准确性和覆盖面。

核心思想:

  1. 对于同一个用户问题,生成多个不同角度的查询
  2. 分别对每个查询进行检索
  3. 使用RRF算法将多个检索结果列表融合成一个统一的排序列表
  4. RRF算法为每个文档分配分数:score = 1/(rank + k),其中rank是该文档在某个结果列表中的排名

优势:实现简单、速度快、没有算力成本

劣势:缺乏深层语义理解,精度弱于模型类重排算法

流程图

代码样例

import osimport uuidfrom dotenv import load_dotenvfrom openai import OpenAIfrom langchain_classic.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.vectorstores import Milvusfrom langchain_classic.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.embeddings import Embeddingsfrom langchain_core.documents import Documentfrom langchain_openai import ChatOpenAIfrom langchain_classic.load import dumps, loadsload_dotenv()# ===================== 配置初始化 =====================OPENAI_API_KEY = os.getenv("GLM_API_KEY")OPENAI_BASE_URL = os.getenv("GLM_BASE_URL")EMBEDDING_MODEL = os.getenv("GLM_EMBEDDING_MODEL", "embedding-3")MILVUS_URI = os.getenv("MILVUS_URI", "http://localhost:19530")COLLECTION_NAME = "finance_report_rrf"# 兼容 LangChain 的嵌入适配器class OpenAIEmbeddingsAdapter(Embeddings):    def __init__(self, client, model):        self.client = client        self.model = model    def embed_documents(self, texts):        response = self.client.embeddings.create(input=texts, model=self.model)        return [item.embedding for item in response.data]    def embed_query(self, text):        return self.embed_documents([text])[0]client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)embed_model = OpenAIEmbeddingsAdapter(client, EMBEDDING_MODEL)# ===================== 文档加载 =====================def parse_finance_document():    """构建财务分析示例文档"""    texts = [        "悦享餐饮2020-2023财务综合分析报告:短期偿债能力分析。"        "2020~2023年间,悦享餐饮速动比率分别为2.85、2.07、1.48和1.53,"        "流动比率分别为2.12、2.24、1.61和1.68,整体呈先降后升趋势。"        "2022年受非流动资产增加影响,流动比率大幅回落;"        "2023年加强应付账款管控,流动资产增加,短期偿债能力略有改善。",        "悦享餐饮2020-2023财务综合分析报告:长期偿债能力分析。"        "资产负债率先升后降(21.56%->22.03%->28.15%->26.87%),"        "产权比率同步变动(28.0%->39.9%->36.7%)。"        "2022年受外部冲击,应付账款大增,资产负债率攀升至28.15%;"        "2023年优化资产结构,总资产增至188652.47万元,负债回落。",        "悦享餐饮2020-2023财务综合分析报告:营运能力分析。"        "应收账款周转率从25.12次降至15.34次后回升至18.65次;"        "存货周转率波动较大(10.87->8.23->12.35->11.68)。",        "悦享餐饮2020-2023财务综合分析报告:盈利能力分析。"        "2020~2021年毛利率稳定在60%左右,2022年大幅下滑至-10.23%,2023年回升至8.65%。"        "净资产收益率同步下降,主要受门店扩张成本和疫情影响。",        "悦享餐饮2020-2023财务综合分析报告:发展能力分析。"        "2022年新增5家门店,成本3256.87万元,加剧资金压力;2023年暂停扩张。"        "营业收入增长率从2021年的15.2%下降至2022年的-8.3%,2023年恢复至5.1%。",    ]    return [Document(page_content=t) for t in texts]def reciprocal_rank_fusion(results: list[list], k=60):    """    RRF(Reciprocal Rank Fusion)算法实现    参数:        results (list[list]): 多个检索结果列表,每个列表包含按相关性排序的文档        k (int): RRF算法的调节参数,默认值60(经验值)    返回:        list: 融合后的(文档, 分数)元组列表,按分数降序排序    算法原理:        score = 1 / (rank + k),同一文档出现在多个列表中时累加分数    """    fused_scores = {}    for docs in results:        for rank, doc in enumerate(docs):            doc_str = dumps(doc)            if doc_str not in fused_scores:                fused_scores[doc_str] = 0            fused_scores[doc_str] += 1 / (rank + k)    reranked_results = [        (loads(doc), score)        for doc, score in sorted(fused_scores.items(), key=lambda x: x[1], reverse=True)    ]    return reranked_results# ===================== 构建向量索引 =====================print("正在加载文档...")docs = parse_finance_document()text_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50)splits = text_splitter.split_documents(docs)print(f"文档已切分为 {len(splits)} 个文本块")print("正在创建 Milvus 向量索引...")vectorstore = Milvus(    embedding_function=embed_model,    collection_name=COLLECTION_NAME,    connection_args={"uri": MILVUS_URI},    auto_id=True,)vectorstore.add_documents(splits)retriever = vectorstore.as_retriever(search_kwargs={"k": 3})print("向量索引创建完成")# ===================== 配置多查询生成器 =====================template = """你是一个帮助用户生成多个搜索查询的助手。请根据以下问题生成4个不同角度的相关搜索查询,这些查询应该:1. 从不同的角度理解原问题2. 使用不同的关键词和表达方式3. 覆盖问题的不同方面原问题:{question}请生成4个相关的搜索查询:"""prompt_rag_fusion = ChatPromptTemplate.from_template(template)llm = ChatOpenAI(    model="glm-4", temperature=0, api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)generate_queries = (    prompt_rag_fusion    | llm    | StrOutputParser()    | (lambda x: [q.strip() for q in x.split("\n") if q.strip()]))# ===================== 测试 RRF 重排 =====================questions = [    "悦享餐饮的短期偿债能力怎么样?",    "2022年毛利率为什么下滑?",    "门店扩张对财务有什么影响?",]for idx, question in enumerate(questions, 1):    print(f"\n{'='*50}")    print(f"问题 {idx}: {question}")    print('='*50)    # 生成多个查询    queries = generate_queries.invoke({"question": question})    print(f"生成了 {len(queries)} 个查询:")    for i, query in enumerate(queries, 1):        print(f"  查询 {i}: {query}")    # 对每个查询进行检索    all_results = []    for query in queries:        query_docs = retriever.invoke(query)        all_results.append(query_docs)    # 使用RRF算法融合结果    reranked_docs = reciprocal_rank_fusion(all_results)    # 展示最终结果    print(f"\nRRF重排结果(前3个):")    for i, (doc, score) in enumerate(reranked_docs[:3], 1):        content_preview = doc.page_content[:200].replace('\n', ' ').strip()        print(f"\n  排名 {i} (RRF分数: {score:.4f}):")        print(f"    内容: {content_preview}...")        if doc.metadata:            print(f"    来源: {doc.metadata.get('source', '未知')}")

02

RankLLM重排

利用大语言模型(LLM)的深度语言理解能力进行文档重排,通过 prompt engineering 引导 LLM 对每个文档进行相关性评分。

优势:语义理解最深、推理能力强、可解释性好

劣势:成本高(LLM API调用)、延迟大

流程图

代码样例

import osfrom dotenv import load_dotenvfrom openai import OpenAIfrom langchain_community.vectorstores import Milvusfrom langchain_core.embeddings import Embeddingsfrom langchain_core.documents import Documentfrom langchain_openai import ChatOpenAIfrom langchain_core.prompts import ChatPromptTemplatefrom langchain_core.output_parsers import StrOutputParserload_dotenv()# ===================== 配置初始化 =====================OPENAI_API_KEY = os.getenv("GLM_API_KEY")OPENAI_BASE_URL = os.getenv("GLM_BASE_URL")EMBEDDING_MODEL = os.getenv("GLM_EMBEDDING_MODEL", "embedding-3")MILVUS_URI = os.getenv("MILVUS_URI", "http://localhost:19530")COLLECTION_NAME = "finance_report_rankllm"class OpenAIEmbeddingsAdapter(Embeddings):    def __init__(self, client, model):        self.client = client        self.model = model    def embed_documents(self, texts):        response = self.client.embeddings.create(input=texts, model=self.model)        return [item.embedding for item in response.data]    def embed_query(self, text):        return self.embed_documents([text])[0]client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)embed_model = OpenAIEmbeddingsAdapter(client, EMBEDDING_MODEL)# ===================== 文档加载 =====================texts = [    "悦享餐饮2020-2023财务综合分析报告:短期偿债能力分析。"    "2020~2023年间,速动比率分别为2.85、2.07、1.48和1.53,"    "流动比率分别为2.12、2.24、1.61和1.68,整体呈先降后升趋势。",    "悦享餐饮2020-2023财务综合分析报告:长期偿债能力分析。"    "资产负债率先升后降(21.56%->22.03%->28.15%->26.87%),"    "2022年受外部冲击,应付账款大增,资产负债率攀升至28.15%。",    "悦享餐饮2020-2023财务综合分析报告:盈利能力分析。"    "2020~2021年毛利率稳定在60%左右,2022年大幅下滑至-10.23%,2023年回升至8.65%。"    "净资产收益率同步下降,主要受门店扩张成本和疫情影响。",    "悦享餐饮2020-2023财务综合分析报告:发展能力分析。"    "2022年新增5家门店,成本3256.87万元,加剧资金压力;2023年暂停扩张。"    "营业收入增长率从2021年的15.2%下降至2022年的-8.3%,2023年恢复至5.1%。",    "悦享餐饮2020-2023财务综合分析报告:营运能力分析。"    "应收账款周转率从25.12次降至15.34次后回升至18.65次;"    "存货周转率波动较大(10.87->8.23->12.35->11.68)。",]docs = [Document(page_content=t) for t in texts]# ===================== 创建 Milvus 向量检索器 =====================vectorstore = Milvus(    embedding_function=embed_model,    collection_name=COLLECTION_NAME,    connection_args={"uri": MILVUS_URI},    auto_id=True,)vectorstore.add_documents(docs)retriever = vectorstore.as_retriever(search_kwargs={"k": 5})# ===================== LLM 重排器 =====================grade_prompt = ChatPromptTemplate.from_messages([    ("system", "你是一个文档相关性评分员。根据用户问题对文档进行评分。\n"              "只输出一个0-10的整数分数,10表示高度相关,0表示完全无关。"),    ("human", "用户问题:{question}\n\n文档内容:{document}\n\n相关性分数(0-10):"),])llm = ChatOpenAI(model="glm-4", temperature=0, api_key=OPENAI_API_KEY, base_url=OPENAI_BASE_URL)grader = grade_prompt | llm | StrOutputParser()def llm_rerank(query, docs, top_n=3):    """    LLM 重排:对每个文档调用 LLM 评分,按分数降序返回前 top_n 个    参数:        query: 用户查询        docs: 候选文档列表        top_n: 返回文档数    返回:        list: 按 LLM 评分排序的 (文档, 分数) 元组列表    """    scored_docs = []    for doc in docs:        try:            score_str = grader.invoke({                "question": query,                "document": doc.page_content            })            score = int(score_str.strip())        except (ValueError, TypeError):            score = 0        scored_docs.append((doc, score))    scored_docs.sort(key=lambda x: x[1], reverse=True)    return scored_docs[:top_n]# ===================== 执行查询和重排 =====================query = "2022年毛利率为什么下滑?"# 第一阶段:Milvus 向量检索候选文档candidate_docs = retriever.invoke(query)# 第二阶段:LLM 重排reranked = llm_rerank(query, candidate_docs, top_n=3)print(f"查询: {query}\n")print("LLM 重排结果(前3个):")for i, (doc, score) in enumerate(reranked, 1):    print(f"\n  排名 {i} (相关性分数: {score}/10):")    print(f"    内容: {doc.page_content[:150]}...")    if doc.metadata:        print(f"    来源: {doc.metadata.get('source', '未知')}")

03

CrossEncoder重排

CrossEncoder是一种基于BERT的双向编码器重排模型,将查询和文档作为一个整体输入,利用[CLS]标记的输出预测相关性分数,能够捕捉查询与文档之间的深层交互。

优势:精度高、语义理解强

劣势:计算开销大,每个查询-文档对需单独编码,不适合第一阶段检索(从海量的文档里快速粗筛选出少量的候选文档)

流程图

代码样例

from transformers import AutoTokenizer, AutoModelForSequenceClassificationimport torch# 1. 加载预训练的CrossEncoder模型model_name = "cross-encoder/ms-marco-MiniLM-L-12-v2"tokenizer = AutoTokenizer.from_pretrained(model_name)model = AutoModelForSequenceClassification.from_pretrained(model_name)def encode_and_score(query, docs):    """    计算查询与每个文档的相关性分数    输入格式: [CLS] query [SEP] document [SEP]    分数越高表示相关性越强    """    scores = []    for doc in docs:        inputs = tokenizer(            query, doc,            return_tensors="pt",            truncation=True,            max_length=512,            padding="max_length"        )        with torch.no_grad():            outputs = model(**inputs)            score = outputs.logits[0][0].item()            scores.append(score)    return scores# 2. 测试数据(财务分析场景)query = "悦享餐饮2022年毛利率为什么下滑?"documents = [    "2020~2021年毛利率稳定在60%左右,2022年大幅下滑至-10.23%,2023年回升至8.65%。"    "净资产收益率同步下降,主要受门店扩张成本和疫情影响。",    "2022年新增5家门店,成本3256.87万元,加剧资金压力;2023年暂停扩张。"    "营业收入增长率从2021年的15.2%下降至2022年的-8.3%。",    "2020~2023年间,速动比率分别为2.85、2.07、1.48和1.53,"    "流动比率分别为2.12、2.24、1.61和1.68,整体呈先降后升趋势。",    "资产负债率先升后降(21.56%->22.03%->28.15%->26.87%),"    "2022年受外部冲击,应付账款大增,资产负债率攀升至28.15%。",]# 3. 执行CrossEncoder重排scores = encode_and_score(query, documents)ranked_docs = sorted(zip(documents, scores), key=lambda x: x[1], reverse=True)# 4. 输出重排结果print(f"查询: {query}\n")print("CrossEncoder重排结果(按相关性分数降序):")for rank, (doc, score) in enumerate(ranked_docs, start=1):    if score > 0:        level = "高度相关"    elif score > -2:        level = "中等相关"    else:        level = "低相关"    print(f"\n  排名 {rank} (分数: {score:.4f}, {level}):")    print(f"    {doc[:100]}...")

04

总结

重排算法 核心原理 核心优势 主要劣势 典型使用场景
RRF 多维度查询检索,通过公式1/(rank+k)融合排名得分 实现简单、速度快、无算力成本 无深层语义理解,精度偏低,依赖查询质量 多路召回合并、高并发快速检索
RankLLM 借助大模型 + 提示词,逐文档人工规则化打分排序 语义理解极强、具备逻辑推理、结果可解释 调用成本高、接口延迟大 专业问答、复杂推理、需要结果解释
CrossEncoder 拼接问句与文档整体编码,依靠 BERT 双向交互计算相关分 语义匹配精准、相关性判别能力强 逐对编码耗时高、算力开销大 搜索业务精排、RAG 少量候选文档排序

总的来说,通过重排序提升检索结果排序精度,将原始的初始检索结果打磨为高质量的生成上下文。好了,RAG系统的重排序就分享到这儿,在座的亦菲、彦祖们有想要讨论的。

最后唠两句

为什么AI大模型成为越来越多程序员转行就业、升职加薪的首选

很简单,这些岗位缺人且高薪

智联招聘的最新数据给出了最直观的印证:2025年2月,AI领域求职人数同比增幅突破200% ,远超其他行业平均水平;整个人工智能行业的求职增速达到33.4%,位居各行业榜首,其中人工智能工程师岗位的求职热度更是飙升69.6%。

AI产业的快速扩张,也让人才供需矛盾愈发突出。麦肯锡报告明确预测,到2030年中国AI专业人才需求将达600万人,人才缺口可能高达400万人,这一缺口不仅存在于核心技术领域,更蔓延至产业应用的各个环节。

那0基础普通人如何学习大模型 ?

深耕科技一线十二载,亲历技术浪潮变迁。我见证那些率先拥抱AI的同行,如何建立起效率与薪资的代际优势。如今,我将积累的大模型面试真题、独家资料、技术报告与实战路线系统整理,分享于此,为你扫清学习困惑,共赴AI时代新程。

我整理出这套 AI 大模型突围资料包【允许白嫖】:

  • ✅从入门到精通的全套视频教程
  • ✅AI大模型学习路线图(0基础到项目实战仅需90天)
  • ✅大模型书籍与技术文档PDF
  • ✅各大厂大模型面试题目详解
  • ✅640套AI大模型报告合集
  • ✅大模型入门实战训练

这份完整版的大模型 AI 学习和面试资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

在这里插入图片描述

①从入门到精通的全套视频教程

包含提示词工程、RAG、Agent等技术点

② AI大模型学习路线图(0基础到项目实战仅需90天)

全过程AI大模型学习路线

③学习电子书籍和技术文档

市面上的大模型书籍确实太多了,这些是我精选出来的

④各大厂大模型面试题目详解

⑤640套AI大模型报告合集

⑥大模型入门实战训练

如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的“投资”换来的是终身受益!

应届毕业生‌:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。

零基础转型‌:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界‌。

业务赋能 ‌突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型‌。

👉获取方式:
有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐