我做过不少 RAG 项目,也看过很多团队从“向量检索一把梭”到“老老实实上混合检索”的全过程。标题这句话说得挺直接——大厂 RAG 从不用纯向量检索,听上去有点绝对,但对比一下生产环境和 Demo 的差距,你会发现这几乎是必然的选择。今天这篇就围绕这个主题,把背后的原因、大厂常见的检索架构、以及真正落地时那几个关键环节掰开揉碎讲清楚。
先说明一点,这篇文章不打算复述论文或官网上那些漂亮话,更多是我在实际项目中踩过坑、调过参、也被在线问题逼着改架构之后积累下来的经验。适合正在做 RAG 落地、或者准备从原型走向生产的朋友参考。
1. 纯向量检索在真实场景中为什么会失灵
1.1 语义相似不等于答案正确
很多人第一次接触向量检索时,都会被“语义匹配”这个概念吸引。查询“你们公司怎么退货运费”,向量检索能把“退货政策说明”“运费承担规则”这种字面上完全不一样的内容捞出来,这确实很惊艳。但等你真把这样的检索器放到知识库问答系统里,问题很快就来了。
向量检索的本质,是把文本映射到高维空间里,然后在空间里找“距离最近”的邻居。可“语义相近”和“答案正确”之间隔着一道巨大的鸿沟。最典型的例子是数字和时间。你问“2023年的营收是多少”,如果知识库里同时存在“2022年营收”和“2023年营收预期”,向量表示上它们之间的距离其实非常近,因为句子结构、主体、甚至连关键词都高度重合。这种场景下,纯向量检索很容易返回错误年份的内容。
我见过一个真实的案例。某团队做的是企业知识库问答,初期只用向量检索,准确率在内部测试集上看着有 85% 以上。等上了生产,用户问的问题开始五花八门,准确率直接掉到 60% 不到。后来翻日志才发现,大量错误都集中在数字、人名、产品型号这类“精确匹配”查询上。这些查询本质上需要词法级别的精确命中,而语义匹配在这类查询上反而是一种劣势。
1.2 向量检索对长文档和全局信息天生不敏感
另一个容易被忽略的点是,向量检索作用于一个文本块时会把它压缩成一个固定长度的向量,通常是几百维到上千维。这意味着什么?意味着文本块里的很多细节信息在向量化的过程中被“平均”掉了。
举个例子。一份产品手册里有几十个型号的参数对比,每个型号的功率、尺寸、重量都不同。如果把这整段塞进一个向量里,最终得到的向量大概率是“中间状态”,它跟任何一个具体型号都匹配不上。用户问“A 型号的功率是多少”,纯向量检索可能根本排不到包含 A 型号参数的那个段落前面。
所以现在工业界做 RAG,第一步往往不是调模型,而是设计分块策略。常见做法是按语义边界切分、控制块长度、加滑动窗口重叠。但这只能缓解问题,不能根治。因为无论怎么切块,向量化这一步永远存在信息压缩,把检索精度完全押在向量上,属于先天不足。
1.3 低频词和专有名词检索的“灾难现场”
向量模型在训练时见过的词汇是有限的,遇到没见过的专有名词、内部缩写、型号编号,模型会尽力而为地把它映射到一个附近的位置。这个“附近”可能根本不在正确区域。比如一个内部系统叫“ZetaFlow”,向量模型训练时没见过这个词,把它编码成一个模糊的语义位置。用户在新发的文档里手写“ZetaFlow 的权限设置”,这个文档也被编码成一个模糊位置。这两个向量如果真能被语义匹配到一起,那属于运气好,不是模型能力。
更麻烦的是,知识库是持续更新的,新出现的专有名词在向量模型里根本没有稳定的表示。为了缓解这个问题,团队要么频繁微调 embedding 模型,要么在检索层加关键词兜底。大部分团队选择了后者,因为微调一个 embedding 模型的成本实在太高了——需要标注数据、需要训练资源、还需要持续维护。
1.4 分数不可解释,阈值形同虚设
做纯向量检索时,你会遇到一个非常现实的问题:怎么判断检索出来的结果可不可信?余弦相似度给了一个 0 到 1 之间的分数,但这个分数到底多少算“相关”,没有任何通用答案。
我在实践中试过给向量相似度设阈值来过滤低质量结果,结果发现自己给自己挖坑。不同领域、不同文档风格、甚至不同 query 方式,相似度分布都不一样。有的 query 检索出来的所有结果都在 0.7 以上但全是无关内容,有的 query 虽然只有 0.5 却是唯一正确答案。这种不可解释性在线上排查问题时非常痛苦——用户反馈答案不对,你打开日志,看到的只是一个 0.73 的相似度分数,却完全没法解释为什么系统认为这个结果相关。
这也是为什么后来很多团队转向混合检索:至少 BM25 的命中逻辑是可解释的——它命中了哪个词,哪个词贡献了多少分,一清二楚。
2. 大厂 RAG 的真实架构:混合检索是默认选项
2.1 “先用关键词圈定,再用向量精排”的思路
我接触过不少公司的检索架构,也包括一些搜索团队转来做 RAG 的案例。几乎没有哪家在生产环境里是只部署一个向量检索服务的。普遍的共识是:混合检索,即向量和稀疏检索(BM25 或类似算法)同时上,再把结果合并。
这里面的核心思路其实很朴素。BM25 擅长处理精确匹配、专有名词、ID 类查询;向量检索擅长处理同义改写、语义扩展、口语化表达。两者互补性非常强。更重要的是,混合检索给系统带来了一条“保底路径”。就算向量模型对某个新词编码失败,BM25 仍然可以把包含这个词的文档捞出来。
实际工程中,常见的组合方式有两种。一种是把向量和 BM25 的结果按权重相加,比如 Elasticsearch 的 hybrid query 或者用 RRF(Reciprocal Rank Fusion)做结果合并。另一种是两阶段检索——先用向量和 BM25 各捞一批候选,再用重排序模型统一精排。后者在数据量大的场景下更常见,因为它把“召回”和“精排”两个目标的优化解耦了。
2.2 召回与精排解耦:各司其职
把召回和精排解耦,是我认为大厂检索系统里最值得借鉴的设计思路。召回阶段的任务是“别漏”,允许结果里有噪声;精排阶段的任务是“别错”,把真正有用的排在前面。
召回阶段可以用各种轻量方法快速圈定候选集。BM25 负责精确词匹配,向量检索负责语义扩展,两者都返回 top-N 结果,然后求并集。精排阶段再用一个更强的模型对候选结果逐条打分。这个更强模型可以是 cross-encoder,也可以是 LLM 本身做相关度评估。
解耦带来的直接好处是,每个环节都可以独立优化。召回模型不够好,可以加一路召回;精排模型不够好,可以换更强的。如果一开始就把检索和排序揉在一个环节里,后面想优化时就会发现自己被绑住了手脚。
2.3 典型的大厂 RAG 检索链路长什么样
我以自己做过的一个项目为模板,描述一个典型的、可以直接落地的检索链路:
- 用户 query 进入系统后,分词器同时做两路处理:一路做关键词提取给 BM25,一路做向量化给 ANN 检索。
- 两路检索分别返回 top-N 候选,其中 BM25 会额外输出词法命中详情,向量检索输出相似度分数。
- 两路结果通过 RRF 或加权融合,得到一份合并后的候选列表。
- 候选列表里的每条结果,连同原始 query,一起送入重排序模型,输出精细化的相关度分数。
- 根据最终分数截断,取 top-K 作为上下文进入 LLM。
- 最后,LLM 生成的答案和参考来源一起返回给用户。
这条链路看起来简单,但每一个环节都有大量的工程细节。比如 RRF 的 k 参数怎么设,重排序模型如何处理长文本,上下文窗口怎么分配 token 等等。后面我会逐一展开。
3. 混合检索怎么落地:BM25 与向量检索的协同工作
3.1 BM25 为什么在 RAG 时代依然是主力
有人可能觉得 BM25 是上个世纪的算法,在 AI 时代是不是该淘汰了。我负责任地说,它不但没有被淘汰,反而是很多大厂检索系统里最稳定的一路召回。
BM25 的核心逻辑很简单:对 query 里的每个词,统计它在当前文档里出现了多少次(词频),以及这个词在整个文档集里有多少文档包含它(逆文档频率)。词频越高、包含该词的文档越少,这个词对当前文档的贡献分就越高。
这套逻辑天然适合处理那些“必须精确命中”的场景。比如产品型号、订单编号、人名、地名、日期。这些实体在向量空间里很难被准确表达,但在 BM25 里就是简单的字符串匹配。而且 BM25 的响应速度非常快,在数据量大时配合倒排索引,性能远优于向量检索。
不过 BM25 也有它的硬伤:对同义词和语义改写无能为力。用户搜“怎么退货”,文档里写的是“退款流程”,BM25 一个词都匹配不上。所以 BM25 和向量检索从来不是二选一的关系,而是互相补位。
3.2 向量索引的工程选型:HNSW 还是 IVF
做向量检索绕不开索引结构的选择。目前工业界最主流的是 HNSW(Hierarchical Navigable Small World),它通过多层图结构实现快速近似最近邻搜索,在高召回率下依然能保持不错的延迟。
另一个常见选择是 IVF(Inverted File),它先把向量空间聚类成若干个桶,查询时只在最近的几个桶里搜索。IVF 的优点是内存占用低、构建速度快,但召回率在高维度下不如 HNSW 稳定。
我个人的经验是,数据量在百万级别以内,优先用 HNSW,参数就按默认配置来,通常效果已经足够好。数据量到了千万级以上,再考虑分布式方案和 IVF 的变体。还有一个容易忽略的问题:内存。HNSW 索引的大小通常是向量原始大小的 1.2 到 1.5 倍,如果你的向量有 1536 维(OpenAI 的 text-embedding-3-large 就是 3072 维,很多用 1024 维),千万条数据的内存消耗会非常可观。选型时一定要把内存成本算进去。
3.3 结果融合的三种方案:加权、RRF、排序学习
混合检索最困难的不是“同时跑”,而是“怎么融合两路结果”。这里介绍三种方案,按复杂度从低到高排序。
第一种是加权分数融合。直接把 BM25 分数和向量相似度分数按一定比例相加,比如 0.4 * BM25_score + 0.6 * vector_score。问题在于两种分数不在同一个量纲上,BM25 分数可能只有个位数,余弦相似度则在 0 到 1 之间波动,硬加会偏向其中一路。必须先做归一化,比如各自转成 0-1 范围内的相对分数。这种方式最简单,适合快速上线,但效果比较粗糙。
第二种是 RRF(Reciprocal Rank Fusion)。它不关心两路分数本身的大小,只关心每条候选结果在两路检索里的排名位置,然后按 1 / (k + rank) 累加得分。这个方案的好处是规避了量纲问题,两个异构检索器出来的结果可以直接融合。k 通常取 60 左右,小一点更强调头部结果,大一点更照顾长尾。RRF 的效果在多数场景下都不错,是目前工业界的默认选项。
第三种是排序学习(Learning to Rank)。收集一批人工标注好的 query-document 相关度数据,训练一个轻量模型来融合两路特征。这种方式效果上限最高,但需要标注数据,成本也最高。适合检索质量已经成为核心瓶颈、团队又有数据积累的场景。
我个人在大多数项目里推荐从 RRF 开始,先把链路跑通,再评估是否需要上更高阶的方案。因为从实际表现来看,RRF 已经能把两路检索的互补性发挥得很充分了。
3.4 一个可参考的混合检索实现框架
这里给出一段基于 Elasticsearch 的混合检索实现示意,用代码演示 BM25 和向量检索如何配置在同一个查询里。
{ "query": { "hybrid": { "queries": [ { "match": { "content": { "query": "退货运费怎么算", "operator": "or" } } }, { "knn": { "field": "content_vector", "query_vector": [0.123, -0.456, ...], "k": 50, "num_candidates": 200 } } ] } }, "size": 20 }这个查询同时执行了关键词匹配和向量检索,Elasticsearch 内部会自行做 RRF 融合。注意 knn 参数里 num_candidates 要大于 k,这样召回池更大,能提升召回质量。
如果你不想依赖 Elasticsearch 的 hybrid 能力,也可以用两段式实现:先分别跑 BM25 和向量检索,各自返回 top-N 的 doc_id,然后在代码里做 RRF 合并,再把合并后的 doc_id 列表去重,去查文档详情。这种方式灵活度更高,也方便你后续把结果扩展给重排序模型使用。
4. 重排序:检索质量提升最关键的一步
4.1 为什么召回 top-K 不能直接用
很多人做 RAG 的时候有个习惯:向量检索返回 top-3 或者 top-5,不加处理直接塞给 LLM 当上下文。这在知识库内容高度规范、query 也相对标准的场景下问题不大,但一旦 query 复杂起来,或者知识库里有大量语义相近的内容,直接使用召回结果会非常浪费 LLM 的上下文窗口。
原因很简单:召回阶段用的是双塔模型或者 BM25,它们对 query 和 document 是分别编码的,两者之间的交互非常有限。而相关度判断本质上需要 query 和 document 的深度交互——比如“这个文档里有没有明确回答 query 的核心问题?”。这种交互式判断,只有 cross-encoder 这类模型能做得好。
重排序模型的作用就是把召回回来的候选结果重新打分。它把 query 和 document 拼接成一个序列,用注意力机制去建模两者之间的交互关系。这个方案精度比双塔高很多,但计算开销也大得多,因为每个候选都要跑一次模型推理。所以典型做法是:召回阶段用轻量方法捞 50-100 条,重排序只对其中的 top-50 精排,最后取 top-5 给 LLM。
4.2 重排序模型选型:从 miniLM 到 LLM
重排序模型的选择范围挺大,按能力从低到高大概可以分成三档。
低档是 small 级别的 cross-encoder,比如基于 miniLM 系列蒸馏出来的模型。优点是速度快,CPU 上都能跑,适合对延迟极度敏感的场景。缺点是精度有限,复杂语义关系判断不到位。
中档是中等规模的跨编码器,比如 BGE-Reranker 这一类的模型。这类模型的参数量在几百 M 级别,精度明显更好,在 GPU 上跑 batch 推理的延迟也可以接受,目前在中文 RAG 场景里用得非常多。
高档是直接用 LLM 做相关度评估。比如把候选文档和 query 一起丢给 LLM,让 LLM 输出一个 1 到 5 的分数。这种方式精度上限最高,但成本和延迟都上去了。一般只对最终进入 context 的 top-10 左右结果用,或者在离线评估时作为标注工具。
我见过一个很实用的中间方案:用中档 cross-encoder 做第一轮精排,把 top-10 结果塞给 LLM 生成答案时,让 LLM 根据检索到的内容自行判断“哪些片段真正有用”。这样相当于在生成层做了一次隐式的二次精排。实操上效果还不错,也规避了 LLM 直接做几十条候选重排带来的高延迟。
4.3 重排序时的截断策略与上下文窗口管理
重排序之后的截断策略也非常影响最终生成的答案质量。这里有两个关键参数:一个是送入重排序的候选数量,另一个是最终保留给 LLM 的文档数量。
候选数量太少,容易被召回的“头部偏置”困住;太多,则重排序的计算开销过大。我一般建议召回 100 条,重排序取前 50,最终保留 5-8 条给 LLM。如果你的知识库内容质量比较高、query 相对简单,可以适当缩小。
最终保留的文档数量取决于两个因素:LLM 的上下文窗口大小,以及单个文档块的平均长度。如果每个块约 500 token,保留 6 个块就是 3000 token,加上 prompt 本身的 token 数,对大多数模型来说完全没问题。如果每个块 1000 token 以上,我会建议先做块压缩,或者减少保留条数,避免上下文塞满无关内容导致注意力被稀释。
很多人忽视了上下文窗口的“注意力稀释”问题。给 LLM 塞的无关内容越多,它对真正相关内容的关注度越低,生成幻觉的概率反而越高。与其多塞几条“可能相关”的文档,不如只塞几条高度相关的。这个取舍在实践中非常重要。
5. 决定检索质量的隐形因素:分块、元数据与查询改写
5.1 分块策略:切得不对,检索精确度直接崩
分块策略对 RAG 检索质量的影响,比我见过的任何模型调参都大。同一个知识库,换一种分块方式,检索准确率能相差二三十个百分点。这真不是夸张,我有过切身体会。
分块的核心矛盾在于:块太大,向量化时信息被平均掉,精确匹配被稀释;块太小,上下文不完整,语义断裂,模型理解困难。最理想的块应该是一个“语义完整的最小单位”——比如一个段落、一个表格的一行、一个 FAQ 的问答对。
实操中我推荐的方案是“递归字符分割 + 语义边界检测”的组合。先按段落切,段落过长的再按句子切,尽量保证每个块在 300-800 token 之间。同时可以做滑动窗口重叠,比如每两个相邻块之间重叠 50-100 token,保证跨块的信息不会因为硬切而丢失。
另外,很多知识库里的内容存在“标题 + 正文”的结构。如果标题信息只存在于前一块,后面的正文块在检索时就缺少了上下文。一个常见的解决办法是在每个块的开头都拼接上文档的标题,或者把多级标题作为块的前缀。这样即使某个块被单独检索到,它自带的元信息也能帮助模型理解内容所处的上下文。
5.2 元数据过滤:向量检索和关键词检索之外的第三层保障
元数据过滤是很多团队容易忽略的强大利器。知识库里的每篇文档往往都带有创建时间、所属部门、文档类型、版本号等结构化属性。在检索时把这些属性作为过滤条件叠加进去,能极大地提高精度。
举个例子。某公司有过去三年的产品文档,每年的参数都不一样。用户问“当前版本的连接方式”,如果只靠语义检索,系统可能把旧版本的文档也捞出来。但如果你在检索时用元数据过滤条件把“版本 = 最新”固定住,就能直接排除掉过期内容。
元数据过滤的实现方式也简单,就是把过滤条件同时应用到 BM25 和向量检索上。向量检索那边的实现是 post-filter 和 pre-filter 两种模式。pre-filter 是在向量检索之前先用结构化条件缩小候选范围,能提升性能也有助于精度;post-filter 是先按向量搜出一批结果再按条件过滤,实现简单但有可能因为过滤条件太严导致最终结果不足。我建议优先用 pre-filter,它对检索质量和性能都有正向作用。
5.3 查询改写:让检索器“听懂”用户的真实意图
用户输入的 query 往往很随意,甚至包含口语表达和指代。直接把这样的 query 丢给检索器,效果一般。所以现在大厂 RAG 链路里普遍会增加一个“查询理解”环节。
查询改写常见的方式有这几种:把口语转成书面语,比如“这个咋退款啊”改写成“退款流程是什么”;补充指代信息,比如“那它的连接方式呢”结合对话历史补全为“XX设备的连接方式是什么”;拆解复杂问题,比如用户问“产品 A 和产品 B 在价格和性能上有什么差异”,改写为几个独立子查询分别检索。
这些改写可以交给一个轻量 LLM 来做,也可以维护一套规则模板。前者灵活度高,后者延迟更低。实际操作里我见过把两者结合的方式:规则先做一轮兜底,LLM 再对规则覆盖不到的 query 做改写。这样既控制了成本和延迟,又保证了大部分 query 的质量。
查询改写与混合检索的配合也很重要。有些团队只对改写后的 query 做向量检索,而 BM25 仍然用原始 query。这样做的好处是:BM25 的精确匹配逻辑不会因为改写而丢失原始词元,而向量检索可以从改写后的语义表达中获得更好的扩展效果。
6. 生产中踩过的坑:检索链路常见问题与排查经验
6.1 “检索结果看起来相关,但答案还是错”的根因分析
这是做 RAG 时最让人头疼的问题。检索出来的内容肉眼看着都对,但 LLM 最终生成的答案却是错的。一开始我会怀疑是 LLM 的问题,后来仔细排查才发现,真正的原因往往在更早的环节。
一个典型案例是“信息散落”。正确答案需要综合两三个不同块的内容才能得出,但检索结果是按相关度排序后独立截断的,只保留了一条最相似的块,缺少了其他关键信息。这种情况下的表现就是“单个块相关度很高,但拼在一起不足以回答问题”。
解决思路有两个方向。第一,在召回阶段增加一个“多样性”维度——检索时不只看单块相似度,还要考虑块与块之间的差异,尽量把不同角度的信息都捞出来。第二,优化分块策略,把经常一起被引用的内容尽量放在同一个块或相邻的块中。有过实践经验的项目,不妨把历史日志里的“失败查询”拿来做块边界调整的参考,看是不是因为切块位置不对导致完整信息被拆散了。
6.2 混合检索两路结果冲突时,该信谁
用混合检索之后,会出现一个有意思的现象:BM25 给出的第一名和向量检索给出的第一名常常不是同一个文档。一旦两路结果差异大,融合后的排序往往不理想,最终答案也会受影响。
遇到这种情况,不要急着调融合权重。先做一次人工抽样分析,看两路检索在具体哪类 query 上表现更优。通常会发现:精确实体类 query 上 BM25 胜出,语义扩展类 query 上向量检索胜出。如果你的产品场景更偏前者,融合权重就向 BM25 倾斜;偏后者则向向量倾斜。如果分布比较均匀,RRF 本身就能给出不错的默认结果。
另一个排查方向是看向量模型和文档内容的匹配度。如果知识库里大量使用专业术语,而你用的 embedding 模型是通用领域的,向量这一路的效果会整体偏低。此时与其调权重,不如考虑微调 embedding 模型,或者选用领域适配性更强的模型。权重调整治标不治本,模型能力才是上限。
6.3 检索延迟飙升:从索引参数到召回数量的全链路排查
生产环境中的检索延迟问题,排查起来需要按链路逐段定位。我遇到过几次延迟突增的情况,原因各不相同,排查路径倒是很有参考价值。
第一步检查向量索引类型和参数。HNSW 的 ef_search 参数直接决定了搜索深度和延迟。ef_search 太大,延迟会线性增长;太小,召回率下降。我一般建议 ef_search 设为 top_K 的 5-10 倍,再根据线上延迟反馈逐步调整。
第二步检查召回数量。如果召回数量从 20 调到了 100,重排序模型那边的延迟就会成倍上涨,因为每个候选都要跑一次模型推理。这种情况下优先考虑减少召回数量,或者在重排序前先做一轮粗过滤,把明显不相关的结果去掉。
第三步检查数据分布。如果某些热门文档被频繁检索,而你的检索架构里没有缓存机制,高并发时服务端压力会显著上升。给检索结果加一层 LRU 缓存,对相同 query 的重复请求直接命中缓存,是性价比非常高的优化手段。
提示:给向量索引留够内存空间。HNSW 图结构在内存不足时会触发磁盘交换,延迟会从几十毫秒飙升到几百毫秒甚至秒级。分配内存时建议预留 20%-30% 的余量,不要贴着上限部署。
6.4 常见问题速查表:检索质量与性能调优
我把平时排查检索问题时最常遇到的清单整理成一个速查表,方便对照自查:
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 精确数字、型号答错 | 纯向量检索无法精确匹配 | 检查是否启用 BM25 混合检索,优化实体类 query 处理 |
| 新词、内部术语检索不到 | embedding 模型未覆盖该词汇 | 加关键词兜底、微调 embedding 模型、更新分词词典 |
| 检索结果相关但答案不完整 | 信息散落在多个块中 | 优化分块策略,增加检索多样性,适量增加保留块数 |
| 相似度分数普遍偏高 | embedding 模型对领域文本区分度差 | 评估更换领域适配模型,或微调后对比评测 |
| 延迟随并发上升明显 | 向量索引扫描成本高、结果没有缓存 | 检查 HNSW 参数、增加缓存、优化召回数量 |
| RRF 融合后排序异常 | 两路分布差异大、某一路整体偏差 | 先单独评估两路效果,再决定权重或换融合方式 |
| LLM 生成的答案频繁幻觉 | 上下文相关度不足或上下文过长 | 检查重排序截断策略,压缩上下文,减少无关信息 |
实际排查时,建议先抽样 50-100 条线上失败 query,人工标注出问题环节是“召回漏了”“排序偏了”还是“生成错了”,再对症下药。这一步虽然费时间,但能避免你在错误的方向上反复修修改改。
7. 检索链路从 60 分做到 85 分,我的调优顺序
前面讲了这么多概念和工具,最后整理一套我实际项目里验证过的调优顺序。如果你当前的 RAG 系统还在用纯向量检索,按这个顺序走一遍,通常能把检索质量提升一个明显的台阶。
第一步,先把纯向量检索改成“BM25 + 向量”的混合检索,融合方式先无脑用 RRF。这一步通常能把精确匹配类问题的成功率拉起来,成本最低,效果最明显。
第二步,引入元数据过滤。梳理知识库里的结构化字段,把版本、时间、类型这些属性加到检索条件里。这一步针对的是“旧内容干扰新回答”的典型问题。
第三步,加重排序模型。召回扩大到 50-100 条,用 cross-encoder 精排后取 top-5。这一步对复杂 query 的效果提升很明显,也是整套链路里精度收益最高的一环。
第四步,根据线上日志做定向优化。收集失败 query,分析失败类型,是召回漏了就去调分块和召回策略,是排序错了就去调重排序和融合权重。这一步不是一次性的,而是一个持续迭代的循环。
第五步,再考虑查询改写和更细粒度的分块优化。这两项都属于“精雕细琢”的范畴,投入产出比低于前四步,但当你的检索质量基线已经到 80 分以上时,细节优化才能帮你继续往上拉。
这个过程中贯穿始终的一个原则是:每走一步,都要有对应的评测方法。没有评测,调优就是盲人摸象。哪怕是拿 200 条线上真实 query 做一轮人工标注评估,也好过没有评测地空调。我见过很多团队耗费大量精力调参,但因为没有评测基准,始终不知道改动到底是变好了还是变差了,最后只能凭感觉行事。
还有个建议:把检索链路做成模块化,每个环节能独立开关。这样你在线上做 AB 对比时,能快速判断是哪个环节带来了收益。比如“混合检索开/关”“重排序开/关”“查询改写开/关”,多做几组实验,你的系统就能长期保持在一个相对健康的状态。
回到开头那个话题,大厂不用纯向量检索,不是因为向量检索不好,而是因为它只是拼图里的一块。真正支撑生产级 RAG 的,是混合召回、重排序、元数据过滤、查询理解、以及一套持续迭代的评测机制共同组成的完整链路。我现在的习惯是,任何新 RAG 项目从第一天起就按这个框架来设计,避免后面走弯路。这个框架不一定是最优解,但至少是一个被验证过、可落地、能稳定迭代的起点。