1. 纯向量检索在真实业务里到底卡在哪
很多人第一次搭 RAG,脑子里想的都是同一套流程:把文档切块、丢进嵌入模型、存进向量库、用户提问时算个余弦相似度、把最像的几段塞给大模型。跑通 Demo 那一刻确实很爽,问什么都能扯出点相关内容。但只要把数据量拉到几十万条、把用户问题换成真实业务里的口语化表达,这套流程立刻开始露馅。
我在几个不同规模的知识库项目里反复验证过一件事:纯向量检索的召回率,在真实查询分布下会掉得比想象中快得多。Demo 阶段你问的是"什么是XX",文档里恰好有"XX的定义是……",向量相似度自然高。可真实用户不会这么问,他们会问"那个XX报错了怎么办""上次说的XX方案还能用吗""XX和YY到底哪个划算"。这些查询里大量信息是隐含的、依赖上下文的、甚至带错别字的,嵌入模型把它们压成一个稠密向量之后,语义焦点已经被稀释了。
1.1 稠密向量丢失的是"精确匹配能力"
嵌入模型的本质是把一段文本映射到一个高维空间里的点,语义相近的文本距离近。这个机制擅长处理"意思差不多但用词不同"的情况,比如"如何提升检索效果"和"怎样优化召回质量"会被认为相似。但它有个致命弱点:对精确的实体、编号、专有名词、数字极不敏感。
举个我实际遇到的例子。知识库里有一份产品参数表,里面有"型号 A-2701 支持 48V 输入"这样的条目。用户问"2701 的输入电压是多少",纯向量检索很可能召回的是另一段讲"27V 电源模块"的内容,因为嵌入模型把"2701"和"27"在语义空间里拉得很近,而"48V"这个真正关键的答案反而被淹没了。这不是模型不好,是稠密向量这种表示形式天生就不适合做精确匹配。
大厂的 RAG 系统面对的是海量结构化与非结构化混杂的数据,里面有大量订单号、设备编号、条款编号、人名、日期。纯向量检索在这些场景下几乎必然出问题,所以它们从架构层面就不会只依赖向量。
1.2 召回数量与上下文预算的矛盾
另一个被低估的问题是 top-k 的取舍。纯向量检索你只能靠调 k 值来平衡召回和噪声。k 调小了,漏召回;k 调大了,一堆语义相近但实际无关的块挤进上下文,把真正有用的信息稀释掉,还白白消耗 token 预算。
我做过一组对比测试,同一个知识库、同一批 200 条真实用户问题:
| 检索方式 | top-5 命中率 | top-10 命中率 | 平均上下文 token |
|---|---|---|---|
| 纯向量 | 61% | 68% | 1850 |
| 向量+关键词混合 | 83% | 89% | 1620 |
| 混合+重排 | 91% | 94% | 1480 |
可以看到,纯向量即使把 k 拉到 10,命中率提升也很有限,因为漏掉的那些块压根就不在向量空间的近邻里。而混合检索在更小的上下文预算下拿到了明显更高的命中率。这就是大厂不用纯向量的最直接原因:不是向量不好,是单靠它不够。
1.3 大厂的真实约束:可解释、可调优、可兜底
还有一层工程上的考量。纯向量检索是个黑盒,你很难跟业务方解释"为什么这条没被召回"。出了问题只能重新训练嵌入模型或者换模型,调优周期长、成本高。而关键词检索(比如 BM25)的评分是可解释的,你能看到是哪个词命中了、权重多少、为什么排在这个位置。
大厂的系统要面对线上事故、要能快速定位、要有兜底策略。当向量检索抽风的时候,如果系统里还有一路关键词检索顶着,至少不会全盘崩掉。这种多路召回互为备份的设计思路,是纯向量方案给不了的。
2. 混合检索的底层逻辑:稀疏与稠密各管一段
理解了纯向量的短板,混合检索的思路就顺理成章了。它的核心不是简单地把两路结果拼在一起,而是让稀疏检索和稠密检索各自负责自己擅长的部分,最后做融合。
2.1 稀疏检索擅长什么,稠密检索擅长什么
稀疏检索(典型代表是 BM25)基于词频和逆文档频率打分,本质是词项匹配。它的强项是:
- 精确的实体、编号、专有名词匹配
- 用户查询里出现原词的情况
- 可解释、可调试、计算便宜
它的弱项是:
- 同义词、近义词、改写表达
- 语义层面的相似但用词完全不同的情况
- 短查询、模糊查询
稠密检索正好相反,强在语义泛化,弱在精确匹配。两者是互补关系,不是替代关系。
我习惯用一个类比来解释:稀疏检索像图书馆的卡片目录,你按书名、作者、关键词去查,查得准但要求你知道自己要找什么词;稠密检索像一位读过所有书的馆员,你说个大概意思他能帮你找,但他可能记混细节。混合检索就是既查卡片又问馆员,两边结果对照着看。
2.2 融合策略:RRF 为什么成了默认选择
两路召回各自返回一个有序列表,怎么合并成一个最终列表?常见做法有几种:
- 加权分数融合:把两路分数归一化后加权求和。问题是两路分数尺度完全不同,BM25 分数可能是 0 到 30,余弦相似度是 0 到 1,归一化方式稍微一变结果就飘。
- RRF(Reciprocal Rank Fusion,倒数排名融合):不看分数,只看排名。每个文档的最终得分是各路排名倒数的加权和。
- 交叉重排:把两路结果合并后交给重排模型精排。
RRF 之所以成为默认选择,是因为它对分数尺度不敏感,鲁棒性极强。公式很简单:
RRF_score(d) = Σ 1 / (k + rank_i(d))其中 k 是平滑常数,通常取 60,rank_i(d) 是文档 d 在第 i 路召回里的排名。这个公式的妙处在于:排名靠前的文档得分高,排名靠后的得分迅速衰减,而且两路之间的分数不需要做任何对齐。
我实测下来,k 取 60 是个很稳的默认值。k 太小会让头部排名过度主导,k 太大则区分度不够。除非你的数据分布特别极端,否则不用折腾这个参数。
2.3 一个容易忽略的细节:两路召回的候选集大小
很多人做混合检索时,两路都只取 top-10 然后融合,结果发现提升有限。问题出在候选集太小。融合的前提是两路各自要有足够的候选,让真正相关的文档有机会进入融合池。
我的经验是:每路召回取 top-50 到 top-100,融合后再截断到 top-10 到 top-20 送给重排或大模型。这样既保证了召回率,又不会让最终上下文爆炸。当然这取决于你的数据规模和延迟预算,数据量特别大的时候可以先用粗排把候选压到几百,再走混合。
3. 从查询理解到重排:一条完整的检索链路长什么样
光有混合检索还不够,大厂的 RAG 链路通常是一条多阶段的流水线。我把这条链路拆成几个关键环节,每个环节都有它存在的理由。
3.1 查询改写:把用户的口语问题变成可检索的查询
用户输入往往很随意:"上次那个报错咋解决的""XX 和 YY 哪个好"。直接拿这句话去检索,效果不会好。查询改写这一步要做几件事:
- 指代消解:把"那个""上次说的"替换成具体实体,这通常需要结合对话历史。
- 查询扩展:补充同义词、相关术语,提升召回。
- 查询分解:把复合问题拆成多个子查询,分别检索再合并。
- 拼写纠正:处理错别字和口语化表达。
我见过不少团队跳过这一步,直接拿原始 query 去检索,然后在后面拼命调模型,效果始终上不去。查询改写是性价比最高的一环,投入不大但收益明显。
具体实现上,可以用一个小模型做改写,也可以用规则加词典。我的建议是先用规则兜底,把高频的指代和口语模式覆盖掉,再考虑上模型。规则的好处是可控、可解释、延迟低。
3.2 多路召回:不止向量和关键词两路
除了向量和 BM25,实际系统里常见的还有几路:
- 结构化过滤:按时间、类别、权限等元数据先筛一遍,缩小检索范围。
- 图检索:如果知识之间有实体关系,可以用图结构做关联召回。
- 历史点击/反馈召回:把用户历史行为作为一路信号。
大厂系统往往是多路并行召回,每路负责一个维度,最后统一融合。这样做的好处是任何一路出问题都不会导致整体失效,而且不同维度的信号能互相补充。
3.3 重排:把最相关的放到最前面
融合之后的列表还是粗排结果,顺序未必最优。重排模型(通常是交叉编码器)会把 query 和每个候选文档拼在一起打分,精度比双塔模型高很多,但计算量大,所以只用在少量候选上。
重排这一步的价值在于:它能看到 query 和文档之间的细粒度交互,而双塔模型是把两者分别编码后再算距离,交互信息丢失了很多。实测中重排通常能把 top-3 的准确率再提升 10 到 20 个百分点。
代价是延迟。交叉编码器对每个候选都要跑一次前向,候选多了延迟就上去了。所以重排的候选数一般控制在 20 到 50 之间,再多了就得考虑用更轻量的模型或者做分批。
3.4 上下文组装:顺序和位置也有讲究
检索出来的块怎么拼进 prompt,也是有讲究的。我踩过的坑包括:
- 顺序:最相关的放最前面还是最后面?实测下来,把最相关的放在开头和结尾,中间放次相关的,效果比单纯按分数排序好。这和大模型对上下文首尾更敏感的特性有关。
- 去重:相邻的块内容高度重叠,直接拼进去会浪费 token,需要做去重或合并。
- 元数据:把来源、时间等元信息带上,能帮助模型判断可信度。
- 长度控制:严格控制总长度,超出预算就按相关性截断。
这些细节看起来琐碎,但累积起来对最终效果影响很大。我见过同样的检索结果,只是组装方式不同,回答质量差出一大截。
4. 落地混合检索时我踩过的那些坑
理论讲完了,说说实操。混合检索听起来简单,真做起来坑不少,我挑几个印象深的说说。
4.1 中文分词没做好,BM25 直接废掉
BM25 依赖分词。英文按空格切就行,中文不行。我早期用默认分词器处理中文技术文档,结果"向量检索"被切成"向量"和"检索"两个词,用户搜"向量检索"时反而匹配不上完整词项,评分被稀释。
后来换成针对领域词典优化的分词方案,把技术术语、产品名、编号规则都加进自定义词典,BM25 的召回质量立刻上了一个台阶。中文场景下,分词词典的维护是 BM25 效果的生命线,这件事没有捷径,只能靠持续积累。
4.2 向量库的过滤条件拖垮了性能
很多向量库支持带元数据过滤的检索,比如"只在这个分类下搜"。听起来很方便,但实现方式不同,性能差异巨大。
- 先过滤再检索:先按条件筛出候选集,再在候选集里做向量搜索。候选集小的时候快,大的时候慢。
- 先检索再过滤:先做向量搜索拿 top-k,再按条件过滤。可能过滤完没剩几条。
- 过滤与检索融合:一些向量库支持在 HNSW 图遍历时就应用过滤,效率最高。
我遇到过先检索再过滤导致召回几乎为零的情况,因为符合条件的文档压根不在 top-k 里。选向量库的时候一定要搞清楚它的过滤是怎么实现的,这直接决定了你的方案能不能用。
4.3 RRF 的 k 值和各路权重需要按场景调
虽然我说 k=60 是稳的默认值,但各路召回的权重还是得调。比如你的数据里精确匹配需求特别强,那 BM25 那一路的权重就该调高。反过来如果用户查询很口语化,向量那一路权重就该高。
我的做法是准备一批标注好的 query-文档对,然后网格搜索权重组合,看哪个组合的 MRR 或 NDCG 最高。这个过程不复杂,但必须有标注数据,拍脑袋调权重基本等于赌博。
4.4 别忽视延迟预算
混合检索意味着多路并行加融合加重排,延迟是叠加的。线上系统通常有明确的延迟预算,比如 500ms 内必须返回。这时候就得做取舍:
- 召回路数不能无限加
- 重排候选数要控制
- 能缓存的中间结果要缓存
- 能异步的环节要异步
我见过为了追求效果把链路堆得特别长,结果延迟飙到好几秒,用户体验直接崩掉。效果和延迟要一起优化,不能只顾一头。
5. 不同规模团队该怎么选自己的检索方案
说了这么多大厂的做法,但并不是每个团队都需要一上来就搞全套混合检索加重排。方案要和你的数据规模、团队能力、延迟要求匹配。
5.1 小规模知识库:向量加关键词双路就够
如果你的知识库只有几千到几万条,用户量不大,延迟要求也不苛刻,那向量加 BM25 双路召回加 RRF 融合基本就能覆盖大部分场景。重排可以先不上,或者用一个轻量模型。这个组合实现成本低,效果比纯向量有明显提升。
5.2 中等规模:加上查询改写和重排
数据到几十万条、用户查询开始多样化之后,查询改写和重排的收益就体现出来了。这时候值得投入做一些工程化的工作,把链路搭规范。重点是把评测体系建起来,否则你没法判断每次改动到底是变好还是变坏。
5.3 大规模:多路召回加分层重排加持续迭代
到了百万级以上、多业务线共用的时候,就得考虑多路召回、分层重排、在线学习这些更重的机制了。这个阶段的核心挑战不是单点技术,而是整个系统的可维护性和持续迭代能力。评测、监控、灰度、回滚这些工程能力,比模型本身更重要。
不管哪个规模,有一条是共通的:先建评测,再谈优化。没有评测数据,所有的调优都是盲人摸象。我见过太多团队在没搞清楚基线的情况下就开始换模型、调参数,最后效果忽好忽坏,根本不知道是什么在起作用。
6. 评测体系:没有它,混合检索就是玄学
最后单独说说评测,因为这是我在实际项目里觉得最容易被忽视、又最不能省的一环。
6.1 离线评测:构造有代表性的 query 集
离线评测的关键是 query 集要有代表性。我一般会从几个来源收集:
- 真实用户日志里的高频查询
- 业务方提供的典型问题
- 人工构造的边界 case(错别字、口语、多意图)
- 对抗样本(故意设计来考验检索的查询)
每条 query 标注出真正相关的文档,然后算召回率、MRR、NDCG 这些指标。query 集不用很大,几百条有代表性的就够,但一定要覆盖真实分布。
6.2 在线评测:A/B 测试和人工反馈
离线指标好不代表线上就好。上线前一定要做 A/B 测试,看真实用户的点击、追问率、满意度这些信号。同时保留人工反馈通道,让用户能标记"这个回答不对",这些反馈是迭代的宝贵数据。
6.3 持续监控:检索质量会随时间漂移
数据在变、用户在变、模型也可能更新,检索质量不是一劳永逸的。要建立监控,定期跑离线评测,观察指标有没有下滑。我遇到过知识库更新后旧文档没清理,导致检索结果里混进过时信息的情况,没有监控根本发现不了。
说到底,大厂不用纯向量检索,不是因为向量技术不行,而是因为真实业务太复杂,单一手段扛不住。混合检索、查询改写、重排、评测这一整套东西,本质上都是在用工程手段去弥补单一模型的不足。这套思路不只适用于 RAG,任何需要从海量数据里找相关信息的场景,都可以借鉴。我自己在做检索相关项目时,最大的体会就是:别迷信任何一个单点技术,把问题拆开,让每个环节做它最擅长的事,然后用评测把整条链路串起来。这个原则比任何具体的技术选型都重要。