高级 RAG:让检索成为可诊断、可迭代的证据流程
从查询改写、混合召回、重排、多跳、纠错检索和答案引用理解何时需要升级基础 RAG。
- 记录基础 RAG 失败
- 定位查询/召回/排序/生成层
- 只为目标失败增加组件
- 每步保存证据与来源
- 限制多跳并处理冲突
- 消融验证端到端收益
1先定位失败层诊断
当一个检索增强系统的答案出错时,最常见的冲动是立刻加组件:上重排器、换嵌入模型、启用多查询。但错误可能发生在整条链路的任意一层——解析、切分、查询构造、召回、重排、上下文利用或生成,而新组件很可能加在了与失败无关的位置。因此先要回答“是哪一层偏离了预期”,再决定改什么。
分层诊断的输入是链路各层的产物:原始查询、解析出的文档块、召回候选、重排结果、装配出的最终上下文和最终回答;输出是首次偏离预期的那一层,以及一条可证伪的修复假设。保存每层产物并用证据标注,是定位瓶颈的前提,也是避免组件盲堆的防线。当候选集里根本不含正确信息时,问题出在查询或召回;当候选覆盖了正确信息而答案仍然错误时,问题出在装配或生成;当引用之间相互冲突时,问题往往出在旧版本或错误版本混入了召回。
可证伪假设是这层诊断的核心工具。笼统地说“检索质量不好”无法指导改动,应把每次失败写成可以被实验推翻的具体断言,例如“若把质量例外加入候选,答案会正确”“若过滤旧版本,引用冲突会消失”。随后只改变该假设指向的那一层,并在同一份查询集上复跑。同一数据、单一变量,是后续结论能够成立的前提:一次改动多个环节,收益就无法归因,失败也无法定位。
复跑结果有两种典型解释。若候选覆盖率上升而答案不变,说明瓶颈已经转移到装配或生成,召回不再是主要矛盾,继续在召回上投入就是浪费;若答案上升但延迟超出预算,则不必让所有查询都承担复杂链路的成本,只把复杂链路路由给困难查询。诊断因此不只是找到出错点,也是在决定在哪里继续投入、在哪里收手。
这解释了为什么未定位失败前不能直接堆组件。分层实验——提出假设、只改一层、复跑同一查询集——比同时启用多查询、HyDE、重排器和 Agent 更能说清收益来自哪里,并且保留了安全回退路径:每一层都有清晰的前后对照,任何一层回退都不会波及其余层。
2查询改写与扩展查询
用户提出的问题往往不是最好的搜索词:自然语言里混着歧义、缺省的实体、日期和否定条件,检索系统未必能直接从一句话里读出该找什么。查询改写针对的就是这层落差,常见手段包括消歧、补实体、生成多查询,以及构造假设文档的 HyDE。这些手段的共同作用是补全搜索表达、提高召回,但它们同时也可能引入错误假设,因此原问题与改写结果都应保留并可审计。
改写器的输入是原问题以及其中可识别的实体、日期、否定和歧义,输出是保留这些约束的结构化改写或多个子查询。关键在于改写只补搜索表达,而不替换用户意图。为此改写器应输出结构化差异:补了哪个实体、拆了哪些子问题、保留了哪些日期和否定条件。差异不写出来,后人就无法判断一次检索偏移是刻意的扩展还是事故。
丢掉否定的代价可以用一个例子看清:“不能退吗”被改成“退款流程”后,检索结果在主题上高度相似,但资格判断已经丢失——用户问的是“我有没有退款资格”,改后的查询去找的是“退款怎么走流程”。主题相似掩盖了语义偏差,召回再高也是沿着错误前提收集证据。同理,新增实体也可能把检索带向错误前提,例如把一个问题绑定到某个并不相关的产品线。
所以改写与验收必须分离。改写存在的唯一目的是寻找证据,最终生成必须用原问题来验收:回答是否仍然回答了用户原话所问的事。HyDE 也服从这条规则——假设文档只是构造搜索表示的手段,用生成的一段“理想答案”去匹配文档,不意味着这段假设内容本身有约束力。保留原问题、保留改写差异、保留来源,才能让每一步扩展都可回溯、可撤销。
3混合召回与融合召回
稠密向量检索与关键词检索各有一块对方够不到的区域:关键词检索擅长精确匹配名字、型号和数字,但对同义改写无能为力;稠密向量擅长语义层面的改写匹配,却容易漏掉必须逐字命中的专名与编号。混合召回把两类检索器并行跑在同一查询上,再把结果融合成一个候选列表,用互补覆盖换取召回率。
融合的难点在于两类检索器的分数不可直接比较——BM25 的分数和余弦相似度处于完全不同的量纲。倒数排名融合(RRF)绕开分数,只使用排名。它把 J 路检索器各自给出的候选名次 rⱼ(d) 和一个平滑常数 k 作为输入,为每个文档 d 输出融合分数:
RRF(d) = Σⱼ 1/(k + rⱼ(d))
其中 j 是检索器编号,rⱼ(d) 是文档 d 在第 j 路中的名次。每一路都按 1/(k + rⱼ(d)) 贡献一个项:名次越靠前,rⱼ(d) 越小,贡献越大;多路都排在前面的文档会累加出更高总分。k 是平滑常数,它防止排名第一的项过大而主导融合,也让相邻名次之间的差距变得平缓,减少了单路微小波动对融合结果的冲击。按排名融合而不是按分数相加,从根本上避免了不同量纲直接求和的问题。
融合得到的是一个重新排序后的候选集合,仅此而已。它不判断文档的时效、权限或真实性,这些属性需要在链路更下游的环节单独处理。权重如何分配也应按查询类型验证:哪些查询类型主要靠关键词、哪些靠语义,需要实际验证而不是猜测。同时应保留各路检索器输出的原始名次——融合后的分数无法解释某次回归,回溯每一路原本把相关文档排在第几位,才能定位是哪路检索器出了问题。
4重排与上下文装配排序
融合后的候选虽然都与查询相关,仍可能彼此重复、互相冲突,或只是勉强沾边。重排与上下文装配解决的是“相关之后怎么办”:把候选变成一段生成模型可以可靠使用的上下文。
重排通常交给交叉编码器。它把查询和文档成对输入同一模型做联合编码,逐字交互地判断相关程度,因此比只分别编码再算相似度的双编码器更精确,代价是计算量更大,只能用在候选数量已经缩减之后的小集合上。精排输出的分数给出的是“这个片段适不适合用来回答”,而不是“这个片段是否可信”——前排相关只说明适合回答,不等于可信。
装配环节的输入不只是重排分,还包括每个候选的来源、邻接块、版本和权限;输出是一段去重、且保留必要冲突与引用的上下文。按来源去重可以合并同一文档被切散后的重复块,并扩展相邻块,把碎片还原成完整段落,避免生成模型只看到半句话。同时要过滤过期内容和无权访问的内容。不同观点与时间版本不能简单合并:相互冲突的权威证据必须显式保留下来,并在生成时解释冲突,而不是悄悄丢弃其中一方。关键证据应放在与任务相关的位置附近,并带引用,让最终答案的每一处主张都能追溯到上下文中具体的来源。
5多跳与自适应检索Agentic RAG
有些问题单次检索无法回答:必须先查到 A,才知道下一步该查 B。例如想知道“提出某定理的学者在哪所大学任教”,得先查出定理对应的学者,再用学者名去查任教机构。多跳检索把这类问题拆成子问题,逐步检索,并把每一步发现的中间实体记录进证据槽位;当已有证据不足以回答、或证据之间互相冲突时,触发补检索继续推进。
一次多跳循环的输入是原问题、已填充的已知证据槽位和当前缺口,输出是下一个子查询、新发现的中间实体,以及更新后的槽位状态。每一跳只在填补缺口或解决冲突时才有理由继续;如果当前证据已经足够,继续检索就是徒增噪声。每一步还必须验证实体和来源,因为错误实体会让后续检索滚雪球:一旦第一跳把错误的中间实体写进槽位,之后所有查询都会围绕这个错误前提展开,越查越偏,且后面的正确结果也难以挽回前面的偏差。
因此多跳检索必须带上硬边界:最大步数、检索预算,以及无进展终止。达到步数上限或预算耗尽就停下,用现有证据作答并说明缺口;连续若干跳没有填补任何槽位,同样应当终止。多跳的价值在于把检索变成可验证、可解释的证据链条,而不是无限延伸的搜索循环。
6是否值得升级评测
高级链路不能靠“感觉更强”来证明自己。评测的输入是一个固定查询集,以及基础 RAG 与每个单组件变体的对照,输出是 Recall、NDCG、证据忠实、答案成功、延迟和成本这几项指标的差异。固定查询集保证每次改动都在同一把尺子上衡量,单组件变体则构成组件消融:每次只增删一个环节,看它到底改善了哪一项。
组件消融回答的是端到端问题,而不是局部问题。一个环节改善了自己的局部指标——比如召回率更高、排序更准——并不自动等于系统更好。若重排提升了排序质量而答案成功没有变化,说明瓶颈已经在后续环节,此时应检查上下文利用,而不是继续往前端加组件。指标必须与真实后果挂钩:排序与召回都是中间指标,答案成功、证据忠实才是任务本身,延迟和成本则是任何改进都要付出的代价,四项必须一起报告。
“更多检索不等于更多真相”是升级决策中最容易被忽略的边界。扩充召回往往把重复块、过期内容和低可信来源一并带入上下文,噪声随候选数量放大,反而稀释了原本可靠的证据。因此没有稳定净收益的组件应当删除:消融显示某个环节在答案、忠实、延迟与成本上没有可重复的正向贡献,它就不值得留在链路里。
7一次退款查询怎样逐层修复运行示例
“签收 35 天的质量问题能退吗”不是简单把 top-k 调大就能答好的查询。它同时涉及一般期限、质量例外、时间与资格判断,靠单路召回的任何一路都会漏掉一半证据。高级 RAG 的流程是从原问题出发,先做查询分解,再走 BM25 与向量双路召回,融合重排后经时效与权限检查,最终只把通过治理的材料交给生成。每一步的产物都可以单独检查,发现问题就回流到对应层修复——它不是一条只能向前的组件流水线。
双路召回的名次与融合结果可以完整复算。下表按候选 A 到 D 列出 BM25 名次、向量名次与 RRF 分数(k=60):
RRF 只利用名次,避免了把不可比的 BM25 与余弦分数直接相加;但表中 A、C、D 的得分几乎相同,说明融合不是最终裁判——它只负责扩大候选面,不负责判断谁真正回答了问题。重排必须围绕“退款资格”做细粒度相关性判断,时效过滤还要把看似相关度不低的旧版政策 D 移除。最终上下文保留 A、B 和订单日期,才足以推出结论:一般期限已过,但质量例外仍可能适用。
这个结论是可复算的。若只用单路 top-2,BM25 给出 A+C,缺少质量例外;向量给出 B+D,缺少当前生效的一般规则。融合把两路的证据覆盖合并起来,重排与时效规则再把四个候选缩成能共同支撑结论的 A+B。最终回答只能引用真实取回、且通过治理检查的材料,任何未进入这组证据的断言都无权出现在答案里。
| 候选 | BM25 名次 | 向量名次 | RRF 分数(k=60) | 重排判断 |
|---|---|---|---|---|
| A:30 天一般规则 | 1 | 4 | 1/61+1/64=0.0320 | 必要但不充分 |
| B:质量问题例外 | 5 | 1 | 1/65+1/61=0.0318 | 回答核心 |
| C:到账时间 | 2 | 3 | 1/62+1/63=0.0320 | 主题近但不回答资格 |
| D:旧版政策 | 3 | 2 | 1/63+1/62=0.0320 | 因版本过期剔除 |
8纠错与多跳何时应该停止失败边界
系统发现证据不足后继续搜索,最危险的不是多花几次检索,而是把一次缺证变成无休止循环。防止失控的第一步,是每轮检索都声明尚缺的证据槽位,例如“订单事实已知、一般规则已知、质量例外未知”,而不是笼统地要求“再找更多”。有了槽位清单,新一轮检索只有在填补槽位、解决版本冲突或提高来源等级时才算是取得了进展,其余动作都是空转。
空转之外,错误的修复方向同样常见,常见情形与对应动作如下:
每一行都对应同一个原则:修复要发生在失败实际发生的那一层,而不是在别处做更用力的事。证据冲突时,多数的片段不必然正确,正确做法是核对日期、权限与权威来源。
停止条件必须和进展条件一样硬。应预先设置最大跳数、最大查询数、token 或延迟预算,以及无进展终止;达到任一边界后,输出分为三部分:已知、未知、需要人工确认的事项。连续两轮没有新证据槽位就停止并透明报告不足,而不是让 Agent 自由改写查询继续循环。
还有一条失败边界属于证据来源本身:HyDE 生成的“假设文档”只用于构造搜索表示,不是证据。若假设文本虚构了产品名或条款,后续召回就可能围绕错误前提滚雪球。同样,Agent 自己写出的内容也不能升级为回答证据。回答只能引用真实取回、且通过版本与权限检查的材料。
| 观察 | 正确动作 | 不应做的事 |
|---|---|---|
| 正确文档从未进入候选 | 修查询、切块或召回通道 | 只换生成提示 |
| 候选里有证据但排得靠后 | 重排、去重与版本过滤 | 无限扩大上下文 |
| 证据互相冲突 | 核对日期、权限和权威来源 | 按多数片段投票 |
| 连续两轮没有新证据槽位 | 停止并透明报告不足 | 让 Agent 自由改写查询循环 |
9把因果链连起来综合
高级 RAG 的所有手段,最终都落回同一条从问题到实践的因果链。链条的起点是记录基础 RAG 的真实失败,而不是先入为主的方案清单。有了失败样本,再定位它们发生在查询、召回、排序还是生成中的哪一层;随后只为定位到的目标失败增加组件,一处失败对应一处改动。每一步都保存证据与来源,使任何一层的产物都能被复查、被回退。多跳与纠错循环必须设置边界并显式处理冲突,防止检索在错误前提上无限延伸。最后用固定查询集做组件消融,验证新增环节带来的是端到端收益,而不是局部指标的自我安慰。链路每前进一步,都在回答同一个问题:这次改动,让哪类查询的哪个失败消失了,代价是什么。
- Retrieval-Augmented Generation for Large Language Models: A Survey:RAG 技术谱系
- Precise Zero-Shot Dense Retrieval without Relevance Labels (HyDE):假设文档检索
- Corrective Retrieval Augmented Generation:检索质量判断与纠错
- Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods:RRF 排名融合