RAG 混合检索融合关键词与向量结果
RAG 同时接入关键词检索和向量检索后,真正棘手的通常不是“查两次”,而是如何把两份结果合成一份可信的候选列表。关键词检索擅长命中产品名、错误码和专有术语,向量检索更容易找到同义改写与语义相近内容。一个实用起点是让两路检索各自排序,再用 Reciprocal Rank Fusion(RRF)按名次融合,最后把统一的 Top-K 文档交给大模型。
先划清这个小项目的边界
本文只实现融合层:输入是两份已经排好序的候选,输出是去重后的统一列表。BM25、嵌入生成、向量索引和大模型调用都留在融合器外面。这样做的好处是可以独立检查融合逻辑,也便于以后替换 Elasticsearch、OpenSearch、Milvus、pgvector 或其他检索后端。
Elastic 的混合搜索文档把全文检索和向量检索组合为一份排名结果,并推荐用 RRF 合并两类排名。RRF 不直接比较不同检索器的原始分数,而是根据文档在每份结果中的名次贡献分数,因此很适合处理 BM25 分数与向量相似度不在同一量纲的问题。
为什么不能直接相加原始分数
假设关键词检索返回 12.8、9.4、7.1,向量检索返回 0.92、0.86、0.81。数值范围不同,相加后谁占主导取决于评分模型,而不是文档是否真的更相关。即使先做归一化,也要决定采用 min-max、z-score 还是按查询动态校准,线上数据分布一变,规则就可能漂移。
RRF 只看名次。对于文档 d,它在每份排名中的贡献为 1 / (k + rank),其中 rank 从 1 开始,k 是平滑常数。文档同时出现在两路前排时会获得两次贡献,只在一路出现也不会被直接丢弃。

准备统一的候选数据结构
两路检索必须共享稳定的 doc_id。标题或正文不适合作为去重键,因为内容更新、分段策略或空格差异都会制造重复项。融合层还应保留来源、原始分数和元数据,便于日志分析,但排序只使用名次。
from dataclasses import dataclass
from typing import Iterable
@dataclass(frozen=True)
class Hit:
# doc_id 必须能跨检索器唯一标识同一文档
doc_id: str
title: str
source: str
raw_score: float
# 下面两组数据代表检索器已返回的有序结果
keyword_ranked = [
Hit("d1", "RRF 融合算法", "keyword", 12.8),
Hit("d2", "RAG 检索调优", "keyword", 9.4),
Hit("d4", "BM25 字段权重", "keyword", 7.1),
]
vector_ranked = [
Hit("d2", "RAG 检索调优", "vector", 0.92),
Hit("d3", "语义召回实践", "vector", 0.86),
Hit("d1", "RRF 融合算法", "vector", 0.81),
]
把公式落成一个最小 Python 融合器
实现时遍历每份排名,以 doc_id 为键累计贡献。为了让相同分数的结果稳定,示例额外记录文档出现过的最好名次,并用 doc_id 作为最终兜底排序键。真实项目还可以把更新时间、权威级别或业务优先级放进明确的二级排序规则。
def rrf_fuse(
ranked_lists: Iterable[list[Hit]],
*,
rank_constant: int = 60,
top_k: int = 5,
) -> list[dict]:
if rank_constant
按照这组示例输入,d1 和 d2 都在两路结果中出现,会获得两次贡献;d3 与 d4 只获得一路贡献。这里不需要比较 12.8 与 0.92 谁更大,融合器只关心它们各自在所属列表里的位置。

接入真实 RAG 链路时放在哪里
一次完整请求可以拆成六个动作:先接收用户查询,再并行执行关键词检索和向量检索;把两路结果映射成统一 Hit;调用融合器;按融合后的 doc_id 读取正文片段;最后控制上下文长度并交给生成模型。融合前的检索可以并行,融合后的正文读取则应严格按照新排名进行。
过滤条件要保持一致。例如租户、文档状态、语言和权限最好在两路检索前都应用;如果只对一路过滤,融合结果可能出现另一条路径带回的越权文档。融合之后仍应做一次权限兜底,但不能把它当作唯一的访问控制。
三个参数分别控制什么
- 候选深度:每路取多少条参与融合。窗口越大,召回机会越多,但查询、传输和去重成本也越高。
- rank_constant:控制头部名次与后部名次贡献的差距。数值越大,靠后候选之间的差异越平缓。Elastic 官方 RRF 文档当前给出的默认值是 60。
- top_k:融合后最终保留多少条。它应与分段长度和模型上下文预算一起确定,而不是简单复制候选深度。
不要一开始就为两个检索器添加大量权重。先用等权 RRF 建立可解释基线,再根据带标注的查询集观察精确术语、同义改写、长查询和冷门主题的表现。需要偏置时,可以把每路贡献改为 weight / (k + rank),但权重应来自评测,而不是凭感觉。
本地验收时检查这五点
- 输入包含错误码或产品全名时,关键词路线的高位结果能够进入最终列表。
- 输入改写为同义表达时,向量路线仍能补回语义相关文档。
- 同一个 doc_id 同时出现于两路时,最终只保留一条且分数累加。
- 原始分数范围发生变化时,融合排序不会因为量纲不同而直接失真。
- 相同输入重复融合时,二级排序规则保证输出顺序稳定。
从最小实现走向生产环境
生产系统还需要补上超时、空列表、日志和评测。某一路超时时,可以降级为另一份排名;两路都为空时,不应让模型凭空回答。日志至少记录查询标识、两路候选 doc_id、各自名次、融合分数和最终入选项,方便定位“召回不到”还是“融合丢失”。
如果搜索引擎已经原生支持 RRF,可以把融合放到引擎端,减少应用层传输和重复代码;如果两路来自不同系统,本文这个小型融合器更容易统一接入。无论采用哪种方式,核心原则都相同:先保留每种检索信号的独立排序,再用可解释的融合规则生成最终上下文。
iter.Pull 未关闭造成 goroutine 残留的定位
- 上一篇
- iter.Pull 未关闭造成 goroutine 残留的定位
- 下一篇
- iter.Seq 连接多个数据源时的停止协议
-
- 科技周边 · 人工智能 | 3小时前 |
- RAG 文档切块按标题层级保留语义边界
- 300浏览 收藏
-
- 科技周边 · 人工智能 | 15小时前 | 人工智能 · rag · 语义检索重排器 第二阶段重排 Cross-Encoder Retrieve and Re-Rank Recall@K
- 语义检索重排器何时值得加入第二阶段
- 449浏览 收藏
-
- 科技周边 · 人工智能 | 19小时前 | 缓存 · 人工智能 · 提示词工程 · 提示词缓存 cache_control Prompt Caching 静态前缀 cache_read_input_tokens
- 提示词缓存命中率低应如何划分静态前缀
- 453浏览 收藏
-
- 科技周边 · 人工智能 | 22小时前 |
- 模型路由怎样按任务难度分配不同推理预算
- 232浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 |
- 结构化输出遇到递归字段时怎样约束模式
- 215浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 | 人工智能 · 工具调用 ·
- 智能体工具调用失败后怎样设计可控重试
- 236浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 |
- 多模态模型输入图片过大时如何控制视觉令牌
- 196浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 |
- 推理服务连续批处理怎样减少 GPU 空转
- 404浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 | 人工智能 · LoRa PEFT merge_and_unload 量化推理 模型合并
- LoRA 合并权重后输出变化过大应检查什么
- 404浏览 收藏
-
- 科技周边 · 人工智能 | 1天前 |
- RAG 分块重叠过大为什么会降低检索多样性
- 409浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 402次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 478次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 488次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 435次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 262次使用
-
- go格式“占位符”输入输出 类似python的input
- 2023-01-19 346浏览
-
- Golang如何调用Python代码详解
- 2023-01-07 235浏览
-
- HTTP 的 response 中的响应体和头部是分开发送的吗?
- 2023-01-28 387浏览
-
- B站等视频网站的弹幕用的是 websocket 还是轮询?
- 2023-02-16 447浏览
-
- Linux 下有什么命令行工具以时序显示 CPU 占用率?
- 2023-01-13 360浏览

