在多跳问答(Multi-hop QA)场景下,RAG 系统应该如何设计?
在多跳问答(Multi-hop QA)场景下,传统的“一次检索+一次生成”的朴素 RAG(Naive RAG)往往会失效,因为答案分散在不同的文档中,且文档之间存在逻辑关联(例如:文档 A 包含线索,指向文档 B,文档 B 包含最终答案)。
设计一个能够处理多跳问题的 RAG 系统,核心在于将推理(Reasoning)与检索(Retrieval)交织在一起。以下是针对多跳 QA 场景的 RAG 系统设计方案,分为架构模式、关键模块设计和优化策略三个部分。
一、 核心架构模式
针对多跳问题,主要有三种主流的架构设计思路:
1. 迭代检索 (Iterative Retrieval)
这是最直观的方法。系统通过多次检索循环来逐步逼近答案。
- 流程:
- LLM 基于原始问题生成一个初始检索 Query。
- 检索 Top-K 文档。
- LLM 阅读检索到的文档,判断信息是否足够回答原始问题。
- 如果不够,LLM 基于当前获得的上下文(Context),生成下一个检索 Query。
- 重复上述步骤,直到收集齐所有信息。
- 优点: 灵活,不需要复杂的预处理。
- 缺点: 延迟高(多次 LLM 调用),容易陷入死循环或检索偏离。
2. 问题分解 (Query Decomposition / Least-to-Most)
在检索之前,先规划路径。
- 流程:
- 分解器(Decomposer): LLM 将复杂的多跳问题分解为一系列简单的子问题(Sub-questions)。
- 例: “奥巴马出生地的现任市长是谁?” -> ① “奥巴马出生在哪里?” ② “[地点]的现任市长是谁?”
- 顺序执行: 系统先检索子问题 ①,获得答案(火奴鲁鲁)。
- 变量替换: 将答案代入子问题 ②(火奴鲁鲁的现任市长是谁?),再进行检索。
- 最终生成: 汇总所有信息回答原始问题。
- 分解器(Decomposer): LLM 将复杂的多跳问题分解为一系列简单的子问题(Sub-questions)。
- 优点: 逻辑清晰,每一步检索都更精准。
- 缺点: 依赖于问题分解的质量,如果第一步分解错或回答错,后续全错(级联错误)。
3. GraphRAG (基于知识图谱的 RAG)
这是目前解决多跳问题最前沿且有效的方法。利用知识图谱(KG)天然的实体连接特性来处理“跳跃”。
- 流程:
- 索引阶段: 从文档中提取实体(Entity)和关系(Relation),构建知识图谱,同时保留文本块(Chunks)与图节点的映射。
- 检索阶段:
- 先通过向量检索找到与问题相关的“入口实体”节点。
- 在图谱上进行多跳游走(Traversal)(例如查找邻居节点、2度邻居)。
- 将路径上的实体关系和关联的文本块作为上下文。
- 优点: 极强地解决了实体间的隐式关联,可解释性强。
- 缺点: 构建和维护知识图谱成本高,工程复杂度大。
二、 关键模块详细设计
为了构建一个健壮的多跳 RAG 系统,建议采用 “ReAct Agent + 混合检索” 的设计。
1. Query 理解与规划层 (The Planner)
- COT (Chain-of-Thought) Prompting: 强制 LLM 在生成检索词之前先输出推理步骤。
- Step-back Prompting: 如果具体问题很难回答,先生成一个更高层级的抽象问题进行检索,获取背景知识。
- HyDE (Hypothetical Document Embeddings) 的变体: 让 LLM 虚构一个包含推理链的“假答案”,用这个假答案去检索,可能比直接用问题检索更能命中包含逻辑链的文档。
2. 检索层 (The Retriever)
在多跳场景下,单一的向量检索(Dense Retrieval)往往不够,需要混合策略:
- 混合搜索 (Hybrid Search): 向量搜索(语义匹配) + 关键词搜索(BM25,精确匹配实体名)。多跳问题通常涉及精确的实体名称(人名、地名),关键词搜索至关重要。
- 递归检索 (Recursive Retrieval):
- 父子索引 (Parent-Child Indexing): 检索时匹配小切片(句子级),返回时给大窗口(段落/全文)。这有助于LLM理解上下文关系。
- 元数据过滤: 利用时间、地点、文件类型等元数据缩小范围,防止跨域的错误跳跃。
3. 重排序与上下文压缩 (Rerank & Selection)
多跳检索会产生大量文档,容易导致 LLM 的“Lost in the Middle”现象。
- Cross-Encoder Reranker: 对每一轮检索的结果进行精细打分,只保留最相关的 Top-N。
- 自洽性校验 (Self-Consistency): 在将其喂给 LLM 之前,检查检索到的文档 B 是否真的与文档 A 有逻辑联系,剔除噪声。
4. 生成层 (The Reasoner)
- 引用溯源 (Citation): 要求 LLM 在生成答案时标注来源(例如:
[Doc 1]),这迫使模型真正利用检索到的信息,而不是产生幻觉。 - 思维树 (Tree of Thoughts, ToT): 对于极难的多跳问题,让 LLM 生成多个可能的推理路径,并自我评估哪条路径最靠谱。
三、 一个具体的 Pipeline 设计示例
假设我们采用 ReAct Agent 模式,流程如下:
- 用户提问: “电影《奥本海默》的导演执导的第一部电影的主演是谁?”
- Agent 思考 (Thought 1): 我需要先找出《奥本海默》的导演是谁。
- Action 1: 调用搜索工具
Search("Oppenheimer movie director")。 - Observation 1: 检索结果显示导演是 Christopher Nolan。
- Agent 思考 (Thought 2): 现在我需要找出 Christopher Nolan 执导的第一部电影。
- Action 2: 调用搜索工具
Search("Christopher Nolan first directed movie")。 - Observation 2: 检索结果显示是《Following》(1998)。
- Agent 思考 (Thought 3): 现在我需要找出电影《Following》的主演。
- Action 3: 调用搜索工具
Search("Following movie 1998 cast main actor")。 - Observation 3: 检索结果显示主演是 Jeremy Theobald。
- Final Answer: 电影《奥本海默》的导演是克里斯托弗·诺兰,他执导的第一部电影是《Following》,该片的主演是 Jeremy Theobald。
四、 常见坑点与优化建议
“断链”问题:
- 现象: 第一跳找到了文档,但文档里没有足够的信息支持第二跳。
- 解法: 扩大 Chunk Size(切片大小),或者使用“滑动窗口”技术,确保切片包含完整的上下文实体关系。
语义漂移:
- 现象: 随着跳数增加,检索内容逐渐偏离原始意图。
- 解法: 在每一轮检索的 Query 中,始终保留原始问题的一部分核心约束(Context Carryover)。
性能/延迟:
- 现象: 串行多跳导致响应时间过长(3跳可能需要 10秒+)。
- 解法:
- 并行分解: 如果子问题不相互依赖(例如:“A的妻子和B的丈夫分别是谁?”),并行执行检索。
- 小模型路由: 使用轻量级模型(如 GPT-3.5 或本地 Llama-3-8B)做中间的分解和检索决策,只用大模型(GPT-4)做最终总结。
评估困难:
- 建议: 使用专门的多跳 QA 数据集进行测试,如 HotpotQA 或 2WikiMultiHopQA。关注指标不仅是最终答案的准确率,还要评估中间检索路径的召回率(Retrieval Recall)。
总结
设计多跳 RAG 系统的核心是“化整为零,步步为营”。
- 对于一般难度,使用 Query Decomposition(问题分解) 配合 Iterative Retrieval(迭代检索) 即可。
- 对于实体关系极其复杂、跨文档关联度高的场景,引入 GraphRAG(知识图谱) 是最佳选择。