# 面向 UltraRAG 工作流的结构化排障指南

本文档提供一套面向 UltraRAG 工作流的结构化排查方法，用于分析以下常见情况：

- 回答结果可疑，但暂时无法判断问题出在哪一步
- 检索结果表面正常，但最终答案明显偏题或解释错误
- 多步工作流前半段正常，后半段逐步漂移
- 会话状态、上下文或历史信息没有被正确继承
- 本地实验表现正常，部署后结果却明显不一致
- 错误现象已经出现，但难以快速定位是检索问题、推理问题、状态问题，还是运行环境问题

UltraRAG 提供了灵活的工作流编排能力、可视化的中间步骤追踪能力，以及面向复杂 RAG 场景的案例分析入口。  
在实际调试过程中，很多问题并不是单点报错，而是由多个环节共同造成的。与其一开始就大幅修改流程，不如先用一套统一的结构化方法，把问题归到正确层级，再做最小修复。

本文档将排查过程分为四个层级：

1. 输入与检索
2. 推理与规划
3. 状态与上下文
4. 部署与运行

建议先做定位，再做修复。  
先判断问题属于哪一层，再决定修改 prompt、检索配置、工作流结构、状态管理，还是运行环境。

## 一、适用场景

当你在 UltraRAG 中遇到以下现象时，可以优先参考本指南：

- 用户问题很明确，但系统命中的知识片段明显偏离主题
- 检索结果看起来相关，但答案组织方式与证据不一致
- 前几步节点输出正常，后续节点开始累计偏差
- 回答语气很肯定，但缺少充分依据或证据连接
- 相同输入在不同轮次中得到不一致的输出
- 工作流中的某些状态变量、历史记录或中间结果未按预期传递
- 新环境首次运行报错，或索引、配置、依赖在部署前后不一致
- 本地实验正常，但线上调用结果明显变差

## 二、建议的排查原则

在开始逐项检查之前，建议先遵循以下原则：

### 1. 先定位层级，再修改实现

不要一上来同时调整检索器、prompt、节点顺序和部署参数。  
先判断问题更像发生在输入与检索、推理与规划、状态与上下文，还是部署与运行，然后只改最可能的那一层。

### 2. 优先看中间输出，不只看最终答案

最终答案只是症状，不一定是根因。  
如果可以，请优先检查：

- 用户输入是否被正确解析
- 检索 query 是否被改写偏移
- 命中的片段是否真的支持当前问题
- 推理链条是否在中途改变方向
- 中间状态是否被覆盖、丢失或误用
- 部署后的配置是否与本地实验一致

### 3. 每次只做最小修复

如果一次修改太多项，很难确认真正起作用的是哪一个改动。  
更稳妥的方式是每轮只改一处，然后重新运行并对比结果。

### 4. 把“看起来像模型问题”的情况，先当作流程问题来排查

很多异常并不是模型能力不足，而是检索、拼接、状态继承、工具调用顺序或部署配置出了问题。  
先把流程层排查干净，再判断是否需要更换模型或提高模型能力。

## 三、四层结构化排查方法

---

## 1. 输入与检索

这一层关注的是：问题有没有被正确理解，检索有没有拿到真正相关的上下文。

### 常见症状

- 命中的片段与问题主题弱相关
- 检索结果包含关键词，但语义重心不对
- 命中了很多内容，但真正有用的信息很少
- 问题问的是 A，检索结果大多在讲 B
- 用户表达较复杂时，检索 query 被改写得过于宽泛或过于狭窄
- 检索片段表面正确，但关键约束条件缺失

### 常见原因

- 用户问题没有被正确拆解
- query rewrite 发生偏移
- chunk 切分过粗或过细
- 召回策略把相似但无关的片段也带了进来
- top-k 设置不合适
- embedding 空间能找相似文本，但找不到真正支持答案的证据
- 索引内容本身不完整、过期或未正确更新

### 建议检查项

- 原始用户输入是什么
- 工作流中间是否对 query 做了改写
- 改写后的 query 是否保留了原问题的关键约束
- 最终命中的片段是否真的回答了用户问题
- 片段中是否缺少时间、对象、范围、条件等关键限定信息
- 是否存在高相似但低支持性的片段
- 如果减少或增加 top-k，结果是否明显变化
- 索引是否和当前数据版本一致

### 修复建议

- 优先修正 query 的表达与约束保留
- 检查 chunk 策略是否适合当前文档类型
- 调整召回数量，而不是盲目增大上下文
- 明确区分“相似内容”和“可作为证据的内容”
- 必要时为复杂问题补充更稳定的检索前置步骤，例如先拆解问题，再分别检索

---

## 2. 推理与规划

这一层关注的是：拿到上下文以后，系统是否沿着正确的方向组织解释、做步骤规划、输出答案。

### 常见症状

- 检索片段看起来没问题，但答案仍然明显错误
- 回答用了命中的材料，却解释出了完全不同的结论
- 多步流程前几步正常，后几步逐渐偏离原问题
- 系统在中途开始回答另一个更容易的问题
- 输出语气很肯定，但论证链条很弱
- 中间节点都有内容，但节点之间缺少有效衔接

### 常见原因

- 推理链条过长，后段发生语义漂移
- 规划步骤太松散，缺少关键约束
- 节点之间的输入输出契约不清晰
- 模型更倾向于“补全一个看起来合理的答案”，而不是忠实根据上下文推导
- 提示词要求结论，但没有要求校验证据
- 多步链路中存在局部正确、整体错误的累计偏差

### 建议检查项

- 最终答案是否能逐段回溯到对应的检索片段
- 中间节点输出是否仍围绕原问题
- 多步工作流中哪一步开始偏离
- 节点之间传递的是结论、摘要，还是原始证据
- 提示词是否要求模型给出依据、限制和不确定性
- 是否出现“回答很像对，但没有真正被证据支撑”的情况

### 修复建议

- 缩短不必要的推理链路
- 在关键节点增加“是否仍然回答原问题”的检查
- 明确要求模型引用或对齐证据，而不是只产出自然语言结论
- 让中间节点保留更多结构化信息，减少过度摘要
- 对高风险步骤增加最小验证，例如重新比对问题、证据与结论是否一致

---

## 3. 状态与上下文

这一层关注的是：工作流执行过程中，会话历史、变量、缓存、中间结果、上下文窗口是否被正确保存和继承。

### 常见症状

- 相同用户在相邻轮次中得到互相矛盾的回答
- 系统前面记住了条件，后面突然忘了
- 某一步已经得到的重要信息，在后续节点中消失
- 节点输出看起来正常，但下一节点接收到的却不是预期内容
- 长流程中前半段信息被后半段覆盖
- 本该保持一致的状态变量，在分支或循环后发生异常

### 常见原因

- 历史上下文没有正确注入
- 中间状态被覆盖或截断
- 节点之间的数据格式不一致
- 过长上下文导致关键条件被稀释
- 分支逻辑中状态继承规则不清晰
- 缓存命中或复用逻辑与当前任务不匹配

### 建议检查项

- 当前轮次真正传入了哪些历史信息
- 哪些字段是节点输出，哪些字段是状态缓存
- 状态变量在每一步之后如何变化
- 分支合流时，是否丢失了某些关键字段
- 中间结果是原始内容、压缩摘要，还是只剩结论
- 长上下文中用户的核心约束是否还可见

### 修复建议

- 明确每个节点的输入字段与输出字段
- 减少不必要的状态复用
- 让关键变量保持结构化，不要只保留自然语言摘要
- 对长流程增加阶段性重述，重新显式写入关键约束
- 检查缓存策略，避免错误复用旧结果
- 对分支和循环单独验证状态继承是否稳定

---

## 4. 部署与运行

这一层关注的是：本地实验、测试环境、生产环境之间是否存在依赖、配置、索引、启动顺序或运行状态的不一致。

### 常见症状

- 本地运行正常，部署后结果异常
- 首次调用失败，但重试后偶尔恢复
- 配置文件看起来一致，行为却不同
- 检索系统可用，但索引结果与预期不一致
- 工作流结构没改，但上线后输出质量下降
- 节点、模型、服务或依赖在不同环境中版本不一致

### 常见原因

- 初始化顺序错误
- 索引尚未准备完成就开始调用
- 配置文件、环境变量、模型名、路径或权限不一致
- 本地与线上依赖版本不同
- 缓存、服务状态或外部资源在新环境中不可用
- 部署脚本执行成功，但部分资源并未真正就绪

### 建议检查项

- 当前环境使用的配置是否与本地一致
- 模型、索引、向量库、reranker、服务地址是否一致
- 新环境是否完成了所有初始化步骤
- 首次调用前，依赖资源是否已经 ready
- 是否存在“表面部署完成，实际资源未就绪”的情况
- 日志中是否能定位到第一处失败环节

### 修复建议

- 优先检查初始化顺序与依赖就绪状态
- 把关键资源的 ready 状态显式化，不要依赖隐式成功
- 核对模型名、路径、环境变量、索引版本、服务端点
- 在部署后先做最小验收，再放大到完整流程
- 对首次运行异常单独建一个最小复现实验

## 四、从案例分析到最小修复的建议流程

如果你已经在 UltraRAG 的案例分析或中间输出追踪中发现某个回答可疑，可以按下面顺序操作：

### 第一步：锁定可疑样本

先选一个最能稳定复现问题的输入，不要一次看太多案例。  
目标是找到一个可以重复触发异常的样本。

### 第二步：观察中间步骤

按工作流顺序查看：

- 输入是否被正确解析
- query 是否发生偏移
- 检索片段是否真的支持问题
- 中间节点是否逐步偏离原始目标
- 状态变量是否被覆盖或丢失
- 最终答案是否还能回溯到证据

### 第三步：给问题归层

把当前异常优先归到以下四类之一：

- 输入与检索
- 推理与规划
- 状态与上下文
- 部署与运行

如果一时无法完全确定，就先归到“最早开始出现异常的那一层”。

### 第四步：只做一个最小修复

例如：

- 修正 query rewrite
- 调整 top-k
- 修改一个关键节点的提示词
- 明确一个状态字段的传递规则
- 修正一个部署配置或初始化顺序

每次只改一处，然后重新运行。

### 第五步：重新对比结果

对比修复前后的中间输出与最终答案：

- 错误是否提前消失
- 偏移是否减弱
- 证据与答案的一致性是否提高
- 是否引入了新的副作用

如果问题仍在，再继续下一轮最小修复。

## 五、快速排查清单

当你时间有限时，可以先用下面这份简版清单：

### 输入与检索
- 问题有没有被错误改写
- 检索到的片段是否真的支持答案
- 召回数量是否过多或过少
- 索引内容是否完整且最新

### 推理与规划
- 答案能否回溯到证据
- 中间节点是否偏离原问题
- 是否存在“语气自信但依据不足”的情况
- 多步流程中哪一步开始漂移

### 状态与上下文
- 历史上下文是否被正确注入
- 关键变量是否被覆盖或截断
- 分支与循环后的状态是否一致
- 中间结果是否只剩结论、缺少证据

### 部署与运行
- 本地与线上配置是否一致
- 初始化顺序是否正确
- 首次运行前依赖资源是否 ready
- 模型、索引、服务和版本是否一致

## 六、边界说明

本文档是一份面向调试与案例分析的实践性排障指南。  
它的目标不是替代实验设计、评测体系或正式监控工具，而是在问题已经出现时，帮助使用者更快地判断问题层级，并优先采取最小修复。

对于复杂问题，四个层级之间可能会相互影响。  
例如，检索偏差可能进一步诱发推理偏差，状态管理异常也可能放大部署后的不一致行为。  
因此，建议始终优先定位“最早出现异常”的层级，而不是只关注最终答案。

## 七、致谢

致谢：本文档的结构化排障思路参考了社区贡献者 @onestardao 的相关实践。
