在构建检索增强生成(RAG)系统时,混合搜索已成为行业标准实践。BM25 负责捕捉精确关键词匹配,语义向量负责理解概念关联,但问题在于这两个子系统输出的分数根本不在同一个度量空间内。BM25 的分数范围可能是 0 到 100 甚至更宽,而 pgvector 的余弦相似度固定在 -1 到 1 之间,直接相加会导致向量搜索的微弱优势被 BM25 的巨大分数完全淹没。排序融合算法的核心价值,正是绕过原始分数的量纲问题,转而利用两种搜索结果各自的排序位置来计算综合相关性得分。
RRF(Reciprocal Rank Fusion) 是当前最广泛采用的排序融合方案,其设计哲学简洁而优雅:如果一篇文档在多个搜索系统中都排得很靠前,那它很可能就是真正相关的内容。RRF 的计算公式为 RRF (d) = Σ₁/(k + rankᵢ(d)),其中 k 是一个常数参数,通常取值为 60。这个公式的巧妙之处在于,它只依赖排序名次而非原始分数,因此天然解决了不同搜索系统分数量纲不一致的问题。假设一篇文档在 BM25 结果中排名第 2,在语义搜索中排名第 3,那么它的 RRF 得分就是 1/(60+2) + 1/(60+3) ≈ 0.0161 + 0.0159 = 0.0320。当文档在两个系统中都进入前十时,RRF 得分会出现明显的跃升,这使得最终排序能够有效综合两种搜索信号的优势。
窗口大小(Window Size) 是影响 RRF 效果的关键工程参数。理论上应该对每个搜索系统返回完整结果集进行融合,但实践表明这既不必要也低效。更合理的做法是只取每个系统的 top N 结果参与融合,这个 N 就是窗口大小。ParadeDB 的技术文档建议使用 20 到 50 之间的值。窗口设得太小会丢失潜在的优质候选文档 —— 某些文档可能在 BM25 中排名第 80,但结合语义信息后应该是 top 10;窗口设得太大则引入过多噪声候选,增加计算开销却对最终排序质量提升有限。一个经验法则是:对于纯技术文档搜索,窗口 30 通常足够;对于内容多样化的知识库,可以尝试窗口 50。
加权 RRF(Weighted RRF) 允许工程师根据业务场景调整两种搜索信号的权重占比。当用户主要搜索特定术语、版本号或代码片段时,BM25 的精确匹配能力应该得到更多强调;当用户使用自然语言描述需求时,语义搜索的理解优势应该被放大。加权 RRF 的实现非常直接,只需在融合时为每个系统的得分乘以对应的权重系数即可。例如,SET work_mem = '256MB'; SET maintenance_work_mem = '1GB'; 这两条 SQL 参数看似与搜索无关,但实际上会显著影响大型排序操作的执行效率。
CombSUM 与 CombMAX 是另外两种常见的分数融合策略。CombSUM 将各系统的归一化分数直接相加,得分高的文档意味着在多个维度上都表现良好;CombMAX 则只取各系统分数的最大值,更强调单一系统的极致表现。与 RRF 相比,这两种方法都需要先进行分数归一化处理,增加了工程复杂度。归一化本身又有 min-max、z-score、百分位等多种选择,每种方法对异常值的敏感程度不同,容易引入新的调优点也引入新的调参负担。对于大多数 Postgres 混合搜索场景,RRF 的「无归一化」特性反而成为其主要优势 —— 它让系统行为更加可预测,降低了维护成本。
分数归一化的工程陷阱 值得特别关注。如果坚持使用 CombSUM 或直接相加的方案,必须先将两种分数映射到同一个范围内。最简单的是 min-max 归一化:scaled_score = (raw_score - min_score) / (max_score - min_score)。但这种方法对极值非常敏感,如果某个 BM25 分数因为文档特别长而异常升高,整个归一化结果都会被扭曲。百分位归一化更加鲁棒,它将原始分数转换为在结果集中的排名百分比:percentile = rank /count。百分位归一化的缺点是丢失了分数之间的相对差异信息 —— 排名第 1 和排名第 2 的文档在归一化后可能只有 0.01 的差距,而排名第 50 和排名第 100 的差距同样微小。在实际生产中,建议先用 A/B 测试验证归一化方案的必要性:如果 RRF 已经能够满足业务需求,就不必引入额外的归一化复杂度。
混合搜索的 SQL 实现模式 需要注意几个工程细节。首先,每个子系统查询都应该独立执行并使用 LIMIT 限制结果数量,这既避免了不必要的全表扫描,也让 RRF 计算的数据量可控。其次,UNION ALL 用来收集两个系统的候选文档和各自的 RRF 贡献分数,而不是使用 UNION 以防止重复文档丢失得分累加。第三,GROUP BY 文档 ID 并 SUM 累加分数后,ORDER BY 综合得分 DESC 即可得到最终排序结果。最后,如果系统中已有用户行为数据(如点击、收藏、停留时长),可以将这些信号纳入融合流程,创建额外的「popularity」或「recency」排名分支,让搜索结果兼顾相关性和时效性。
生产环境的监控指标 应该关注几个关键维度。第一是 BM25 与语义搜索的贡献比例,可以通过统计最终结果中分别来自两种搜索的文档占比来追踪。如果某段时间内 BM25 贡献率从 60% 骤降至 20%,需要检查向量索引是否出现碎片化或 embedding 模型是否更新导致语义分布变化。第二是头部结果的稳定性,如果排序结果在相邻两次查询之间出现剧烈波动,可能是窗口参数设置不当或某个子系统返回了异常结果。第三是查询延迟的分解监控,BM25 查询、向量 kNN 查询、RRF 融合计算各自消耗多少毫秒,需要在索引优化时作为参考依据。第四是 null 召回率(Recall@K),定期抽样检查 top 20 结果是否真的与查询意图相关,作为排序质量的主观评价指标。
索引构建与查询参数调优 是性能优化的关键战场。ParadeDB 的 pg_search 扩展支持 BM25 索引的多种 tokenizer 配置,包括 pdb.simple 用于基础分词、pdb.english 支持词干提取和停用词过滤。HNSW 索引在 pgvector 中的参数 k(邻居数)和 ef_construction(构建时的搜索宽度)直接影响召回率和索引大小。HNSW 的查询参数 ef 决定了搜索时的探索广度:ef 值越高召回率越好但延迟越高,典型取值范围是 40 到 200。IVFFlat 索引在数据量更大时可能更具成本优势,但需要预先将数据分桶,查询时指定 nprobe 参数控制搜索桶数。对于千万级文档的搜索场景,建议在测试环境对比 HNSW 和 IVFFlat 的 QPS - 召回率曲线,选择最适合业务延迟要求的方案。
多信号融合的扩展思路 让混合搜索系统具备更强的业务适配能力。除了 BM25 和语义向量之外,可以轻松添加以下排名信号:基于发布时间的 recency 排名,赋予新文档更高的权重;基于点击或购买行为的 popularity 排名,让热门内容获得更多曝光;基于用户显式标签或历史偏好的个性化排名;基于内容质量评分或编辑权重的 authority 排名。每种信号都实现为一个独立的子查询,计算出各自的排名序号后参与 RRF 融合。这种架构的优势在于新增信号时不需要修改现有的融合逻辑,只需要添加一个新的排名分支即可。信号之间的相对权重可以通过配置中心动态调整,实现搜索策略的敏捷迭代而无需代码发布。
回退策略与降级方案 是生产系统不可或缺的保障机制。当向量索引因磁盘故障不可用时,系统应该能够回退到纯 BM25 搜索,保证核心功能可用;当 BM25 服务异常时,可以临时提升向量搜索的权重系数来弥补。当用户查询命中敏感词库时,可能需要禁用语义搜索以避免潜在的越狱风险;当查询延迟超过 SLA 阈值时,可以缩小 RRF 窗口大小来换取响应时间。这些降级规则可以通过 PgBouncer 的路由配置或应用层的条件判断来实现,关键是确保每种故障模式都有明确的回退路径,而不是让整个搜索服务直接崩溃。
技术选型的边界判断 帮助团队做出务实决策。如果文档集合不超过一百万且查询 QPS 较低,PostgreSQL 原生的全文搜索加上 pgvector 就能满足需求,无需引入外部搜索引擎。如果团队没有专职的搜索基础设施工程师,ParadeDB 提供的统一 BM25 接口比自建 Elasticsearch 集群更容易运维。如果对查询延迟要求在 10 毫秒以内且数据量达到十亿级别,可能需要考虑专门的向量数据库如 Pinecone 或 Milvus,而不是在 Postgres 上进行极限优化。技术选型的本质是在功能、性能、运维成本之间找到最适合当前团队阶段的平衡点,而不是追求理论上的最优解。
资料来源:ParadeDB 技术博客《Hybrid Search in PostgreSQL: The Missing Manual》。