Skip to main content

RAG 链路与失败模式

把文档丢进向量库、问一句、拿到回答,这三步跑通只要一个下午。跑通之后用户开始说"答案不对",接下来三个月都在补洞。

差别在于:RAG 不是一个组件,是两条流水线,而两条线上的每一段都会坏在不同地方。坏在哪一段,决定了该动哪里。多数团队把力气花在最后一段(换模型、改提示词),而实际出错的地方通常在第二段或第三段。

离线 · 索引在线 · 查询原始文档解析切片向量化向量索引用户提问检索重排组装生成带引用回答查询时读取
图:RAG 的两条流水线——离线索引与在线检索,两条线只在「检索读索引」这一步相交

1. 两条流水线,一个交点​

图上那条竖线是两条线唯一相交的地方:离线建好的索引,在用户提问时被读出来。除此之外两边互不依赖,可以独立改、独立测、独立部署。

这个拆分是后面所有判断的基础——答案不对时,第一件事是分清是"没找到"还是"找到了但没用对"。前者的病在离线侧,后者的病在在线侧,两边开的药完全不同。

1.1 离线侧:把文档变成可检索的东西​

四个动作,顺序不能换。

解析 —— 把 PDF / Word / HTML / 扫描件变成带结构的文本。这一步丢的东西后面找不回来:表格被压成一行文字、公式变成乱码、页眉页脚混进正文、双栏 PDF 的左右两栏被读成一整行。

切片 —— 把长文本切成可检索的单元。这是整条链路里投入产出比最高、也最容易被随手做掉的一段:很多人默认按固定字数切,然后花几个月去调检索参数,其实问题在切片(详见《切片决定检索上限》)。

向量化 —— 把每个切片变成向量。要注意的是:查询侧和文档侧必须用同一个模型,模型一换,全库要重灌。这不是小事,语料大时一次重灌的成本和耗时都不低。

入库 —— 写进向量库,同时把元数据一起写进去。元数据不是可选项:语种、文档版本、权限标签、更新时间、来源 URL,这些决定了后面能不能过滤。检索前过滤和检索后过滤的效果差别很大:先取 top-50 再过滤掉 45 条,剩下的 5 条质量完全没有保证,这一点在《混合检索与 RRF 融合》里有具体说明。

1.2 在线侧:把问题变成有依据的回答​

检索 —— 拿问题去找候选切片。只做向量检索会漏掉"精确词匹配"这一类:产品型号、错误码、人名、代码里的函数名。这也是为什么生产环境基本都用混合检索(详见《混合检索与 RRF 融合》)。提问方式本身也可以先做一层加工,三种做法和适用边界见《查询改写的三种做法》。

重排 —— 候选可能有 50 条,真正该进上下文的只有几条。重排用一个更强的模型给候选重新打分排序,是单点收益最大的一步,代价是多一次模型调用和百毫秒级的延迟。

组装 —— 把选中的切片拼进提示词。这一步最容易被忽略,但它直接决定生成质量:切片的排列顺序、是否带来源标识、上下文超长时怎么截断、以及要不要明确写"只能依据这些内容回答"。

生成 —— 模型出答案。到这里才轮到提示词和模型本身,而如果前三段没做好,换任何模型都救不回来。

1.3 两条线的质量口径不一样​

离线侧看的是语料覆盖率:文档解析干净吗、切片合理吗、有没有漏掉整类文档。这大部分只能靠抽样人眼看——解析得好不好、切片边界对不对,没有 ground truth 可比,自动化指标给不出结论。

能自动化的是解析产物的结构异常检测,它测的不是"质量"而是"反常":单份文档抽出的文本长度是否异常偏短(多半是解析失败或走了图片路径)、表格数量是否符合预期(该有 3 张表只解析出 0 张)、空页与超短页占比是否过高、页眉页脚是否被清掉。这类检测的价值在于把"整批失败"挡在入库之前,至于解析出来的内容对不对,仍然只能抽样看。

在线侧看的是检索指标和 groundedness:Recall@K、MRR、以及答案里每一句话能不能在切片里找到出处。这些能自动测,详见《RAG 评测指标怎么选》。

很多人只测在线侧,然后困惑于"指标都挺好但用户还是说不对"——那通常说明问题在离线侧,而离线侧根本没被测到。

2. 每一段会怎么坏​

下面这张表是实际排查时最有用的东西:先定位症状落在哪一段,再去动那一段。

症状最可能坏在快速验证办法
答案里出现语料里根本没有的说法生成段没约束住把上下文清空再问同一个问题,如果还是答得出来,说明模型在自由发挥
明显该找到的文档没被找到解析段或入库段直接用文档里的原句去搜,搜不到说明压根没进索引
找到了正确的切片,但答案没用它组装段或生成段打印实际进上下文的内容,看那句话在不在里面
找到的切片"看起来相关"但不包含答案切片段打开那条切片看边界,多半是被切断了
换个问法结果差很多检索段同义改写后对比召回集合的重合度
老文档和新文档混在一起回答入库段元数据查元数据有没有写版本和更新时间

2.1 解析段的典型问题​

表格是最先崩的。一个三列的表被压成"型号 A 价格 100 库存 5"这种文本流之后,向量检索能找到它,但模型读不出"哪一列是价格"。公式、代码块、多栏排版同理。

处理办法是让表格走单独的解析通道,输出结构化结果(CSV / HTML / Markdown 表格),不要让它进通用切片器。

2.2 切片段的典型问题​

最常见的是"例外条款被切走":一段话里前半句是规则,后半句是"除非……",切完之后规则进了索引、例外不在这里。模型只看到规则,给出一个明确但错的答案。

这种情况比"没找到"更危险,因为有引用、语气也确定,人一眼看不出错。

2.3 检索段的典型问题​

纯向量检索对精确匹配很弱。一个具体的现象:用户问错误码 E1032,向量检索可能返回一堆讲错误处理流程的段落,但就是不返回那条专门讲 E1032 的短条目——因为它在语义空间里和"错误处理流程"离得太近,而那条短条目本身的语义信息太少。

这类查询正是 BM25 的强项。这就是为什么混合检索是默认解,而不是可选项。

2.4 生成段的典型问题​

模型在上下文不足时会补一个看起来很合理的答案。这是最需要防的一条:如果不显式允许它说"不知道",它会用训练数据里的知识填坑,而填出来的东西会被当成事实往下传。

所以《System Prompt 写的是约束》里那句"失败行为必须写清楚"在 RAG 场景里是硬要求,不是优化项:要明确写"只依据给定内容回答,内容中没有就明确说不知道"。

3. 三种常见的错误归因​

3.1 「换个更强的模型就好了」​

只有当"检索到了正确内容、但模型没用好"时,换模型才有用。实际排查下来,多数 bad case 是内容压根没进上下文,属于检索侧问题。换模型只会让它用更流畅的语气把错的答案说出来——可观测性变差了,而不是变好了。

判断办法很直接:把正确的切片手动贴进上下文再问一次。如果这时候答对了,问题是检索;如果还是答错,才是生成。

3.2 「召回越多越好」​

不是。上下文越长,模型对中间部分的注意力越弱,这就是 Lost in the Middle 现象(Liu 等,TACL 2024):信息放在上下文开头和结尾时容易被用到,放在中间容易被忽略。

实践中常见的做法是控制进上下文的切片数量,并把最相关的放在首尾。堆 50 条切片进去,效果通常不如精选 5 条。

3.3 「评测只看答案对不对」​

只看答案,出了问题定位不到段落。答案错了,可能是没召回(检索问题)、召回了但排序靠后(重排问题)、排序对但被截断(组装问题)、都对但模型没照着说(生成问题)。四种情况的修法完全不同。

所以评测要分层:检索层看 Recall@K / MRR,生成层看有没有 hallucination(答案里的说法能不能在上下文里找到出处)。分层之后每次改动才知道动对了哪一层。

4. 什么时候不该上 RAG​

三种情况,硬上会付出比收益更高的代价。

知识量小且稳定。 如果全部内容只有几页,直接放进提示词或者做成一份静态说明,比搭一套检索链路划算得多。检索是为了解决"内容太多放不下"和"内容会变"这两个问题,不是标配。

需要的是精确查询而非语义匹配。 "这个订单的状态是什么"是数据库查询,不是检索。让模型去向量库里"找"订单状态,是把确定性问题变成概率性问题。正确做法是给模型一个查订单的工具(Function Calling),让它查到确切的结果。

答案要求 100% 确定。 概率生成的东西没法保证永远正确。合规条款、金额计算、法律条文这类场景,要么走规则引擎,要么让模型只做"找到出处"这一步,结论由规则生成。

反过来说,有两类问题会被"单次检索"卡住,需要换形态:答案跨文档、要经过中间实体的(多跳),见《多跳问题该用图谱》;问题需要先查一步再决定下一步查什么的,见《把检索做成一个工具》。这两种不是"RAG 做得不好",是流水线形态本身不匹配。

5. 一个能跑起来的起步配置​

不追求最优,只追求"先跑起来并且每一段都能单独测":

  • 解析:先用成熟的开源解析器(这一步不要自己写),表格单独走一条通道
  • 切片:按段落/小节的语义边界切,目标 300–600 token,重叠 10%–20%——这是起步值不是结论,用真实问题集验证之后再定(详见《切片决定检索上限》)
  • 向量化:选一个通用 embedding 模型,记下模型名和版本,写进配置而不是散在代码里
  • 检索:dense + BM25 两路,RRF 融合,候选取 50
  • 重排:用一个 cross-encoder 收窄到 5 条
  • 组装:切片带来源编号,开头写明"只依据编号内容回答,没有就说不知道"
  • 评测:先攒 50 条真实问题 + 期望答案,每次改动跑一遍(详见《RAG 评测指标怎么选》)

比这些数字更重要的是每一段都要能单独看中间结果。看不到切片长什么样、看不到召回了哪几条、看不到实际进上下文的是什么,就只能靠猜,而猜在 RAG 上几乎总是猜错。

还有两个选型问题不在本文范围内,但会直接影响要不要上这套链路:语料能不能直接塞进长上下文(见《长上下文能取代检索吗》),以及答案需不需要来自会变的外部来源(见《联网检索与时效性》)。这两个问题的答案如果分别是"能"和"不需要",那这套检索管道可能从一开始就不必建。