分享一个rag的线上事故

分享一个rag的线上事故
分享一个 RAG 的线上事故工具文档抢答维修方向答错了这是一次智能马桶维修 Agent 的真实复盘。问题不在模型“不会回答”而在于召回内容来自不同知识源却没有在进入 Prompt 前明确优先级和适用范围。事故现象一位维修师傅处理智能马桶盖板时把固定螺丝的操作方向弄反导致螺丝滑丝并引发投诉。最初我们怀疑是盖板维修文档写错了。回查原始资料后发现产品维修手册对这个盖板固定螺丝的说明是正确的真正的问题出在 RAG 的多路检索与答案生成环节。用户询问的是“智能马桶盖板的螺丝怎么处理”。系统同时召回了两类资料知识源内容实际适用范围盖板维修手册盖板固定螺丝的安装/拆卸步骤与方向指定产品、指定盖板组件、指定操作视角螺丝刀工具手册螺丝刀的通用操作说明通用工具使用不描述该产品螺丝的工艺步骤两段内容都出现了“顺时针”这个词。前者是产品零件的维修工艺后者只是工具的一般操作表述。Agent 把两段材料平铺注入上下文后没有识别它们的对象和适用范围不同最终将工具说明误当成了该螺丝的维修依据输出了有歧义的操作指令。这类问题的危险点在于表面上看召回“没有错”真正出错的是模型在多个来源之间做了不该做的自由拼接。排查过程先看答案再看证据链事故发生后不应该只检查最终答案。我们沿着一次请求的证据链回溯用户问题 → Query Rewrite / Multi-Query → 产品维修手册、工具手册等多路召回 → 融合、重排 → 注入 Prompt → Agent 生成维修步骤重点记录每个候选 Chunk 的{chunk_id:cover_manual_5_2,document_type:product_repair_manual,product_id:STC-1000,component:盖板,knowledge_domain:维修工艺,source_rank:1,score:0.91}同一轮里工具手册也会以类似结构返回只是它的document_type是tool_manual、knowledge_domain是工具使用并且通常没有具体产品与组件约束。复盘时我们发现检索阶段已经能找到正确的盖板维修手册但 Prompt 里只按相关性分数拼接了文本没有将“它是哪个产品的哪个组件、能回答什么问题”一并告诉模型。工具手册因此获得了与产品维修手册近似的发言权。根因相关性不等于可执行性RAG 常见的融合链路是向量检索、BM25、RRF 融合、CrossEncoder 重排。它们主要回答的是这段文本和用户问题是否相关但维修类问题还必须回答另一件事这段文本能否作为当前产品、当前部件、当前工序的操作依据工具手册对“螺丝刀”“顺时针”当然相关但它并不等于某型号盖板固定螺丝的工艺规范。把两者混成同一层证据会让模型把通用说明补全成具体动作风险会直接落到现场操作上。修复在 Prompt 注入前做知识源治理这次修复没有依赖“换一个更大的模型”而是在 RAG 结果进入 Prompt 前补上优先级与适用范围。1. 先按 product_id 过滤产品维修类问题从请求开始就带着product_id。第一次召回与二次召回都先限定为当前产品范围避免混入其他型号的维修规范。检索过滤product_id STC-1000注意这里的核心并不是在二次召回后再排除其他产品而是在整个检索链路的入口就固定产品范围。2. 再做组件与知识域适用性过滤问题目标是“盖板固定螺丝”优先保留component盖板且knowledge_domain维修工艺的 Chunk。工具文档不是全部丢弃而是只在用户明确问“螺丝刀怎么使用”“用什么工具”时作为辅助证据。问题盖板螺丝如何拆装 优先知识域产品维修工艺 可辅助知识域工具使用、安全规范 不作为方向依据通用工具操作说明3. 明确知识源优先级同一问题下来源优先级要变成结构化规则而不是让模型自行猜测产品专属维修手册 产品安装说明书 配件手册 工具使用手册 通用检修流程 / 行业规范优先级不是全局真理。它应随问题类型变化问“工具型号或操作规范”时工具手册自然应升到前面问“某型号盖板螺丝的拆装方向”时产品维修手册必须拥有最高优先级。4. 用结构化上下文约束 Agent不要只把 Chunk 原文堆到 Prompt 里。建议按“主依据”和“辅助资料”分区并把适用范围写清楚【主维修依据优先遵循】 来源STC-1000 盖板维修手册第 5.2 节 适用范围STC-1000盖板组件固定螺丝 用途确定拆装顺序、方向、扭矩和注意事项 【辅助资料不得覆盖主维修依据】 来源螺丝刀工具使用手册 适用范围通用工具 用途仅回答工具选择、安全操作和握持方式 回答规则 1. 对具体零件的方向、顺序、扭矩等操作必须以主维修依据为准。 2. 辅助资料与主维修依据的对象或范围不一致时不得用于推导具体维修动作。 3. 主维修依据不足时明确说明缺失信息不得用通用工具说明补造产品工艺。 4. 输出时标注引用的文档与章节。这段规则的作用不是让 LLM 背诵优先级而是限制它对证据的使用方式通用文档可以补“怎么选螺丝刀”不能覆盖“这个螺丝该怎样拆装”。二次检索也要回到同一套规则维修手册经常有跨章节引用例如如果按压泄阀按钮被卡住请检查章节 2 的杠杆方向是否正确。第一次召回可能拿到了故障现象和当前操作步骤但缺少“章节 2”的杠杆说明。此时可以让模型做上下文完整性评估生成补充 Query再进行二次召回。首次召回 → RRF CrossEncoder → 检查是否缺少被引用章节或前置步骤 → 生成补充 Query → 仍以 product_id 作为检索入口过滤 → 与首次候选按 chunk_id 合并去重 → 按文档类型、组件、知识域过滤 → 再次重排 → 按来源优先级注入 Prompt二次召回不能直接拼入上下文。即使已经在入口用product_id限定了同一产品它仍可能召回词面相近、但属于不同组件或知识域的说明也可能与首次结果的工艺步骤不一致。因此仍需要去重、适用性过滤和重排。最终回答增加“可追溯性”高风险维修指令不应该只返回一段自然语言。接口可以把答案与其主依据一并返回{answer:请按 STC-1000 盖板维修手册第 5.2 节执行固定螺丝操作不要以通用螺丝刀说明替代该部件的方向与顺序。,references:[{chunk_id:cover_manual_5_2,document_type:product_repair_manual,chapter:5.2 盖板固定,priority:primary}],supporting_references:[{chunk_id:screwdriver_manual_1_3,document_type:tool_manual,priority:supporting}]}这样现场人员、质检和研发都能看见答案究竟依据哪一本手册、哪一节内容生成一旦出现争议也能快速定位是文档问题、召回问题还是生成阶段的问题。复盘结论这次事故让我重新确认了一点在设备维修 RAG 中召回 TopK 不是“资料越多越好”。真正需要治理的是证据的边界相关的内容不一定能指导当前维修动作产品专属工艺不能被通用工具说明覆盖多路召回和二次召回都要保留产品、组件、知识域与文档类型Prompt 注入必须把主依据、辅助资料和禁止覆盖规则表达出来最终答案必须带上可追溯的文档与章节引用。对高风险操作而言RAG 不是把资料“找出来”就结束了系统还要明确告诉模型哪段资料可以回答什么、哪段资料只能作为辅助、冲突时究竟该听谁的。