Skip to content
BaiRuic
Go back

从搜索引擎到RAG:检索增强生成背后的技术演进

目录

第一次接触 RAG(Retrieval-Augmented Generation,检索增强生成)时,很容易把它理解成向量数据库加大模型:文档切块,调用 Embedding,查出 Top-K,然后把结果塞进 Prompt。依赖现成的框架,几十行代码就能跑起来,看起来也确实如此。

但只要开始调优,问题就来了:Chunk 应该切多大?为什么明明知识库里有这篇文档,系统却搜不到?已经加上了 Reranker,最终答案为什么还是不对?这时候如果还把 RAG 当成几个 API 的简单拼装,就很难找到排查方向了。

事实上,在工程实践中,我更愿意把 RAG 看成一个带有生成能力的搜索系统。检索系统负责从外部知识库中找到证据,大模型负责阅读证据并组织答案。这条链路的产品形态虽然很新,但文档切块、稀疏与稠密检索、近似最近邻搜索、多阶段排序这些核心组件,大多来自信息检索领域长期的技术积累。

站在搜索系统的视角看,排查问题其实只有三个核心疑问:系统是没有找到证据,没有排好证据,还是没有正确使用证据? 把这三件事拆开,RAG 调优才会有的放矢。

本文将从经典信息检索的视角出发,拆解一次 RAG 请求背后在切块、查询改写、多路召回、打分融合到级联重排上的关键取舍,梳理从传统搜索引擎到现代生成式系统的演进脉络,并给出一套自底向上的工程排查思路。

一、 为什么要把知识留在模型外面?

假设我们要用大模型回答公司内部的报销问题。报销制度每年都可能调整,不同部门的标准也不一样,有些文档还只能向特定层级的员工开放。我们不可能每改一条制度就重新微调一次模型,也不能指望通用大模型凭空猜出企业内部的规章细节。

这就是 RAG 要解决的核心痛点。它不把所有知识都强行压进模型参数,而是把易变的、私密的知识留在外部系统中,按需动态查找。

在实际运行时,知识以原始文档或结构化数据的形式独立保存,更新即时生效;用户提问时,检索系统根据当前权限和问题筛选候选文档;最后,大模型在有限的上下文里像开卷考试一样阅读证据,结合语言理解能力生成回答,并附带原文出处。

RAG 不等于向量数据库加大模型。它是一条完整的数据处理与检索管线,上游任意环节的疏漏,都会沿着链路放大并传递到最终答案中。

二、 拆解一次端到端的 RAG 请求

把一次端到端的 RAG 请求拉开来看,它是由一条数据流水线构成的:

文档 → 解析 → 切块 → 建立稀疏/向量索引
                              │
用户问题 → 查询理解与改写 → 多路召回 → 结果融合 → 级联重排 → 构造 Prompt → LLM 生成
                           │
                      BM25 + Dense

从原始数据入库,到最终生成回答,这条流水线上的每一个节点,背后都承载着来自经典搜索系统的关键工程取舍。

1. 文档切块

原始文档通常不适合直接进入检索系统。比如,PDF 需要解析版面,网页需要剔除导航栏和无用广告,表格、列表与代码块则需要保留原有的逻辑边界。完成清洗后,系统必须将长文档切成若干片段(Passage / Chunk)。

为什么需要切块呢?假设一份两百页的技术手册同时包含安装、配置、故障处理和 API 规范,而用户只想知道某个特定错误码的处理方法。如果直接用整本手册计算相关性,那几行关键内容就会淹没在浩瀚的篇幅中。切块的目的,正是让局部的相关性信号更集中,提高特征匹配的信噪比。

但 Chunk 绝不是切得越小越好。片段过小,容易切碎前后的因果链条,丢失定义所属的章节、代码依赖的上下文和前置条件;片段过大,又会引入大量无关噪音,加重大模型阅读与理解的负担。实际做切块时,没必要机械照搬某个固定的 Token 数,通常考虑几条原则:

首先,切块尽量跟着文章原有的结构走,段落、表格、代码块就是最自然的边界,避免死板地按固定字符数硬切一刀。 其次,切出来的片段最好附带层级上下文,自动拼接所属文档标题与各级大纲路径,使片段脱离原文后依然具备自解释性。 至于切块粒度,得结合具体的业务场景权衡:精准事实问答偏向较小片段,宏观主题分析或跨段推理则需要更宽广的上下文。如果文档动辄几十上百页,还可以采用分层检索,先在文档或大章节定位范围,再在内部精准检索小段落。

索引阶段决定了系统的信息输入上限。如果分块时切碎了关键事实,后续的模型再强也无法凭空恢复它。Chunk size 应该是一个通过业务评测持续调整的系统参数,而不是从示例代码中直接继承的常数。

2. 查询理解

数据准备就绪后,流程进入用户发起请求的第一步。在实际业务中,用户很少会使用知识库里的规范术语来提问。用户可能只随口输入一句“这个怎么退”,其中代词“这个”需要结合多轮对话历史才能理解,“退”在知识库中可能规范写为退款、退货或撤销申请。利用大模型进行查询改写(Query Rewriting),可以补齐代词、拆解复合问题并扩展同义词,从而增加命中相关文档的机会。

但查询改写也是一把双刃剑。当改写模型为原问题引入了不存在的限定假设时,检索方向就会发生意图漂移(Query Drift);在多轮对话中,后续改写甚至会不断滚雪球般强化一次错误的理解。

更稳健的做法是保留原始查询,实施多路并行检索:让用户的原查询与改写后的查询同时发起检索,再对两路结果去重与融合。这样既发挥了大模型理解口语的优势,也为原始意图保留了一条不受过度改写影响的检索通道。

3. 多路召回

拿到整理后的查询后,流程进入召回阶段。召回的核心任务可以概括为一句话:先别漏掉正确答案。这一阶段不追求精细的最终排位,而是在可控的延迟与算力成本内,从海量文档中捞出一个覆盖充分的候选池(通常是数十到数百个候选片段)。

许多刚接触 RAG 的开发者,最直觉的做法是使用纯向量数据库做单路召回:认为大模型时代的 Embedding 已经能够理解深层语义,传统的关键词匹配已经过时。

但在实际生产环境中,单一的向量召回通道往往存在明显的盲区:

向量检索(稠密检索)通过神经网络将查询和文档映射到同一个连续几何空间。即使用户问的是“如何申请退钱”,文档标题写的是“退款流程”,模型也能凭借语义相似度将两者关联起来。但在检索产品型号(如 iPhone 15 Pro 与 iPhone 14 Pro)、具体错误码(如 ERR_401 与 ERR_403)或特定函数名时,向量表征容易把细微的字面差异当作近义概念泛化掉,导致排位失真。

而传统的稀疏检索(如 BM25)直接统计词项在文档中的频次与在全库中的稀有度。它虽然无法识别同义词,但对精确字面匹配具有极高的忠实性。在排查故障、查找配置项或精准代码定位时,关键词命中就是最强的信息信号。

正因为两种机制的优势与盲区正好互补,成熟的生产系统普遍采用多路召回(Hybrid Search):让稀疏检索保精确命中,稠密检索保语义泛化,两路并行抓取候选。

然而,多路召回虽然解决了漏检问题,却立刻引出了下一个工程难题:两路独立的检索器分别返回了一份候选名单,打分规则完全不同,应该如何将它们合并为一个有序列表?

4. 结果融合

当 BM25 和向量检索各自给出了一份候选排名后,如何将两份结果合并?最直观的想法是将两种分数相加,但 BM25 得分通常是大于 0 的无上界数值,余弦相似度却落在 [0,1][0, 1] 之间,它们根本不在同一量纲上。两者的分布与含义截然不同,即便做线性归一化,也极易被单侧偶尔出现的极端离群分带偏。

倒数排名融合(RRF, Reciprocal Rank Fusion)巧妙地绕开了原始分数的量纲问题。它不看绝对分数值,只关注文档在各个通道中的相对排名次序:

RRF⁡(d)=∑r∈R1k+rank⁡r(d)\operatorname{RRF}(d) = \sum_{r \in R} \frac{1}{k + \operatorname{rank}_{r}(d)}
  • RR 是参与融合的检索系统列表集合(如 BM25 列表与向量列表);
  • rank⁡r(d)\operatorname{rank}_{r}(d) 是文档 dd 在列表 rr 中的排位名次(从 1 开始计);
  • kk 是平滑常数(通常取 60),用来拉平前几名之间的断层差距,避免单路异常榜首垄断排序。

RRF 不需要针对不同系统做复杂的分数校准,对没有训练数据的冷启动系统尤其实用。这一算法早在 2009 年的论文中便已提出,并不是为 RAG 临时发明的技巧。

5. 结果重排

多路召回捞出几十篇候选文档后,系统通常会接入第二阶段的重排序模型(Cross-Encoder Reranker)做更精细的把关。

初检阶段的双塔模型为了实现毫秒级快速筛选,切断了查询词与文档词之间的层间注意力交互;而重排序模型基于 Cross-Encoder 架构,把 [Query, Document] 拼接在一起送入模型,让每一个查询词与文档词在多层注意力网络中充分逐字比对。这种机制能捕捉到极其细微的否定语态、修饰关系与逻辑限定,因此打分精度显著高于双塔向量。

这是经典的级联排序(Cascade Ranking):第一阶段快而广,第二阶段慢而准。但重排序模型存在一条不可逾越的边界:Reranker 只负责重排,不负责召回。如果正确的文档根本没有进入第一阶段的候选集,后端重排模型算得再准也无济于事。级联系统的召回上限,永远受制于初检候选集本身的召回率:

Recallfinal≤Recallstage1\text{Recall}_{\text{final}} \le \text{Recall}_{\text{stage1}}

因此在排查系统效果时,应当先确认目标文档是否出现在召回候选池中,再去看它的具体排名。如果候选集里压根没有目标文档,应优先检查切块方式、查询改写、召回通路与 Top-K 数量;只有当目标文档已经入选、但排在较后位置时,才需要调整融合权重或更换 Reranker。

6. 证据生成

经过重排筛选后,系统将最终精选的文档片段、用户问题与约束规则组装成 Prompt,交给大模型生成回答。

这一阶段的关键在于控制上下文密度:片段之间可能重复、冲突,或者超出上下文预算,混入过多弱相关内容反而会稀释关键事实,诱发大模型幻觉。成熟的生成策略要求模型严格依据提供的证据回答,在证据不足时明确告知限制,并在生成内容中标注出处,建立可溯源的回答机制。

三、 从传统搜索到 RAG,到底新在哪里

既然 RAG 的许多环节都能在传统信息检索中找到影子,那么它究竟新在哪里?主要体现在两点:工程门槛降低了,人机交互范式变了。

第一是工程门槛的下降。现代开源生态把文档解析、Embedding、向量索引、重排序与模型调用封装成了拿来即用的组件,工程师无需从零构建倒排引擎和复杂的精排管线,就能快速搭建起垂直知识库问答。这种封装虽然没有发明新的底层算法,却极大地拓展了检索技术的应用边界。

第二是交互范式的质变。传统搜索引擎返回的是一份文档链接列表,用户需要自己去点击、逐篇阅读、提取信息并做归纳;而在 RAG 中,检索系统将多份证据直接交给大模型,由模型在上下文内部完成跨文档的事实整理与自然语言表达。当问题复杂、需要多步推理时,大模型还能进一步充当检索规划器,自主拆解子问题、发起多轮检索并校验信息。

搜索引擎在吸收大模型的生成能力,RAG 也在重新引入稀疏检索、多路召回、结果融合和离线评测。两者最终走向了高度相似的系统形态:多通道检索负责广撒网找证据,精排模型负责严格评判相关性,大模型负责综合理解与生成。

四、 如何排查一个效果不好的 RAG 系统?

当问答系统给出错误答案时,很多人的第一反应是更换一个参数量更大的大模型。但如果系统从一开始就没给模型输入正确的证据,换更强的模型只是让它以更流畅的语调胡说八道。

诊断一个 RAG 系统,不能只看最后的生成结果,而要把整条链路拆开排查:

首先,挑选几十个具有代表性的真实业务问题,人工标注出能支持正确答案的源文档切块,作为基准评测集。 接着,用 Recall@K 检查召回。验证目标文档切块是否成功进入了前 20 或前 50 的候选集。如果前几步根本没有捞出目标文档,后面再调模型也是徒劳。 然后,用 MRR 或 NDCG 检查排序。目标证据即便召回了,是否排在了前 3 到前 5 的核心展示位。如果排位偏后,重点优化打分融合规则与 Reranker。 之后,固定输入证据评估生成质量。在人工直接给入标准证据的前提下,单独评测大模型回答的忠实性、完整性与引用准确性,确认提示词与模型能力是否达标。 最后,在各个单项环节都理顺之后,再综合验证系统对端到端业务目标的满足度。

对应的工程优化顺序通常是:先检查文档解析与切块粒度,再建立可解释的 BM25 词法基线,然后引入向量召回和 RRF 融合,调整 Top-K 与索引参数,最后才轮到调优 Reranker 和生成 Prompt。这个排查顺序非常自然,却能避免用更强、更贵的大模型去掩盖上游检索链路的数据缺陷。

五、 总结

RAG 的核心突破不在于重新发明了搜索技术,而在于将成熟的检索能力与大模型的语言理解、生成能力无缝结合,大幅降低了构建知识问答系统的工程门槛。

但再好的框架封装,也不会消除底层的工程权衡。Chunk 的粒度切分、稀疏与稠密召回的互补、RRF 的排名融合、候选集的召回上限,以及查询改写带来的语义偏移,仍然实打实地决定着一个 RAG 系统是否靠谱。

与其把 RAG 当成一组默认参数和开箱即用的黑盒 API,不如把它当成一个需要逐环节打磨的检索系统。大模型能让答案表达得更自然,也能融会贯通多份材料;但它究竟能回答得多好,首先取决于给它喂了什么。

参考资料