RAG 答错以后:从一次请求走到故障分支
直接结论:先找到第一处证据断裂,再改对应阶段。没有原文、候选和最终上下文的记录,单看答案无法判断是入库、召回、上下文裁剪还是生成出错;换模型通常不能解释问题。
适用 Java 21、Spring AI 2.0.x 的知识库服务;2026-11-06 核对的 Spring AI 官方参考 显示版本为 2.0.2,滚动文档后续可能变化,使用时应对照项目实际依赖。下面的排查顺序是应用层建议;文中原有本地验证仅运行固定记录分类器,未连接真实向量库或模型,本次文字修订未重新运行分类器。它处理一条失败请求,与 评估指南 的题集统计不同。
1. 保存一份足够定位的请求快照
接到“制度明明写了,机器人却回答不知道”,先保留请求号、脱敏问题、服务端租户与权限版本、文档版本、切分和嵌入模型标识,以及检索条件、候选片段 ID、最终送模片段 ID、提示词版本和引用。正文只在受控测试环境抽查,普通日志避免保存客户原文和令牌。
先人工确认问题在当前授权文档里有答案,标出参考片段;若事实只在旧版本或未授权文档里,正确行为可能就是拒答。Spring AI 可观测性 提供客户端、模型、嵌入与向量库的观测能力;业务还需关联片段与版本,不能把一条总耗时当作整条证据链。
2. 按第一处失败执行排查树
从上向下检查,命中就停止并补证据;修复后从头回放同一请求:
请求身份与文档授权有效?否 -> DENY:先阻断读取,复查授权和缓存键
是 -> 参考片段已落库且解析文本完整?否 -> INGEST:查页码、表格、OCR、分块边界
是 -> 查询/文档编码协议正确,向量空间与索引兼容?否 -> EMBEDDING:修正对应编码,按兼容性决定是否重建索引
是 -> 参考片段在候选里?否 -> RETRIEVE:查过滤、查询改写、阈值、Top-K、索引
是 -> 参考片段进入最终上下文?否 -> ASSEMBLY:查重排、去重、token 裁剪
是 -> 上下文版本仍有效?否 -> VERSION:查版本筛选与缓存
是 -> 回答事实逐项有本次证据支撑?否 -> GENERATE:查提示词、历史与引用绑定
是 -> PASS:仅表示本条人工检查通过查入库时,按参考片段 ID 读取存储正文,对照原始页,不以“上传成功”代替落库核对。查召回时,在隔离环境保持授权条件不变,先固定原始问题,再分别比较改写前后、阈值前后和重排前后的 ID 列表。一次只变一项;不能通过去掉租户过滤换取命中。
同样是“没有答出参考事实”,证据消失的位置决定了该改哪一层。以下对照假定身份、授权、入库与编码检查已通过,属于排查建议,不是新增实测结果:
| 参考片段的位置 | 优先解释 | 调整时的取舍 |
|---|---|---|
| 不在召回候选中 | 检索条件排除了它,或相似检索未将它选入 | 降低阈值、增大 Top-K 可能带回参考片段,也会带回更多噪声;先核对过滤条件与候选 ID |
| 在候选中,不在最终上下文中 | 重排、去重或预算裁剪移除了它 | 增大 Top-K 不保证能送模;应核对后处理前后内容及上下文预算 |
| 在最终上下文中,但回答缺乏依据 | 生成阶段未正确使用证据,或引用未绑定事实 | 先核对送模正文与逐项引用,再调整提示词;仅有片段 ID 不能证明事实完整保留 |
官方检索接口 将过滤、阈值与 Top-K 作为检索条件;模块化 RAG 文档 则把重排、去冗余和压缩放在检索后的处理阶段。因此,“多取一些”与“最终保留关键证据”是两个不同的检查点。
查询侧与文档侧的预处理不一定完全相同,应分别符合模型协议并进入相互兼容的向量空间。例如 multilingual-e5 官方模型卡 要求分别使用 query: 与 passage: 前缀。仅查询侧格式错误时先修正查询;模型、维度或文档编码改变导致已有入库向量不兼容时,再重建隔离索引并回放题集。
查组装时,要看最终请求,不能只看检索日志。长会话、系统提示与工具描述也占窗口,重排命中不代表送模保留。Spring AI 的 模块化 RAG 区分检索、后处理与上下文增强,适合在这些边界记录输入输出;并非每个 Advisor 的空上下文策略都相同。
3. 用本地记录跑通分支
下载 分类器 与 组装故障记录,保持 traces 目录。记录如下,全部是合成教学数据,不是用户服务成绩;布尔值应由人工核对真实记录后填写,不能让模型自评后直接采信。
caseId=fixture-assembly
authorized=true
chunkStored=true
rawTextOk=true
embeddingCompatible=true
referenceInCandidates=true
referenceInContext=false
contextCurrent=true
answerGrounded=truejavac -encoding UTF-8 --release 21 RagFailureProbe.java
java -cp . RagFailureProbe traces/assembly.properties本机 JDK 21.0.12 实际输出 case=fixture-assembly branch=ASSEMBLY。完整样例另记候选 fixture-c7,fixture-c9、上下文 fixture-c9,所以先查裁剪或后处理,不能直接归因为模型不懂。后续的 answerGrounded=true 只是独立开关占位,前一关失败后没有被用来判定通过。
4. 遇到两种故障同时出现怎么办
排查树输出的是当前最先阻断的一关,不是穷尽根因。扫描件漏字与错误过滤可以同时存在;修好解析后仍需继续检查召回,不能把第一次分支当作整张工单的终局。嵌入维度相同也不代表兼容,至少记录模型标识、版本和归一化方式;切换模型后混用旧向量,可能没有报错却明显漏检。
只有回答表述不同时,先逐条核对事实与引用,再判断是否真的失败。反过来,答案碰巧正确也不能证明证据链健康。若记录缺字段,分类器会直接报输入错误;真实排障也应补采记录,而不是把未知值填成通过。增加日志后需在同一身份与版本下重放,避免复现期间权限或资料变化掩盖原问题。
5. 修复记录要能回答什么
每次工单写“首个失败阶段、对应片段与版本、改动、原请求重放结果、仍未覆盖的边界”。例如教学记录可写:“参考 c7 在候选、未在上下文;待查裁剪;修复结果未运行”,不要提前填写提升比例。确认越权时先停止返回相关证据。知识库问答在空上下文时应说明缺少证据;官方空上下文策略 中,ContextualQueryAugmenter 默认通过提示词要求模型不回答,allowEmptyContext(true) 则允许继续生成。这个开关不会修复召回,也不等于程序强制拒答;需要确定返回缺证据说明的服务,应在应用层检查空结果并直接返回。
VectorStore 官方参考 用于核对过滤与检索接口;相似度尺度和近似索引行为仍需按具体实现检查。分类器不验证权限、不计算向量相似度,也不会自动判断幻觉;八份固定样例的 实测记录 只验证分支逻辑。记录自己的完整验收时使用 RAG 验收手册,主资料入口为 Java AI 资料。
