RAG 只用向量检索找不到精确编号时怎么加混合检索
来源:17golang原创
时间:2026-09-08 00:23:06 320浏览 收藏
RAG 只用向量检索时,问“设备编号 ZX-2047 对应哪份维护记录”很容易拿不到正确文档:编号本身没有丰富语义,embedding 更关注整句含义,精确字符串反而可能排在较后。处理办法是把检索拆成两路:关键词检索保护编号、型号和条款号,向量检索覆盖“怎么更换滤芯”这类自然语言表达,再把两路候选融合成一个 Top-K。
精确编号不是把向量模型换大就能稳定解决的问题。保留 BM25/全文检索作为 sparse 通道,再与 dense 向量候选用 RRF 或可解释权重合并,通常比单路向量召回更稳。
- 编号、型号、版本号优先交给关键词通道;自然描述交给向量通道。
- 刚开始用 RRF,避免直接比较 BM25 分数和向量相似度的不同量纲。
- 若精确命中仍靠后,再提高 sparse 权重,并用四组查询做回归。
为什么精确编号会被单一路径漏掉
向量检索回答的是“语义上像不像”,而不是“是否包含这串字符”。订单号、设备序列号、SKU、法规条款号和内部缩写往往是低语义、高区分度的词。查询只改一个数字,业务含义可能完全改变,但两个句子的向量距离未必拉开足够差距。
先把查询中的词分成两类:编号词原样送入全文索引,自然描述送入 embedding。NVIDIA RAG Blueprint 将搜索类型区分为 dense 与 hybrid;Elastic 的混合搜索说明也把全文和向量查询合成一个排序列表。这里的关键不是二选一,而是让两条通道互相补位。

纯向量检索对精准编号、工号、订单号这类离散字符串的匹配召回率很低,加混合检索时可以先在向量库外绑定关键词倒排索引,同时给编号类字段开启精确匹配权重加权,最后通过多路归并把两类检索结果按业务规则融合排序,就能解决精准编号找不到的问题。
两条召回通道如何分工
| 通道 | 擅长命中 | 常见短板 | 适合保留的字段 |
|---|---|---|---|
| sparse / BM25 | 订单号、型号、原文短语、条款号 | 同义表达和改写不容易命中 | 编号、标题、别名、关键词字段 |
| dense / 向量 | 自然语言描述、同义词、概念关联 | 可能忽略一个关键数字 | 正文切片及其 embedding |
以 NVIDIA RAG Blueprint 为例,部署层可把 APP_VECTORSTORE_SEARCHTYPE 设为 hybrid。如果使用 weighted ranker,还可以分别设置 APP_VECTORSTORE_SPARSE_WEIGHT 与 APP_VECTORSTORE_DENSE_WEIGHT。这只是召回配置,不能替代字段分析:编号最好仍有可检索的原文或 keyword 字段。
# 开启混合召回,让关键词和向量候选同时参与 export APP_VECTORSTORE_SEARCHTYPE="hybrid" # 编号类查询更看重原词命中,可先从均衡权重开始 export APP_VECTORSTORE_RANKER_TYPE="weighted" export APP_VECTORSTORE_SPARSE_WEIGHT="0.6" export APP_VECTORSTORE_DENSE_WEIGHT="0.4"
配置改变后要让 RAG 服务和 ingestion 服务使用一致的搜索类型,并按所用后端的要求重建或重新写入索引。不要把 0.6 当成通用答案;它只是保护精确词的起点,最终应由真实查询集决定。
RRF 和加权融合该怎么选
两路检索的分数通常不能直接相加:BM25 分数受词频、字段长度等因素影响,向量相似度又是另一种尺度。RRF 只看排名位置,把两路都排得靠前的文档推到前面,适合作为第一版基线。NVIDIA 文档说明混合搜索默认使用 RRF,也提供 weighted 方式调节 dense 与 sparse 的相对重要性。
需要保护“编号必须出现”时,再采用加权融合或硬规则。示例中的融合函数只表达决策边界,不绑定某个数据库 SDK:
def merge_candidates(sparse_hits, dense_hits, top_k=6):
# 用文档 ID 合并两路候选,避免同一切片重复出现
merged = {}
for rank, hit in enumerate(sparse_hits, start=1):
item = merged.setdefault(hit["doc_id"], {"doc_id": hit["doc_id"], "score": 0.0})
# 倒数排名让不同检索器的分数无需直接比较
item["score"] += 0.6 / (60 + rank)
for rank, hit in enumerate(dense_hits, start=1):
item = merged.setdefault(hit["doc_id"], {"doc_id": hit["doc_id"], "score": 0.0})
item["score"] += 0.4 / (60 + rank)
# 稳定排序后截断,实际项目还应保留来源和命中通道用于解释
return sorted(merged.values(), key=lambda item: item["score"], reverse=True)[:top_k]
如果系统已经提供 RRF,优先使用原生实现;自写融合时至少保留 doc_id、来源和命中通道,方便回答“为什么这条排在前面”。对于要求编号完全一致的场景,可以在融合后给包含编号的文档加一个小幅业务加分,但不要让它绕过权限过滤或文档状态过滤。

上线前用四组查询检查召回是否真的变好
不要只拿一个编号测试。至少准备四组小样本:完整编号、编号加自然描述、只说业务含义的改写句、故意接近但不相同的编号。分别记录正确文档是否进入 Top-K、首位排名和重复块数量。若第一组变好而第三组明显变差,说明 sparse 权重过高;若第一组仍漏召回,应检查编号是否被分词、是否进入索引字段,而不是继续调 embedding。
- 先用 RRF 建立基线,再决定是否需要 weighted。
- 两路候选取更大的候选池,融合后再做 Top-K 截断。
- 按文档 ID 或稳定 chunk ID 去重,保留来源字段供引用。
- 记录 sparse、dense、融合后三个结果,排障时不要只看最终列表。
相关问题
为什么不能直接把 BM25 分数和向量分数相加?
两者量纲和分布不同,直接相加会让某一路因为数值范围更大而主导结果。先用 RRF,或在离线样本上校准权重更可靠。
混合检索是不是一定比向量检索准确?
不一定。它增加了召回覆盖,但也可能带来延迟和噪声;应以精确词、自然描述和误命中样本的 Top-K 结果共同判断。
改成 hybrid 后为什么旧集合仍然查不到?
要检查后端限制和索引状态。NVIDIA 文档提示,部分已有 dense 集合在切换 hybrid 后需要新建集合并重新写入文档;具体行为以所用向量数据库的官方说明为准。
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
355 收藏
-
277 收藏
-
177 收藏
-
261 收藏
-
233 收藏
-
473 收藏
-
197 收藏
-
124 收藏
-
399 收藏
-
科技周边 · 人工智能 | 15小时前 | 性能优化 · 人工智能 · transformers · 批量推理 · Hugging Face Transformers dynamic padding attention_mask297 收藏
-
108 收藏
-
207 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习