Rerank:花小钱办大事的精排
约 2439 字大约 8 分钟
2026-09-25
检索侧的第二场手术,也是这个系列里我最想安利的一个组件。上一篇混合检索把「该捞的都捞回来了」,但 20 条候选里鱼龙混杂;今天请守门员上场,把真正能答题的 3 到 5 条挑出来。如果整个系列你只打算抄一个优化,抄这篇。 本篇对应 relax-admin 的「会话洞察 → 检索评估」(看 Rerank 分数列)和「流量管控 → 降级与容错」(Rerank 挂了怎么办)。
为什么召回之后还需要精排
先讲架构逻辑,这是面试也爱问的一块。
混合检索的两条通道,不管向量还是 BM25,本质上都是粗排:它们快,是因为「查询和文档从来没见过面」。向量通道是两边各自提前算好指纹,查询时比一下距离;BM25 是对着倒排索引数词频。这种「没见过面」的打分天生就粗——它不知道词和词之间怎么互相呼应,读不懂否定,看不出来「这段话虽然词都对上了,但压根没在回答你问的事」。
Rerank 模型是另一种生物,叫交叉编码器(Cross-Encoder):它把「问题 + 候选段落」拼在一起送进模型,逐词过注意力,让「不适合」这三个字亲眼看见「缓存」这个词,然后给出一个分数:这段话到底能不能回答这个问题。
一个我特别喜欢的比方:粗排是看两人照片猜合不合适,精排是把俩人拉来当面聊一场。 当面聊当然准,当然也当然贵——每对组合都要实时算一遍,没法预计算。所以它只能待在漏斗末端,对已经缩到 20 条的候选集下手。
两级漏斗的分工,一张图钉死:
全库 3000 段
→ 粗排(向量+BM25+RRF)捞 20 条 快而粗,~100ms
→ 精排(Rerank)挑 3~5 条 慢而准,~200ms
→ 进 Prompt这个「召回-精排」两段式是搜索引擎验证了十几年的架构,RAG 只是把它搬了进来。
面试答题模板
回答「为什么用了向量检索还要 Rerank」:Bi-Encoder 和 Cross-Encoder 的区别是精度换延迟——前者可预计算、快、无交互、粗;后者拼接逐词交互、准、慢、只能实时。所以放漏斗末端。答到这个层面基本就到位了。
动手:封装一个 RerankService
Java AI 目前对 Rerank 没有统一抽象(各家模型 API 形态不一),需要自己封一个,好在逻辑简单:
public interface RerankService {
List<ScoredChunk> rerank(String query, List<ScoredChunk> candidates);
}实现就三步:把 query 和每条候选拼成对 → 调 Rerank 模型 API(DashScope 的 gte-rerank、Jina Reranker 这类,选一个有稳定 API 的)→ 按返回分数重排。几十行代码的事,重点是参数:
候选集别贪大。 20 条是舒适区,200 条丢给交叉编码器,延迟从两百毫秒变两秒,检索段的体验直接崩掉。
top_n 取 3 到 5。 大模型读 5 条精选证据的效果,好过读 20 条泥沙俱下的——上下文不是越多越好,噪声证据会带偏模型,这在第 12 篇还会展开。
分数阈值是真正的宝贝。 Rerank 分数有明确的语义:「这段话能不能回答这个问题」。设一条及格线(按你用的模型试探,比如 0.3),低于它的候选直接扔,扔回来的候选凑不够数就走「拒答」分支——不是硬凑着答,而是回复「博客里暂时没有关于这个问题的内容」。
这个闸门为什么重要,值得单独说一段。
拒答闸门:RAG 系统的美德
想象两种知识库助手:一种什么都敢答,你问它月球房价它都能编个首付比例出来;另一种偶尔说「我的资料里没有这个」。
第二种可信得多。 大模型的默认性格是有问必答,没有证据它就现编——幻觉的源头不在模型「坏」,在它「太配合」。RAG 系统要反过来教它:答不出就说答不出。
而「答不出」的判断不能靠 Prompt 里一句「不确定时请说不知道」(这是概率性约束,模型嘴上答应手上照编),得有硬闸门:
拒答的流程控制
Rerank 分数不够 = 检索层面就没找到证据 = 直接走拒答分支,根本不进生成。这是把「不知道」从模型的道德自觉,变成系统的流程控制。
所以后面第 14 篇做评测时,会有一个专门的指标叫拒答准确率:该拒的拒了没(知识库外的问题有没有被硬答)、不该拒的有没有误杀(库里明明有却装不知道)。一个 RAG 系统的成熟度,看拒答比看答题更准。
顺手解决上一篇的遗留
上篇留了个尾巴:「哪些场景不适合缓存」在混合检索后只是缓解——「不适合」字面对上了,但排序里「缓存配置大全」那段还是紧咬着。Rerank 上场后这类问题迎来终局:交叉编码器把「问题 + 段落」放在一起逐词读,「不」字终于被看见了。「不适合的场景」段落分数明显高于「适用场景配置」,断层胜出。
否定语义的理解,是 Cross-Encoder 相对向量检索最直观的碾压局。 你的基线档案里如果也有这类翻车记录,上了 Rerank 之后回头看一眼,很治愈。
在「检索评估」页里,这一刻的呈现就是结果列表多了一列 Rerank 分数:融合排序的候选先展示,精排分数标注在旁——「不适合的场景」那条分数 0.8+,「适用配置大全」那条掉到 0.2 以下,断层肉眼可见。低于阈值的条目直接不展示(它们已被闸门拦下),如果全部候选都低于阈值,页面就该呈现拒答态了。
📷 图片位(待补充)
应放什么图:「检索评估」页 Rerank 版截图,用否定式问题(如「哪些场景不适合用缓存」),圈出 Rerank 分数列上正确答案与干扰项之间的断层分差;如能截到一次拒答态(候选全部低于阈值)更好。
延迟和成本的账
把账摊开算,你会放心上这个组件:
- 延迟:一次 Rerank 调用处理 20 条候选,主流 API 在一两百毫秒量级。加上前面的两路检索,整个检索段控制在半秒内没问题,对聊天场景完全无感(反正生成那头要等好几秒)
- 成本:Rerank 模型的计价通常比生成模型低一个量级,而且输入就 20 条候选段落。相比「它带来的检索精度提升使得
top_n从 20 缩到 5、生成端省下的 token」,多半还是省的
再配合一个总原则回顾:宽召回 + 精筛。上一篇把向量阈值放宽到这里就闭环了——召回阶段宁滥勿缺(漏了谁也救不回来),Rerank 阶段宁缺勿滥(分数不够就不进 Prompt)。两阶段各司其职,比任何单阶段的精调都稳。
老规矩:留个降级开关
增强链不能是故障链。Rerank 服务挂了怎么办?降级方案:跳过精排,直接用 RRF 融合后的排序取前 5。效果降一档,但服务不死。配置一个 rag.rerank.enabled 开关加个超时,超时即降级。
这些开关在 relax-admin 里有个专门的家:「流量管控 → 降级与容错」页。每一行就是一个增强环节的开关和降级策略——Rerank 超时退 RRF、改写超时退原句、BM25 挂了退纯向量——还能做故障注入演练:手动把 Rerank 通道标记为故障,观察请求是不是真的按预期降级、用户端是不是无感。演练这事特别值得做一次:降级路径没跑过,等于没有。
📷 图片位(待补充)
应放什么图:「流量管控 → 降级与容错」页截图,圈出 Rerank / 改写 / BM25 三条降级策略的开关状态,以及一次故障注入演练的记录或实时状态。
第 09 篇的 BM25 通道、这篇的 Rerank、下一篇的查询改写——在线侧的每个增强环节都遵守同一条纪律:
三可纪律
可开关、可降级、可观测。这条纪律平时看不出价值,等哪天 Rerank 的 API 抖动,你的博客助手若无其事地继续答题,而隔壁「全链路强依赖」的项目群里已经在@运维了,你就懂了。
今天的落地清单
到这里,检索侧的三板斧(混合、RRF、Rerank)全部到位,检索段算是竣工了。下一篇离开检索,去处理一个聊天场景躲不开的问题:用户说「那它呢」的时候怎么办——多轮对话、查询改写和记忆管理,这是把「问答系统」变成「对话系统」的那一步。
下篇见。