当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > RAG 检索结果怎么用 CrossEncoder 重新排序

RAG 检索结果怎么用 CrossEncoder 重新排序

来源:17golang原创 2026-09-06 06:22:47 0浏览 收藏

RAG 的召回结果不理想时,通常不用立刻更换向量库。更实用的做法是保留第一阶段的高效召回,再把查询和每个候选片段一起交给 CrossEncoder 重新打分,按分数取最终 Top-K。它能看到查询与文档的交互关系,但需要为每一对文本做一次推理,所以只适合重排一个有限的候选池。

推荐的链路是“先召回 Top-N,再用 CrossEncoder 重排,最后把 Top-K 放进提示词”。N 要大于 K,但不能大到让重排延迟吞掉 RAG 的响应预算。
要点速览
  • 双编码器或 BM25 负责从大语料中快速找候选,CrossEncoder 负责精排,不负责建立可索引的单文本向量。
  • 输入必须是 [query, document] 成对数据;排序只比较同一次查询下的分数。
  • MS MARCO 类模型可能输出原始 logit,排序时直接比较即可;只有展示概率或设阈值时才考虑 Sigmoid。

先把 RAG 的两阶段职责分开

在百万级语料中逐个计算查询与文档的 CrossEncoder 分数,成本会随候选数量线性增长。Sentence Transformers 的官方 Retrieve & Re-Rank 示例因此把流程拆成两段:双编码器或关键词检索先取出一小批候选,CrossEncoder 再给这些候选排序。第一段追求覆盖率,第二段追求相关性。

阶段输入与输出主要目标常见调参点
召回查询 + 大语料 → Top-N尽量不要漏掉答案向量模型、BM25、N
重排查询 + 每个候选 → 分数把真正相关片段提前模型、batch_size、max_length
组装Top-N → Top-K 上下文控制提示词长度和噪声K、去重、来源约束

例如召回 50 段、最终放入 5 段,就只计算 50 个查询-文档对,而不是对整个知识库做配对。这里的 N 和 K 不是固定标准:N 太小会让重排器没有机会找回漏掉的答案,K 太大又会把相似但无关的片段塞回上下文。

RAG 双阶段检索中召回候选、CrossEncoder 查询文档配对和最终 Top-K 上下文的静态关系框图
图1:RAG 先从大语料得到有限候选,再把查询与候选片段成对交给 CrossEncoder。

用 CrossEncoder 给候选片段逐对打分

下面示例使用 Sentence Transformers 文档中的常见 MS MARCO CrossEncoder。模型名只是示例,生产环境应结合中文语料、领域和延迟预算选择经过验证的模型。候选文档保留 id、正文和来源,重排后只改变顺序,不丢掉这些元数据。

from sentence_transformers import CrossEncoder

# 用一个查询和多个候选片段组成成对输入。
query = "如何为 RAG 检索结果增加重排阶段"
candidates = [
    {"id": "doc-1", "text": "先用向量检索得到候选,再用 CrossEncoder 重新排序。", "source": "rag-guide"},
    {"id": "doc-2", "text": "BM25 适合按关键词匹配文档。", "source": "search-note"},
    {"id": "doc-3", "text": "CrossEncoder 同时读取查询和候选文本,输出相关性分数。", "source": "model-note"},
]

# 加载可用于句对相关性打分的模型;首次运行会下载模型文件。
model = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")
pairs = [[query, item["text"]] for item in candidates]
scores = model.predict(pairs, batch_size=16, show_progress_bar=False)

# 分数只用于同一查询下排序,元数据随候选一起保留。
ranked = sorted(zip(candidates, scores), key=lambda pair: float(pair[1]), reverse=True)
top_k = [{**item, "rerank_score": float(score)} for item, score in ranked[:2]]
for item in top_k:
    print(item["id"], item["rerank_score"], item["source"])

predict 接收的是句对列表,而不是一组孤立句子。示例里的 batch_size=16 只影响推理批次,不改变排序逻辑;显存不足时可以调小,吞吐允许时再逐步调大。scores 可能是 NumPy 数组或类似序列,转成 Python float 后再写入结果,便于序列化和日志记录。

CrossEncoder 将同一个 RAG 查询分别与三个候选片段配对并输出可排序分数的静态技术框图
图2:同一个查询与候选片段逐对配对,分数用于相对排序,候选的 ID 和来源继续随结果返回。

把重排结果接回 RAG 上下文

重排结束后,不要只把一串文本直接拼进提示词。建议先按分数取 K,再做轻量去重和长度裁剪,最后保留来源标识。这样模型既能看到靠前的证据,也能在回答中指出证据来自哪一个片段。

# 只选重排后的前 K 个片段,并限制单片段长度。
def build_context(ranked_items, top_k=5, max_chars=1200):
    blocks = []
    seen = set()
    for item in ranked_items[:top_k]:
        # 用文档 ID 去重,避免同一片段的不同切片重复占用上下文。
        if item["id"] in seen:
            continue
        seen.add(item["id"])
        text = item["text"].strip()[:max_chars]
        blocks.append(f"[{item['source']}] {text}")
    return "\n\n".join(blocks)

context = build_context(top_k, top_k=2)
prompt = f"请只依据以下资料回答问题:\n{context}\n\n问题:{query}"
print(prompt)

最终的 K 应由提示词窗口、回答任务和离线评估共同决定。问答任务可以从较小的 K 开始;需要跨段落综合时,宁可扩大召回 N 并评估重排后的 Recall@K,也不要只凭一次示例把 K 写死。

用指标和边界控制重排成本

性能测试至少记录“每次查询的候选数 N、最终 K、重排耗时、端到端 P95 延迟、Recall@K 或 nDCG@K”。可以固定一组真实问题,分别比较不重排、N=20 和 N=50 的结果;如果 N 增大后 Recall@K 不再提升,却持续增加延迟,就没有必要继续扩大候选池。

现象优先检查调整方向
最终答案相关性没提升召回池是否已经漏掉正确片段先提升第一阶段 Recall,再谈重排
延迟突然变高N、文本长度和 batch_size限制候选与片段长度,按显存调整批次
分数看起来不像概率模型的输出定义排序直接比较;需要概率时再配置 Sigmoid
中文领域结果不稳定模型训练语料与领域差异准备标注查询-片段对,评估或微调领域模型

CrossEncoder 的分数不是跨模型、跨查询都可直接比较的业务概率。它最可靠的用途是:在同一个查询的候选集合中排序。若业务要按阈值过滤,先用带标注的验证集校准阈值;若候选规模本身很小,也可以直接对全部候选打分,但不要把这种做法照搬到大语料全量扫描。

常见问题

CrossEncoder 能替代向量检索吗?

通常不能。它需要为查询和候选逐对计算,适合第二阶段重排;大规模语料仍应先用向量检索、BM25 或其他高效方法缩小候选集。

分数必须转换成 0 到 1 吗?

不必须。排序只需要比较相对大小。Sentence Transformers 文档说明部分 MS MARCO 模型输出 raw logits;若展示概率或按概率阈值决策,再使用 Sigmoid,并重新用验证集确认阈值。

N=100、K=5 是固定最佳值吗?

不是。N 影响召回覆盖和重排成本,K 影响上下文噪声与提示词长度。用真实查询集同时看 Recall@K、nDCG@K 和 P95 延迟,按业务预算选择。

落地时可以先从“召回 20 段、重排后取 5 段”做基线,再逐步增加候选数。只要把召回覆盖、重排排序和上下文组装分别记录,RAG 的问题就能从“感觉答得不准”变成可测量、可调参的检索链路。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 用 IP 连接 HTTPS 为什么会证书域名不匹配Go 用 IP 连接 HTTPS 为什么会证书域名不匹配
上一篇
Go 用 IP 连接 HTTPS 为什么会证书域名不匹配
Go 泛型函数怎么给不同类型的切片去重
下一篇
Go 泛型函数怎么给不同类型的切片去重
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    159次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    87次使用
  • ClickPrompt:AI提示词生成与优化工具,支持Stable Diffusion、ChatGPT及代码辅助
    ClickPrompt
    ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
    47次使用
  • PromptHero官网:AI提示词搜索、优化与学习平台,支持Midjourney/Stable Diffusion
    PromptHero
    PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
    30次使用
  • OpenArt免费开源指南:Stable Diffusion Prompt Book提示词手册详解
    Stable Diffusion Prompt Book
    深入解析OpenArt推出的Stable Diffusion Prompt Book,这本免费的开源提示词指南涵盖从基础语法到高级技巧,提供风格化词库与参数建议,助您优化AI绘画生成效果。
    31次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码