← RAG 检索增强

混合检索里向量分、BM25 分、RRF 分和重排分量纲不同,阈值应该怎么设?

困难 混合检索与 RRF 融合 · 第 2 / 2 问 更新于 2026/09/29
RAG混合检索阈值RRFRerank
本题落地项目AI Agentic RAG高级企业知识库平台

简化版

阈值只能卡在有绝对含义的分数上:余弦相似度和重排相关分可以设阈值,而且两者要各配一个;BM25 分没有上界,RRF 分只代表名次,都不能和固定门槛比大小。检索管线里 score 会被不同阶段反复覆盖,所以要让量纲跟着结果一路传下去,过滤时按最终量纲选阈值,门槛值用「有答案 / 无答案」两组问题的实测分布来定。

详细版

一条混合检索管线里,score 字段会被四种分数轮流改写:

阶段分数取值特点能否卡阈值
向量检索余弦相似度0~1,数值有语义距离含义能
BM25 检索BM25 分没有上界,随查询词数和词稀有度变化不能
RRF 融合名次倒数和k=60 时大约 0.016~0.033,只反映名次不能
重排重排相关分0~1,但分布和余弦完全不同能

设计要点:

  1. 量纲显式化:谁改写分数,谁就把量纲一起改掉,结果带着 score_scale 往下传;
  2. 阈值按量纲分档:余弦相似度一个阈值、重排相关分一个阈值,两者可能差一个数量级;
  3. 不适用就不过滤:最终是 BM25 分或 RRF 分时跳过阈值,并在日志或页面上说明原因;
  4. 门槛靠标定:分别采集有答案和无答案问题的最终分数,门槛放在两组分布的空档里;
  5. 换模型就重标:Embedding 或重排模型一换,分数分布跟着变,旧阈值作废。

完整版教学

一、阈值要解决的是「资料里没有」这件事

向量检索永远能返回 Top-k。用户问一个知识库里根本没有的问题,检索照样会交回 5 条「最像」的片段,模型拿着这 5 条不相干的资料,很容易一本正经地编出答案。

阈值的作用是给检索加一道「够不够格」的判断:最终分数低于门槛的结果丢掉,一条都不剩时,问答流程就走「知识库里没有相关内容」的兜底话术。所以阈值本质上是一个拒答开关。它设错会带来两种相反的事故:

门槛太低:无关片段混进上下文 → 模型基于错误资料作答(误答)
门槛太高:真正相关的片段被砍掉 → 明明有答案却回复「没有」(误拒)

误拒比误答更隐蔽。误答至少能被用户发现是错的,误拒只会显示「没有相关内容」,谁都不会怀疑是阈值出了问题。

二、四种分数各自是怎么算出来的

量纲问题的根源在于,这四个数虽然都叫 score,计算方式却完全不同:

余弦相似度   cos(q, d) = (q · d) / (|q| × |d|)          两个向量的夹角,理论范围 -1~1,文本向量实际多在 0~1
BM25        Σ IDF(t) × tf(t,d)×(k1+1) / (tf(t,d)+k1×(1-b+b×|d|/avgdl))   对查询里每个词累加,没有上界
RRF         Σ 1 / (k + rank_i(d))                        只用名次,k 通常取 60
重排相关分   sigmoid(CrossEncoder(q, d))                  问题和片段拼在一起打分,再压到 0~1

从公式能直接读出两类性质:

分数数值只和什么有关同一个值在不同问题间可比吗
余弦相似度问题和片段的语义距离基本可比
BM25命中词的稀有度、词频、查询有几个词不可比
RRF片段在各路里排第几不可比,而且和相关性无关
重排相关分问题和片段的交叉相关性基本可比

「可比」是能设固定阈值的前提。只有余弦和重排分满足它。

三、为什么 BM25 分和 RRF 分不能卡阈值

BM25 的分数跟着查询长度涨。 它对查询中每个命中词累加一项,查询词越多、词越稀有,分数越高。一个示意算例:

问题 A「质保」                     1 个词,最相关片段 BM25 ≈ 2.1
问题 B「GTE-3000 质保期限多久」     4 个词,最相关片段 BM25 ≈ 9.6

两个问题的首条结果都是正确答案,分数却差了 4 倍多。设一个固定门槛 5:问题 A 的正确答案被砍掉,问题 B 里一个只命中了「质保」的无关片段可能也能过线。

RRF 分只记录名次。 以 k=60 为例:

只在一路排第 1          1/61 = 0.0164
两路都排第 1            1/61 + 1/61 = 0.0328
只在一路排第 10         1/70 = 0.0143

一个完全无关的问题,只要召回了东西,排第 1 的那条照样拿 0.0164。RRF 分高低只说明「排得靠前」,不说明「内容相关」,拿它卡门槛等于没有门槛。

记忆钩子:能卡阈值的分数要满足一个条件——同一个数值放在任何问题上,都代表差不多的相关程度。BM25 和 RRF 都不满足。

四、同样是 0 到 1,余弦阈值为什么不能搬到重排分上

余弦相似度和重排相关分的取值范围都写着 0~1,看起来能共用一个阈值,实际分布却可能差一个数量级。以一套真实的向量模型加重排模型的实测为例:

                  0        0.1       0.2       0.3       0.4       0.5       0.6       0.7       0.8
余弦 · 无关问题    |          [0.14 ── 0.23]
余弦 · 相关片段    |                                          [0.46 ──────────── 0.77]
                                                  ↑ 余弦门槛 0.3 落在空档里,合理

重排 · 无关问题    [0.006]
重排 · 相关片段    |               [0.14 ─ 0.20]
                                                  ↑ 同一个 0.3 比重排分的天花板还高

把余弦的门槛 0.3 直接用在重排分上,内容完全正确的 4 条结果会被全部砍掉,问答返回「没有相关内容」,系统还不会报任何错。

重排分偏低不代表重排不好用。重排模型的输出是交叉编码后压缩到 01 的相关性分数,不同模型的分布差异很大,有的模型对强相关片段也只给 0.10.2,但它区分相关和无关的能力很强:0.14 和 0.006 之间差了 20 多倍。分数绝对值小,不妨碍它是一个好的阈值信号,只是门槛要按它自己的分布来定。

五、让量纲跟着结果走

同一个 score 字段在管线里被覆盖了几次,取决于开了哪些开关:

向量检索 ──→ score = 余弦相似度          ┐
BM25 检索 ──→ score = BM25 分            ├─ 多份榜单 ──→ RRF 融合 ──→ score = RRF 分
(多查询改写时每条查询各一份榜单)        ┘                              │
                                                                         ▼
                                                              重排(可选)──→ score = 重排相关分
                                                                         │
                                                                         ▼
                                                        阈值过滤:按最终量纲选门槛

最终量纲的判断规则:

管线情况最终量纲用哪个阈值
开了重排重排相关分重排阈值
没开重排,只有一份榜单,来自向量检索余弦相似度余弦阈值
没开重排,只有一份榜单,来自 BM25BM25 分不过滤
没开重排,榜单不止一份RRF 分不过滤

注意「榜单不止一份」不等于「开了两路」。只开向量检索,但开了多查询改写,一个问题被拆成 3 条查询,向量这一路就会产出 3 份榜单,同样要走 RRF。反过来,两路都开了,但某次 BM25 一条都没召回,实际只剩一份榜单,分数仍然是余弦相似度。

所以量纲不能靠读配置去猜,要在每个改写 score 的地方把量纲一起写进结果,阈值过滤只看结果上带的量纲。

六、门槛值用两组分布来标定

阈值不是拍脑袋的数字。标定步骤:

  1. 准备两组问题:知识库里有答案的(例如 20 条)和没有答案的(例如 10 条,要贴近业务,比如问其他产品的型号);
  2. 用线上同一套策略跑一遍,记录每个问题最终阶段首条结果的分数;
  3. 看两组分数有没有空档,门槛放进空档;
  4. 在候选门槛上分别算误拒率和误答率。

以重排分为例,有答案的问题首条落在 0.140.20,无答案的问题首条落在 0.0040.012:

重排门槛误拒(有答案被砍)误答(无答案放行)结论
0.3020/200/10全部误拒,这就是误用余弦门槛的后果
0.100/200/10可用,但离有答案组的下沿只差 0.04
0.050/200/10两边都留了余量
0.010/202/10无答案问题开始漏进来

两组分数差了一个数量级时,门槛取两端的几何平均比算术平均更稳:√(0.012 × 0.14) ≈ 0.041,取整到 0.05 附近。余弦那一档同理,无关问题上沿 0.23、相关片段下沿 0.46,门槛取 0.3 左右,离两边都有余量。

易错点:标定用的必须是最终阶段的分数。开了重排却拿粗召回的余弦分去标定,得出的门槛对线上毫无意义。

七、过滤结果要能解释

阈值出问题时,最难受的是「查了半天不知道为什么返回 0 条」。过滤这一步要留下足够的痕迹:

  • 生效时:记下当前量纲、门槛、本次分数区间和过滤前后条数,比如「重排相关分 ≥ 0.05,4 → 3」;
  • 砍光时:明确写出「最高分 0.18 仍低于门槛 0.3,本次结果被全部过滤」,并提示去检查门槛,而不是让人去怀疑知识库没切分、没向量化;
  • 不适用时:最终量纲是 RRF 分或 BM25 分,却配了阈值,要写明「本次不适用」及原因。静默跳过的话,配置页上那个阈值看起来在生效,其实一直在空转,比报错更难查。

八、常见误区与追问

  • 误区:分数都在 0 到 1 之间,就能共用一个阈值。 余弦相似度和重排相关分的分布可能差一个数量级,共用门槛会把一方整片砍掉,必须按量纲分别配置。
  • 误区:RRF 分低说明结果不相关。 RRF 分只由名次决定,无关问题的首条照样是 1/61,它既不能证明相关,也不能证明不相关。
  • 误区:先把分数 min-max 归一化,就能统一卡阈值。 按单次查询归一化后,首条永远是 1、末条永远是 0,绝对含义被抹掉,无答案的问题也能拿到满分。
  • 误区:阈值设高一点更安全。 门槛过高带来的是误拒,用户看到「没有相关内容」,不会有人想到是阈值的问题,反而更难发现。
  • 误区:阈值调好一次就能一直用。 换了 Embedding 模型或重排模型,分数分布整体平移或缩放,旧门槛立刻失效,要用同一套标定问题重新跑一遍。
  • 追问:只开 BM25、不开重排时,怎么判断「资料里没有」? BM25 分本身不能卡门槛,可以打开重排,让最终分数变成有绝对含义的重排分,或者把首条片段交给模型判断证据是否足以回答。
  • 追问:阈值过滤应该放在管线的哪一步? 放在最后一次改写分数之后。开了重排就在重排之后过滤,在粗召回阶段按余弦过滤会提前砍掉本来能被重排救回来的片段。

九、加强记忆

判断一个分数能不能卡阈值,只问一句:同一个数值放在不同问题上,是否代表差不多的相关程度。余弦相似度和重排相关分满足这一点,可以设门槛;BM25 分随查询词数和词稀有度变化,RRF 分只是名次倒数,都不满足。满足条件的两种分数也不能共用门槛,实测里余弦的相关片段在 0.460.77,重排的相关片段只有 0.140.20,拿 0.3 去卡重排分会把正确结果全部砍光。工程上要做三件事:量纲跟着结果走,谁改写分数谁换量纲;阈值按量纲分档配置,不适用时明确说明而不是静默跳过;门槛用有答案、无答案两组问题在最终阶段的分数分布来标定,换模型就重标。

项目实战落地

项目里怎么做的

《AI Agentic RAG高级企业知识库平台》的检索管线里,四种量纲定义成四个常量:余弦相似度、BM25 分数、RRF 名次分、重排相关分。每一步改写 score 时,同时改写当前的量纲,最后和结果一起返回给页面,结果表格的分数列按量纲显示列名,不再笼统地叫「相似度」。

检索策略表 retrieval_strategy 里有两个阈值字段:

  • vector_score_threshold:余弦相似度门槛,预置策略用 0.3;
  • rerank_score_threshold:重排相关分门槛,「完整-混合检索加重排加改写」策略用 0.05。

量纲和阈值字段的对应关系只登记了两档:

# 只有余弦相似度和重排相关分有绝对含义,BM25 分数和 RRF 名次分不在表里,遇到就不做阈值过滤
THRESHOLD_FIELD_BY_SCALE = {
    SCALE_COSINE: "vector_score_threshold",
    SCALE_RERANK: "rerank_score_threshold",
}

阈值过滤作为检索的一个阶段写进阶段记录,召回对比调试台上能直接看到:生效时显示门槛、本次分数区间和过滤前后条数;结果被砍光时写明最高分低于门槛、建议调低;最终量纲是 RRF 名次分时显示「阈值过滤(本次不适用)」,并提示打开重排才能让阈值生效。

策略编辑弹窗里,每个阈值输入框下面标着这一档的实测区间(余弦相关片段 0.460.77、无关问题 0.140.23;重排相关片段 0.14~0.20、无关问题 0.006),当前开关组合下会生效的那个框,说明文字显示为绿色。

为什么这样取舍

  • 量纲跟着结果传,而不是按开关组合去猜。 同一套配置,某次 BM25 一条都没召回时只剩一份榜单,实际不会走 RRF;只看配置会把这一次判错,门槛就用错了。
  • 不适用时明确标注,不静默跳过。 管理员配了阈值却没生效,页面上什么都不说的话,他会一直以为阈值在工作。
  • 不做分数归一化来统一门槛。 归一化会让每次查询的首条都变成 1,无答案的问题也能过线,拒答就失去了依据。

面试官还会追问

  • 策略还没保存、也还没跑过检索,页面怎么提前告诉管理员哪个阈值会生效?这个判断放在前端还是后端?
  • 开了父块回填之后,回填进来的父块没有参与打分,它拿什么分数去和阈值比?
  • 这个量纲问题在项目里是怎么被发现的?

学完《AI Agentic RAG高级企业知识库平台》,上面这些追问你都会迎刃而解。

本题落地项目地狱锤炼AI Agentic RAG高级企业知识库平台基于企业知识库、PGVector 向量检索、Agentic RAG、FastAPI + LangGraph、Function Calling,实现向量 + BM25 混合检索与 RRF 融合、Rerank 重排、HyDE 与多查询改写、父子分块召回、LangGraph 状态图自适应检索、Agent 执行时间线和 LLM-as-judge 四指标评测,覆盖从基础 RAG 到高级 RAG 调优的完整闭环。FastAPILangChainLangGraphRAGPGVector源码+SQL喂饭学习教程配套面试文档环境安装文档项目运行文档 学习这个项目