第一次接触 RAG(Retrieval-Augmented Generation,检索增强生成)时,很容把它理解成向量数据库加大模型:文档切块,调用 Embedding,查出 Top-K,然后把结果塞进 Prompt。几十行代码便能跑起来,看起来也确实如此。
但只要开始调优,问题就来了:Chunk 应该切多大?为什么明明有正确文档却搜不到?已经加了 Reranker,答案为什么还是不对?这时候再把 RAG 当成几个 API 的组合,就很难定位问题了。
我更愿意把 RAG 看成一个带有生成能力的搜索系统。检索系统负责从外部知识库中找到证据,大模型负责阅读证据并组织答案。这条链路的产品形态较新,文档切块、稀疏与稠密检索、近似最近邻搜索、多阶段排序这些组件,却大多来自信息检索的长期积累。
这个视角带来一个非常实用的区分:系统究竟是没有找到证据,没有排好证据,还是没有正确使用证据?把这三件事拆开,才知道优化应从哪一环开始。
RAG 解决的是知识边界问题
假设我们要用大模型回答公司内部的报销问题。报销制度每年都可能调整,不同部门的标准也不一样,有些文档还只能向特定员工开放。显然,我们不可能每改一条制度就重新训练模型,也不能期望模型凭空知道企业内部文档。
这就是 RAG 要解决的问题。它不将所有知识都压进模型参数,而是把知识留在外部系统中,需要时再查找。文档可以独立更新,检索时可以校验权限,最终答案也可以附上原文出处。
RAG 将知识存储与语言生成分开:
- 外部知识以文档或结构化数据的形式保存;
- 检索系统根据当前问题找到候选证据;
- 大模型在有限的上下文中阅读证据并组织答案。
RAG 不等于向量数据库加大模型。它是一条完整的数据处理与检索管线,任意环节的误差都可能传递到最终答案。
从一次请求看懂 RAG 的检索链路
先不急着看 Chunking、Embedding 和 Reranker 这些具体组件。把一次 RAG 请求拉开来看,其实只有索引、召回、排序和生成四个阶段:
文档 → 解析 → 切块 → 建立稀疏/向量索引
│
用户问题 → 查询理解 → 多路召回 → 结果融合 → 重排序 → 构造 Context → LLM
│
BM25 + Dense
索引阶段:把文档变成可检索的数据
原始文档通常不适合直接进入检索系统。PDF 需要解析版面,网页需要移除导航和广告,表格、列表与代码块则需要保留它们原有的逻辑边界。随后,系统将长文档拆成若干 passage,为它们建立倒排索引或向量索引,并保留标题、来源、时间和权限等元数据。
索引阶段决定系统能看到哪些信息。如果文档解析丢失内容,或者切块拆开了一个完整事实,后续的 Embedding 和 Reranker 都无法恢复它。
召回阶段:快速获得尽可能完整的候选集
召回的任务可以概括为一句话:先别漏掉正确答案。这一阶段不追求完美排序,而是在可控的成本内,从大量文档中捞出一个覆盖充分的候选集。稀疏检索负责字面匹配,稠密检索负责语义匹配。当向量数量较大时,系统还会使用 ANN(Approximate Nearest Neighbor)索引,在查询速度与召回率之间做取舍。
排序阶段:让更相关的证据优先进入上下文
召回模型通常对查询和文档分别建模,以便预先计算文档表示。这种架构速度快,但无法充分建模查询与文档之间的细粒度交互。因此,系统往往会对一个较小的候选集使用 Cross-Encoder 重新计算相关性。
这是经典的 cascade ranking:第一阶段快而广,第二阶段慢而准。第二阶段只能重新排列已经出现的候选项。如果第一阶段没有召回正确文档,Reranker 也无法凭空将它找回来。
生成阶段:将检索结果变成可验证的答案
系统最后将排序后的文档片段、用户问题和回答规则组装成 Prompt。这一阶段不是简单地把 Top-K 结果全部拼接起来:片段之间可能重复、矛盾或超出上下文预算,过多的弱相关内容也会稀释关键证据。
一个成熟的 RAG 系统还应保留每个片段的来源。生成策略要求模型依据证据回答,在证据不足时明确说明限制。否则,检索只是为模型提供了更多文字,没有建立可溯源的回答机制。
文档切块:检索粒度决定信息粒度
为什么需要切块?假设一份两百页的技术手册同时包含安装、配置、故障处理和 API 说明,而用户只想知道某个错误码的处理方法。如果直接用整本手册参与检索,那几行有用的内容就会淹没在其他章节中。切块的目的,就是让局部的相关性信号更集中。
Chunk 不是越小越好。片段过小,会丢失定义所属的章节、代码依赖的上下文和结论的前置条件。片段过大,则会引入无关信息,增加 Embedding 和 LLM 的处理负担。与其照搬某个固定 Token 数,不如按下列原则设计:
- 优先按标题、段落、列表、表格和代码块等语义边界切分;
- 保留文档标题和上级标题,使片段脱离原文后仍然可理解;
- 根据查询类型调整粒度,事实问答偏向较短片段,主题性问题需要更多上下文;
- 对长文档采用分层检索,先定位文档或章节,再在局部检索段落。
Chunk size 应当是通过评测选择的系统参数,而不是从示例代码中继承的常数。
关键词检索与向量检索不是替代关系
向量检索既然可以理解语义,是不是就可以丢掉关键词检索了?实际上恰恰相反。
BM25 等稀疏检索方法根据词项在文档与语料库中的统计特征计算相关性。它需要查询和文档在字面上存在一定重合,这看起来是限制,但在搜索产品型号、异常代码、人名、函数名和精确短语时,字面忠实性反而是优势。
稠密检索通过编码器将查询和文档映射到向量空间。DPR 展示了双编码器在开放域问答的 passage retrieval 中具有竞争力。即使用户说的是如何申请退款,文档标题写的是退货流程,这类模型也有机会将两者匹配起来。
然而,Embedding 的语义泛化能力也可能抑制精确区分。对模型而言,两个结构相似的错误码可能语义接近,对用户而言,它们却指向完全不同的故障。因此,更稳健的方案通常是让稀疏与稠密检索同时工作:前者保留精确匹配,后者弥合语义表达差异。
使用 RRF 融合不同的召回通道
现在 BM25 和向量检索分别给出了一份排名,那么应该如何合并?最直观的方法是将两种分数相加,但 BM25 分数和余弦相似度根本不在同一把尺子上。它们的范围、分布与含义均不相同,即使分别归一化,结果也会受当前候选集影响。
Reciprocal Rank Fusion(RRF)绕开了原始分数的量纲问题。它只关心某个文档在每个通道中的排名:
其中 是检索结果列表的集合, 是文档 在列表 中的排名, 用于降低局部异常高排名对结果的过度影响。RRF 不要求不同检索器的分数可比,对没有融合标注数据的系统尤其实用。这一方法来自 2009 年的 RRF 论文,并非为 RAG 临时发明的分数技巧。
Reranker 无法挽救缺失的候选文档
召回阶段先捞出一批候选文档,Reranker 再做一次更精细的判断。它通常使用 Cross-Encoder 将查询与文档同时输入模型,让两者在 Token 层面充分交互。这种方法的相关性判断更精细,计算成本也更高,因此只适合处理较小的候选集。
但 Reranker 只会排序,不会召回。如果正确文档根本没有进入候选集,它再准也没有用。所以,重排序后的可达召回率,不会高于第一阶段候选集本身的召回率。
优化时,先判断正确文档是否进入候选集,再检查它的排名。如果候选集缺少目标文档,优先检查 Chunking、查询表示、召回通道和 Top-K。如果目标文档已经进入候选集,但排名较后,再调整融合方法与 Reranker。
查询改写会改善表达,也会引入偏移
用户很少会使用知识库中的标准表述来提问。他可能只输入一句“这个怎么退”,其中的“这个”要从对话历史中找,“退”在文档中又可能写成退款、退货或取消订单。LLM 可以补全上下文、拆分复合问题,或者生成多个表达变体,从而增加匹配到文档的机会。
查询改写也会损伤原始意图。当模型引入原问题中不存在的限定条件时,检索方向会发生 Query Drift。在多轮查询中,后续改写还可能不断强化一次错误假设。
更稳健的做法是保留原始查询,让原查询与改写后的查询并行检索,再对结果去重、融合和重排序。这样既能利用 LLM 的表达能力,也为原始意图保留了一条不受改写影响的检索通道。
RAG 带来的两项变化
讲到这里,可能会有一个疑问:既然 RAG 的许多环节都能在信息检索中找到前身,那么它究竟新在哪里?我认为主要有两点:工程门槛降低了,人与搜索系统的交互方式也变了。
第一项是工程门槛的下降。开源框架将文档解析、Embedding、向量索引、重排序与模型调用封装为统一组件。开发者因此能以较低成本搭建知识问答系统。封装虽然没有发明底层算法,却扩大了技术的可用范围。
第二项是交互范式的变化。传统搜索引擎返回文档列表,用户自己完成阅读、比较和综合;RAG 将多份证据交给大模型,由模型完成跨文档的组织与表达。当问题需要拆分成多个子问题时,LLM 还可以进一步成为检索规划器,决定搜什么、是否继续搜索,以及如何验证已获得的信息。
搜索引擎正在吸收生成能力,RAG 也在重新引入稀疏检索、多路召回、结果融合和标准评测。两者正在走向相似的系统形态:多通道检索负责找到证据,神经模型负责判断相关性,大模型负责规划与生成。
如何诊断一个效果不好的 RAG 系统
假设系统回答错了,我们第一反应很可能是换一个更强的大模型。但如果模型从一开始就没有看到正确证据,换模型只是让它更流畅地回答错误内容。
因此,评测 RAG 时不能只看最终回答,而要把链路拆开:
- 建立一组真实查询,为每个查询标注能够支持答案的文档或片段;
- 使用 Recall@K 检查目标证据是否进入候选集;
- 使用 MRR、NDCG 或任务定制指标检查目标证据的排名;
- 在固定检索结果的条件下单独评估回答的忠实性、完整性与引用准确性;
- 最后进行端到端评测,观察优化是否改善用户任务。
对应的优化顺序通常是:先检查文档解析与切块,再建立可解释的 BM25 基线,然后加入向量召回和 RRF,调整 Top-K 与 ANN 参数,最后再优化 Reranker 和生成策略。这个顺序并不华丽,却能避免用更强的模型掩盖上游的数据问题。
总结
RAG 的关键贡献不在于重新发明了搜索算法,而在于将成熟的检索能力与大模型的语言理解和生成能力组合起来,并大幅降低了构建知识应用的工程门槛。
但封装不会消除底层取舍。Chunk 的粒度、稀疏与稠密召回的互补、RRF 的排名融合、候选集的召回上限,以及查询改写的偏移风险,仍然决定着一个 RAG 系统是否可靠。
所以,与其把 RAG 当成一组默认参数和框架 API,不如把它当成一个需要逐环节评测的检索系统。大模型可以让答案表达得更自然,也可以综合多份材料;但它能够回答多好,首先取决于我们给了它什么。