多跳推理 RAG:「A 和 B 有什么关系」需要检索两次的问题

「A 和 B 有什么关系」:单跳检索的天花板

多跳推理 RAG:跨文档、跨实体推理

桥接型与比较型问题

把生产 RAG 系统的失败查询拉出来分类,有一类的特征非常鲜明:答案需要两段信息的接力。「采购新流程是哪位负责人提出来的」——第一跳找到「采购新流程」的文档,文档里只有流程号,第二跳拿流程号去查提案记录才能找到人名。「A 项目和 B 项目的验收标准有什么不同」——两跳分别取回两个项目的标准,第三步是对比。这类问题在学术上被分成两型:桥接型(bridge,前一跳的结果是后一跳的检索键)和比较型(comparison,多跳并行取回后聚合对比)。

单跳 RAG 在这两型问题上的失败是结构性的,不是检索器不够好。桥接型问题里,最终答案所在的文档与原始查询的词汇重叠几乎为零——用户问「负责人是谁」,答案文档讲的是某次提案会议,两者之间唯一的桥是中间实体(流程号)。嵌入检索按语义相似度召回,原始查询与答案文档的相似度天生偏低;比较型问题里,任何一个「半答案」chunk(只讲 A 的标准)都不完整,模型拿着一半信息就开始对比,输出的是没有依据的编造。

为什么一次检索必然失败

更准确地说:一次检索的失败在于它把多跳问题当成了单跳问题来检索。多跳问题的查询本身不是信息需求的最小单位——它是复合需求,必须先被分解成子查询链,每个子查询分别命中各自的证据,再在证据集合上做推理。这不是检索质量问题,是问题理解的粒度问题。识别出「这是一个多跳问题」本身就是一个需要显式处理的决策点,多数系统的检索器根本没有这个概念。

顺带澄清一个常见误解:查询改写(rewrite)解决不了多跳。改写优化的是「查询与目标文档的语义对齐」,它是对单次检索的精化;多跳需要的不是更好的措辞,而是多次互相依赖的检索,第一跳的结果改变第二跳的检索键。两者正交,不要指望调改写 prompt 来救多跳场景。

多跳检索的四种范式

查询分解:Self-Ask 与子问题树

最直接的范式是先把复合问题拆成子问题序列,再逐个检索。Self-Ask(Press 等,2022)给出了标准化的执行协议:模型先判断「这个问题是否需要跟进一步」,需要则显式产出「#1 子问题?」并停下来等检索结果返回,再基于答案产出「#2 子问题?」——子问题之间用前一跳的答案作输入,形成接力。分解出的子问题树可以串行(桥接型,必须接力)也可以并行(比较型,各自独立检索后聚合)。

工程上的关键点在分解的忠实性:模型拆出的子问题必须保留原始问题的约束(时间范围、实体限定),并且中间答案要显式回填进下一跳的查询。我们的实现里专门有一层「子问题校验」——子查询里的实体必须能在原始问题或已获取的证据里找到出处,防止分解过程引入原文没有的实体(分解幻觉),这类错误一旦发生,后面所有跳都在忠实地检索一个不存在的前提。

检索与推理交错:IRCoT

查询分解是「先拆完、再执行」,IRCoT(Retrieval Interleaved Chain-of-Thought,Trivedi 等,ACL 2022)提出了另一种节奏:每生成一句推理,立刻触发一次检索。推理链的第一句(往往包含第一跳的关键实体)作为第一跳查询,检索结果注入后继续推理第二句,第二句又成为第二跳的查询——推理和检索逐句交错,推理的内容天然成为检索键。

IRCoT 相比先分解后执行的显著优势是分解错误不传染:它不做一次性的完整规划,而是让每一步检索都由当前最新的推理状态决定,第一跳搜偏了,推理链会带着偏差信息继续生成,第二跳的检索键会自然地往回拉。在 MuSiQue 这类高难度多跳基准上,交错式对先分解式的优势是稳定的。代价是循环次数不可预算——推理几句、检索几次由生成过程决定,成本控制比固定步数的分解方案更难。

flowchart TD
    Q["复合问题"] --> D{"范式选择"}
    D -- "结构清晰" --> P["Self-Ask:分解子问题树"]
    P --> R1["串行/并行检索各跳"]
    D -- "路径不确定" --> I["IRCoT:推理-检索逐句交错"]
    I --> C{"每步评估:证据够吗"}
    C -- "不够" --> I
    C -- "够了" --> A["证据集合上生成答案"]
    R1 --> A
    A --> V["多跳校验:每跳证据链齐全"]

检索图上的束搜索

第三种范式把多跳检索显式建模成图上的路径搜索。第一跳检索取回 Top-N 候选片段,每个片段是一个潜在的路径节点;对每个节点并行展开第二跳(拿片段里的中间实体做检索键),形成第二层候选;最后在「路径」粒度上打分——一条路径(片段1 → 片段2)的得分综合两跳的检索分与两片段的衔接度——得分最高的若干条路径的证据送进生成。这就是 beam search 式的多跳检索,它比串行接力的鲁棒性更好:串行方案在第一跳选错片段时整条链路报废,束搜索保留多条候选路径,错误路径在打分阶段被自然淘汰。

代价是计算量乘上束宽(beam width)。工程上的裁剪手段:第一跳用轻量启发式预筛(比如中间实体的类型过滤——问题问「人」,就只在候选里保留含人名实体识别结果的片段),把束宽控制在 3-5 以内。我们在内部客服知识库上的对照:束宽 4 的图搜索比单路径串行的桥接型问题准确率高约十个百分点,检索调用量增加约三倍——值不值,取决于你的多跳问题占比(后面算账)。

图遍历:多跳的天然盟友

如果语料已经建了知识图谱(GraphRAG 那条链路),多跳问题有第四条路:把检索键的接力变成图上的边遍历。实体 A 到实体 B 的两跳关系,在图上是两次邻接查询,毫秒级完成,不用 LLM 参与「想出第二跳怎么搜」。图遍历对「关系型多跳」(谁和谁有什么关联)的效率碾压检索接力;它对「内容型多跳」(文档 A 的结论被文档 B 引用)不如检索灵活。两种范式不冲突:图负责关系骨架,检索负责内容血肉,按问题类型分流。

基准与真实差距

HotpotQA、MuSiQue 与生产分布

学术基准给多跳能力划了标尺:HotpotQA(Yang 等,EMNLP 2018)是维基百科两跳问答的经典基准,其 distractor 设置(检索池里混入相似的单跳干扰文档)直接模拟了生产里的最大痛点——干扰项与真证据语义相近,单跳检索高分召回的可能是干扰文档;MuSiQue(Trivedi 等,TACL 2021)把跳数提到 2-4 跳且要求可分解性严格(干扰问题不能抄近道),是目前公认更「诚实」的多跳基准;2WikiMultiHopQA 侧重结构化关系(职位、继承、联盟)的桥接。这些基准上的当前最优方法已经把两跳做到接近饱和,但三跳以上仍是显著的开阔地,而生产里的多跳问题往往比基准更脏:子问题没有标准措辞、中间实体有别名、证据分散在结构完全不同的文档里。

生产里的多跳占比与识别

在我们自己的多套生产知识库查询日志里,多跳问题的占比稳定在一成到两成——不算高,但它们集中在高价值场景(跨系统的追溯类问题、对比决策类问题),用户对这类问题的失败容忍度最低。多跳识别器不需要复杂模型:一个轻量分类(问题里是否含两个以上实体 + 关系疑问词 + 无单一文档可完整回答的历史统计)加人工复核样本迭代就够用。识别的意义是分流——不是所有查询都该走昂贵的多跳管线。

成本与预算控制

多跳管线的成本账要算三笔。LLM 调用:分解式是「1 次分解 + N 次子查询检索 + 1 次汇总」,交错式不可预算但可以设步数上限;检索调用:束搜索是束宽乘跳数,图遍历是边数;端到端时延:串行接力是各跳时延之和,两跳典型值是单跳的三到四倍。预算控制沿用 Agentic 管线的那套缰绳(硬步数上限、证据充分性评估、强制收束),多跳只是把「循环体」从泛化检索换成了结构化接力。

两条省钱经验:跳间共享——同一会话里第二跳往往与第一跳检索池高度重叠,检索结果做会话级缓存能省两到三成调用;跳级并行——比较型问题的多跳互相独立,并行发起检索,时延从「跳数倍」压回「单跳多一点」,这是我们实测中对多跳时延体感改善最大的一项优化。

何时不要做多跳

反面清单同样重要,三类查询不要上多跳管线:伪多跳——问题措辞像多跳(「A 和 B 的关系」)但语料里恰好有一篇文档完整回答了它,这批查询用单跳加 reranker 就能解决,上多跳是花四倍的钱买同样的答案;可缓存答案——高频多跳问题(往往是组织的常见决策场景)答案稳定,离线预计算 + 缓存命中比实时接力便宜一个数量级;证据不存在——分解出的子问题在语料里无解时,及时收束输出「缺少 X 的信息」比无意义地多跳强。最后一条是所有多跳系统的通用纪律:校验证据链完整性——答案生成前检查每一跳都有对应证据片段,缺一跳就说明哪一跳缺失,绝不允许模型在断链的证据上「补全」推理。

小结

多跳问题的本质是「检索键的接力」:桥接型的后一跳依赖前一跳的结果,比较型需要多路并行后聚合,单跳检索在结构上无解,查询改写也救不了它。四种范式按确定性递减排布:结构清晰的用 Self-Ask 分解、路径不定的用 IRCoT 交错、需要鲁棒性的用束搜索图检索、有关系图谱的用图遍历。

工程上,多跳是高成本管线,纪律比能力重要:入口先识别分流(生产占比一到两成,且集中在高价值场景)、伪多跳与可缓存答案用便宜方案截留、步数与束宽设硬预算、比较型跳级并行压时延、答案生成前强制校验证据链。多跳能力值得建,但它应该像急救室一样——平时大部分流量走门诊,只有被识别的疑难杂症才推进去,并且进去了就有全套的监护。

← 返回资讯列表

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?