写在前面
做专业文档 RAG,瓶颈很少是「大模型不够强」,而是 Query 类型与召回信号不匹配。
搜错误码、接口名、条款编号 → 需要 词面(lexical) 命中 搜口语化问题、释义改写 → 需要 语义(semantic) 近邻
两路信号量纲不同。只用 dense,硬词易漏;只用关键词,语境易偏。FastGPT、RAGFlow 早已默认「向量 + 全文」;本文记录一套以 Milvus 2.5 同库 Hybrid(dense + Sparse-BM25 + RRF) 为主底座的落地设计:如何分层、如何读分。
边界: Hybrid 不解决切片错误、过期正文、权限过滤——那些是入库与治理问题。切片质量仍是检索上限。
1. 行业对照与设计约束
四条约束决定了后面的架构形状:
Lexical 必须生产级。 Milvus Built-in Sparse 后,仅为 Hybrid 再养一套 ES,成本通常不划算(已有 ES 基建除外)。 Rewrite 是可选阶段。 多轮指代有用;Query 已完整时关闭,少一次模型往返与改写漂移。 差异化放编排,不放第二套混合栈。 「指定文档优先 + 全库补召回」「token 控篇幅」比再造「先选书再找段」更可控。 外部 RAG 用 HTTP 补召回即可,不必复刻对方 ES 全栈。
对齐开源的是 Dense + BM25、RRF/Weighted、Rerank;自建差异落在范围优先 + 全库补召回、分库 token 预算、HTTP 外挂补充。
2. 端到端检索管道
Hybrid 只负责 Candidate Generation(初筛)。完整链路是漏斗,不是单分数系统。

对外排序分约定: 开了 Rerank → 用 Rerank 分;否则 → 初筛分(融合分或余弦)。
启用 Rerank 时,初筛必须放大候选(例:业务 topK=20 → 初筛 80)。阈值只对「当前阶段正在使用的分」有意义。
3. 总体架构:存储与召回如何分工
先看系统级架构,再进入 Hybrid 内部。

hybridSearch | ||
迁移注意: sparse 依赖分段正文。reindex 只拷 dense、省略 content,BM25 通路空转——这是线上「关键词全失效」的高频根因。
4. Hybrid 落地:真混合如何工作
4.1 三种形态
本文采用 真混合:同一 collection 维护 float vector 与 BM25 sparse field,一次 hybridSearch 完成双路召回与融合。
4.2 Hybrid 内部数据流
同一 Query 并行进入两条召回通路,再在库内做排名融合——这是「真混合」与串行假混合的分界。

两路 并行互补,不是互为前置过滤。串行「先关键词再向量」会把词面弱、语义相关的结果提前杀掉。
每路 topK 可独立配置;默认 RRF,明确偏词面或语义时再切 Weighted。
5. 编排扩展:范围优先 + 全库补召回
指定文档集(用户勾选材料、业务绑定文档)时,仅靠全库 Hybrid 不够——相关副本会和指定材料抢 topK。这是 编排层,不是 Milvus 原生能力。

两条硬规则:
范围路最高分过低 → 整路作废且 不 exclude,否则会锁死全库真正相关的段。 同名多格式(docx / pdf / pptx)只挂了部分 metadata → 标题归一化并入范围路,避免副本在全库路抢名额。
工具侧仍宜对外呈现为「一次检索」。
6. 四个分数:定义、量纲与配置
检索链路里会出现多种「分」,它们不是同一把尺子。搞混量纲,是 Hybrid 上线后最常见的配置错误来源。

| 否 | ||||
原则: 阈值只对「当前阶段正在用的分」有意义。把 0.5 的余弦习惯套到 RRF 上,召回经常被滤空。
6.1 向量分(余弦相似度)
Dense 通路把 Query 与分段都映射到同一 embedding 空间,用近邻搜索召回。常用度量是余弦:
cos(q, d) = (q · d) / (‖q‖ × ‖d‖)
阈值怎么用: 仅在 纯 dense、且排序分就是余弦 时,用「相似度阈值」砍候选才有直观意义(例如 0.4 / 0.5)。Hybrid 开启后,对外初筛分通常已是融合分,不要再拿余弦阈值去砍融合分。
调试时可单独拉出「向量路名次 / 向量分」,用于判断语义路有没有进榜;生产流量不必每次多算一次 ANN。
6.2 BM25(词面分)
Sparse 通路对 Query 与分段正文做词面匹配。Milvus Built-in Sparse 使用 BM25 族打分,经典形式为:
BM25(D, q) = Σ_i IDF(q_i) · f(q_i,D)·(k1+1)
/ ( f(q_i,D) + k1·(1 − b + b·|D|/avgdl) )
IDF(q_i) ≈ ln( (N − n(q_i) + 0.5) / (n(q_i) + 0.5) + 1 )
f(q_i,D) | |
|D|avgdl | |
Nn(q_i) | |
k1 | |
b |
直观规律: 词越罕见(IDF 大)、在段内出现越多、段相对越短 → 分越高。BM25 不含语义:同义不同字,分可以很低。
参数建议:
k1调大 → 高频重复更占优,适合术语反复出现才算「真相关」的长文;短分段库保持 1.2 即可。b调大 → 长段惩罚更重;分段长度已经较匀时,0.75 通常够用。多数库先动切片与分词(analyzer),最后才动 k1/b。 默认能跑时不要先调这两个。
BM25 原始分常 >1 且跨库不可比,UI 上不宜直接当进度条式「相关度」。它的价值在于进入 sparse 路 top 列表,进而影响 RRF 名次。
6.3 融合分(RRF / Weighted)
两路召回的原始分不可直接相加(余弦在 0~1,BM25 常 >1)。工程上常用 只看名次、不看原始分 的 Reciprocal Rank Fusion:
RRF(d) = Σ_r 1 / (k + rank_r(d)) # k 常取 60
rank_r(d) | |
k |
数值直觉:
1/(60+3) ≈ 0.016 | |
1/61 + 1/61 ≈ 0.033 | |
1/61 + 1/70 ≈ 0.031 |
所以页面上常见 0.01~0.03:这是相对序,不是「几乎不相关」。融合分只适合:
同一次结果内排序; 调试时看「谁压过谁」。
不要跨次、跨库、与余弦/Rerank 横比大小。
Weighted Ranker: 在明确要「偏关键词」或「偏语义」时,对两路加权再融合。多数知识库先固定 RRF(k=60),减少调参面;个别库再开 Weighted。
6.4 Rerank 分
初筛解决「从全库捞出几十条候选」;Rerank 解决「这几十条里谁更贴 Query」。二者常用两类编码器:
输出常落在 0~1。是否经 sigmoid、精确刻度以云端 Rerank 服务为准——以厂商文档为准即可,关键是:精排开启后,对外排序分应切换为 Rerank 分。
候选量: 业务 topK=20 时,初筛常取 ≈80(×4),给精排留空间。再大会明显拉高延迟与费用;库很小、Query 偏长句时,也可以关掉 Rerank,仅用 dense + 阈值换延迟。
7. 结语
专业文档 RAG 必须同时建模 词面 与 语义。底座上,Milvus Built-in BM25 把 Hybrid 收敛到单一向量库;编排上,范围 / 全库与 token 预算比重复建设混合栈更划算。读懂分数量纲、理清配置优先级,比先换更大的模型更能稳住召回质量。
要点备忘: Milvus 2.5 Hybrid · RRF / Weighted · Rerank · 可选范围 + 全库编排 · HTTP 外挂补充。
- END -