From 7f2e47ca386a2948d03384baa839a82d48bbd76b Mon Sep 17 00:00:00 2001 From: zhuyongxin Date: Tue, 7 Jul 2026 18:51:47 +0800 Subject: [PATCH] docs(mvp): record executor evidence loop design --- mvp/issues/README.md | 2 + ...utor-evidence-attribution-hallucination.md | 133 ++++++ ...executor-self-evidence-loop-design-note.md | 443 ++++++++++++++++++ 3 files changed, 578 insertions(+) create mode 100644 mvp/issues/executor-evidence-attribution-hallucination.md create mode 100644 mvp/issues/executor-self-evidence-loop-design-note.md diff --git a/mvp/issues/README.md b/mvp/issues/README.md index f3bde02..6090f40 100644 --- a/mvp/issues/README.md +++ b/mvp/issues/README.md @@ -8,6 +8,8 @@ | ISS-004 | Executor 域级检索水位控制(Phase 2) | 低 | 待规划 | [ISS-004-executor-domain-hard-limit.md](ISS-004-executor-domain-hard-limit.md) | | ISS-005 | 证据链补齐与降级契约收敛 | 高 | 已归档 | [ISS-005-evidence-trace-hardening.md](ISS-005-evidence-trace-hardening.md) | | ISS-006 | 固定诊断评测集与回归 Harness | 高 | 已归档 | [ISS-006-diagnosis-eval-harness.md](ISS-006-diagnosis-eval-harness.md) | +| executor-evidence-attribution-hallucination | Executor 证据归因幻觉 | 高 | 待规划 | [executor-evidence-attribution-hallucination.md](executor-evidence-attribution-hallucination.md) | +| executor-self-evidence-loop-design-note | Executor 自证循环与证据摘要链路设计记录 | 高 | 已形成方向 | [executor-self-evidence-loop-design-note.md](executor-self-evidence-loop-design-note.md) | | expand-diagnosis-eval-fixtures | 补齐固定诊断评测 fixture 与 baseline | 中 | 已归档 | [expand-diagnosis-eval-fixtures.md](expand-diagnosis-eval-fixtures.md) | | diagnosis-eval-baseline-diff | 诊断评测 baseline diff 与回归判断 | 中 | 已归档 | [diagnosis-eval-baseline-diff.md](diagnosis-eval-baseline-diff.md) | | mvp-demo-interview-runbook | Plan C 面试可复现 Demo 包 | 中 | 已归档 | [mvp-demo-interview-runbook.md](mvp-demo-interview-runbook.md) | diff --git a/mvp/issues/executor-evidence-attribution-hallucination.md b/mvp/issues/executor-evidence-attribution-hallucination.md new file mode 100644 index 0000000..e904c3a --- /dev/null +++ b/mvp/issues/executor-evidence-attribution-hallucination.md @@ -0,0 +1,133 @@ +# Executor 证据归因幻觉 + +**状态**:待规划 +**严重程度**:高 +**发现时间**:2026-07-07 +**来源**:Trace Workbench 复核 / 最近 Chat 诊断会话低置信分析 +**关联**:ISS-005(证据链补齐与降级契约收敛)、ISS-006(固定诊断评测集与回归 Harness)、`chat-verifier-agent`、`diagnosis-playbook-skills` + +--- + +## 背景 + +最近连续多条 Chat 诊断会话都被 Verifier 判定为 `LOW_CONFID`。这些会话并不是没有调用工具;相反,它们大多完成了 `lookup_knowledge`、`query_metrics`、`query_logs` 等证据工具调用。 + +问题出在 Executor 的最终答案:它拿到部分真实工具返回后,将以下三类内容混在一起输出为“本次事故结论”: + +1. 本次工具直接返回的事实。 +2. runbook、知识库或历史案例里的通用模式。 +3. 模型基于经验补全的推断。 + +Verifier 随后逐条核查 `executor_final_answer`,发现大量关键事实没有对应工具证据,于是按规则输出 `LOW_CONFID`。 + +--- + +## 现象 + +最近 5 条 Chat 诊断会话均为低置信: + +| session | verdict | groundedness_score | direct_evidence | indirect_support | no_evidence | +|---|---:|---:|---:|---:|---:| +| `e2e-mysql-skill-rag-20260706-2355` | LOW_CONFID | 0.50 | 2 | 14 | 5 | +| `e2e-mysql-skill-rag-20260706-2312` | LOW_CONFID | 0.37 | 4 | 11 | 14 | +| `e2e-mysql-skill-rag-20260706-2255` | LOW_CONFID | 0.10 | 1 | 2 | 18 | +| `e2e-mysql-skill-rag-20260706-2242` | LOW_CONFID | 0.53 | 3 | 9 | 4 | +| `e2e-mysql-skill-rag-20260706-2230` | LOW_CONFID | 0.20 | 1 | 2 | 11 | + +典型无证据断言: + +- `order-service 出现 OutOfMemoryError: Java heap space` +- `OrderService.processLargeOrder() 内存泄漏导致 JVM OOM` +- `频繁 Full GC(10 分钟 15 次,平均 850ms)` +- `users + user_profiles 慢查询 2.8s,全表扫描` +- `UPDATE orders 锁等待 2.1s` +- `user-service DB 查询超时 5.2s~5.8s` + +这些事实要么没有出现在工具返回中,要么属于其它服务或历史案例,不能作为当前会话的直接事实。 + +--- + +## 根因判断 + +这是一个经典的 Agent 幻觉问题,但更准确地说是: + +**Executor 的证据归因幻觉。** + +Executor 已经调用了工具,但在综合答案阶段没有严格区分: + +- `direct evidence`:工具本次实际返回的事实 +- `reference / runbook`:流程指导、历史模式、建议方向 +- `hypothesis`:基于已知证据的推测 +- `missing evidence`:还没有被工具证实的内容 + +因此它会把“可能相关的历史模式”写成“当前事故事实”,并把“建议排查方向”写成“已确认根因”。 + +--- + +## 影响 + +- Verifier 正确拦截后,最近 Chat 会话长期停留在 `LOW_CONFID`。 +- 用户侧结果虽然带免责声明,但仍难以直观看出哪些内容是真实证据、哪些只是推测。 +- 评测里 verdict 分布会被 Executor 输出质量拖低,掩盖工具链本身是否已经足够。 +- 面试讲解时容易被追问:既然工具都调用了,为什么答案仍然不可信? + +--- + +## 本 issue 目标 + +收紧 Executor 的最终输出契约,避免把未证实内容写成确认结论。 + +完成后应做到: + +1. Executor 最终答案明确分区: + - `已证实事实` + - `合理推测` + - `证据缺口` + - `建议动作` +2. `已证实事实` 只能来自本轮 evidence tools 的返回。 +3. runbook / skill / 知识库中的流程和历史案例不得直接作为本次事故事实。 +4. 其它服务的证据不得迁移为当前服务事实。 +5. 根因结论必须绑定至少一条直接或间接证据;否则只能放入 `合理推测` 或 `证据缺口`。 +6. Verifier 的 `LOW_CONFID` 缺口应能直接映射回 Executor 的分区错误。 + +--- + +## 范围 + +### In scope + +- 修改 `chat-executor-prompt.md`,加入证据分区和证据归因规则。 +- 必要时在 `ChatService.buildChatExecutorAgent(...)` 中注入更明确的最终答案格式约束。 +- 增加 focused tests,覆盖 Executor prompt 中的证据边界规则。 +- 增加或更新诊断 eval fixture,验证 unsupported claims 不会出现在确认结论中。 +- Trace Workbench 可继续消费 Verifier 缺口展示低置信原因。 + +### Out of scope + +- 不放宽 Verifier 的 PASS 判定矩阵。 +- 不通过调高 `verifier.low-confidence-threshold` 掩盖问题。 +- 不把 runbook 或 skill 内容写入 `tool_invocation` 伪装成事实证据。 +- 不新增数据库 schema。 + +--- + +## 验收标准 + +- 对 MySQL 连接池耗尽类 case,Executor 输出中: + - 已证实事实只包含工具返回的服务、指标、日志、慢 SQL 等事实。 + - OOM、Full GC、连接泄漏等未证实内容只能出现在推测或证据缺口中。 + - 不再把 `order-service` / `user-service` 事实混入 `payment-service` 当前事故。 +- Verifier 对同类会话的 `no_evidence` 数量明显下降。 +- 若证据不足,最终用户输出明确说明“当前无法确认根因”,而不是补全一个完整事故故事。 +- 评测报告能捕捉 unsupported confirmed claims 的回归。 + +--- + +## 相关文件 + +- `src/main/resources/prompts/chat-executor-prompt.md` +- `src/main/resources/prompts/chat-verifier-prompt.md` +- `src/main/java/com/superbiz/agent/service/ChatService.java` +- `src/main/java/com/superbiz/agent/service/ToolTraceSummaryService.java` +- `src/test/java/com/superbiz/agent/service/ChatServiceSequentialAgentTest.java` +- `mvp/eval/` diff --git a/mvp/issues/executor-self-evidence-loop-design-note.md b/mvp/issues/executor-self-evidence-loop-design-note.md new file mode 100644 index 0000000..2f0bb9a --- /dev/null +++ b/mvp/issues/executor-self-evidence-loop-design-note.md @@ -0,0 +1,443 @@ +# Executor 自证循环与证据摘要链路设计记录 + +**状态**:已形成方向,待创建 OpenSpec change +**严重程度**:高 +**记录时间**:2026-07-07 +**来源**:近期 Chat 诊断 LOW_CONFID 排查、Executor 幻觉问题复盘、与 `interview-guide` 类似评估链路对比 +**关联**:`executor-evidence-attribution-hallucination`、`chat-verifier-agent`、`evidence-trace-hardening` + +--- + +## 背景 + +近期诊断会话中,Executor 已经成功调用了 `lookup_knowledge`、`query_logs`、`query_metrics` 等证据工具,但最终仍频繁被 Verifier 判定为 `LOW_CONFID`。 + +复核工具调用和 Executor 输出后发现,问题不是“完全没有证据”,而是 Executor 在拿到部分工具返回后,会把以下内容混在一起: + +- 工具本次真实返回的事实; +- runbook、RAG、历史案例里的通用模式; +- 模型基于经验补全的诊断故事; +- 其它服务的告警和日志; +- 尚未被当前工具证明的具体错误、数值或因果关系。 + +这种行为导致 Executor 的最终答案看起来完整,但其中一部分关键事实并没有被当前会话证据支撑。Verifier 正确拦截后,系统长期停留在 `LOW_CONFID`。 + +--- + +## 关键发现 + +### 1. LOW_CONFID 不是因为 Verifier 只看 RAG + +实际 `verifier_evaluation.facts_checked[].evidence_refs` 中已经出现了 `query_logs` 和 `query_metrics`。 + +例如,`payment-service CPU 92%` 可以被 Verifier 识别为来自: + +- `query_metrics` +- `query_logs` + +因此问题不是 Verifier 完全忽略非 RAG 工具。 + +真正的问题是: + +- Executor 的证据绑定不稳定; +- 工具原始输出、摘要、Executor claim、Verifier 校验之间没有一条稳定的数据契约; +- Executor 仍然可能把未证明内容写成 confirmed claim。 + +### 2. 工具返回目前不是统一格式 + +现有证据型工具返回结构不同: + +- `query_logs` 的证据主体在 `logs[].message` +- `query_metrics` 的证据主体在 `alerts[].description` +- `lookup_knowledge` 的证据主体在 `evidenceBlocks[].content` / `contextPack.packedText` + +同时模型看到的工具名和持久化工具名也不完全一致: + +- 模型工具名:`queryLogs`、`queryPrometheusAlerts`、`lookupKnowledge` +- 持久化工具名:`query_logs`、`query_metrics`、`lookup_knowledge` + +这让 Executor 自己生成 `tool_name`、`source_invocation_ids`、`source_id` 时很容易漂移。 + +### 3. 规则抽取 `text` 不足以解决问题 + +曾讨论过由后端将不同工具返回统一抽成: + +```json +{ + "id": "tool:394:0", + "tool": "query_logs", + "invocation_id": 394, + "domain": "system-metrics", + "text": "CPU使用率过高: 92.0%, 线程数: 245" +} +``` + +这个方向能解决“证据可引用”的一部分问题,但有明显风险: + +- 不同工具返回形态不同,适配器会持续膨胀; +- 长日志、堆栈、RAG 长文本很难靠固定规则安全缩短; +- 规则生成的 `text` 可能丢掉否定、上下文或条件; +- 一旦 `text` 被当成事实来源,它本身也可能成为幻觉入口。 + +因此,仅靠“工具适配器 + 规则 text”不是最终方案。 + +--- + +## 设计转向 + +我们最终认可的方向是: + +**LLM 做语义压缩,代码做边界控制;不要让 Executor 同时总结证据和使用证据下结论。** + +这意味着系统不再追求“所有工具由规则抽成完美 evidence block”,而是引入独立的证据摘要阶段。 + +--- + +## Executor 自证循环 + +当前危险链路可以描述为: + +```text +Executor 调工具 + -> Executor 阅读工具输出 + -> Executor 自己总结工具输出 + -> Executor 自己生成 claims + -> Executor 自己给 claims 绑定 evidence + -> Verifier 事后校验 +``` + +这个流程的风险是:如果 Executor 总结错了,它可能继续基于错误摘要生成 claim,形成“自己造证据,再用自己造的证据证明自己”的闭环。 + +要拆掉这个闭环,需要把“证据摘要”和“诊断推理”拆开。 + +--- + +## 目标链路 + +推荐链路如下: + +```text +Planner + -> Collector Agent + 只调用工具,收集证据 + -> Evidence Digest Chain + 只总结工具输出,不做诊断 + -> Diagnosis Executor Chain + 只基于 evidence_digests 做诊断推理 + -> Verifier + 校验 digest 是否忠实 raw,claim 是否可合理推出 + -> Final Composer Chain + 基于 verifier 过滤后的内容生成最终用户答案 +``` + +这里并不要求所有阶段都实现为 Agent。 + +- Collector 需要调用工具,适合作为 Agent。 +- Evidence Digest 不调用工具,适合作为无状态 LLM chain。 +- Diagnosis Executor 可以先作为无工具 LLM chain。 +- Verifier 可以继续作为质量门。 +- Final Composer 不调用工具,只做最终表达,也适合作为 LLM chain。 + +--- + +## 各阶段职责 + +### Collector Agent + +职责: + +- 根据 Planner 计划调用工具; +- 调用 `lookup_knowledge`、`query_logs`、`query_metrics` 等证据工具; +- 不输出根因; +- 不输出修复建议; +- 不输出最终诊断结论。 + +输出: + +```json +{ + "collection_summary": "已完成知识库、日志、指标查询", + "tool_invocation_ids": [392, 393, 394, 395] +} +``` + +### Evidence Digest Chain + +职责: + +- 读取工具原始输出或持久化工具调用记录; +- 对工具结果做语义摘要; +- 每条摘要必须引用 `source_invocation_ids`; +- 不做因果判断; +- 不输出修复建议; +- 不把“可能原因”改写成“确定根因”。 + +输出: + +```json +{ + "digests": [ + { + "digest_id": "digest-1", + "source_invocation_ids": [394], + "tool": "query_logs", + "domain": "system-metrics", + "summary": "payment-service 的系统指标日志显示 CPU 使用率为 92.0%,线程数为 245。", + "source_quote": "CPU使用率过高: 92.0%, 进程: java (PID: 1), 线程数: 245" + } + ] +} +``` + +### Diagnosis Executor Chain + +职责: + +- 只读取用户问题、Planner 计划、`evidence_digests`; +- 不直接读取 raw 工具输出; +- 不调用工具; +- 基于 digest 做诊断推理; +- 区分直接观测、合理推导、假设和证据缺口。 + +输出: + +```json +{ + "claims": [ + { + "claim_id": "claim-1", + "claim_type": "observation", + "claim_text": "payment-service 当前存在 CPU 使用率过高现象。", + "evidence_refs": ["digest-1"] + }, + { + "claim_id": "claim-2", + "claim_type": "reasonable_inference", + "claim_text": "CPU 使用率过高可能是支付接口超时的重要因素。", + "evidence_refs": ["digest-1", "digest-knowledge-1"] + } + ], + "candidate_answer": "..." +} +``` + +### Verifier + +Verifier 不再只判断“是否存在直接证据”,而是校验两层: + +1. `digest` 是否忠实于 raw 工具输出; +2. `claim` 是否可由 digest / raw 合理推出。 + +推荐判定类型: + +- `direct_observation`:证据直接观测到; +- `reasonable_inference`:可以从证据合理推出; +- `overstated`:有部分依据,但结论说重了; +- `unsupported`:证据不足; +- `external_unknown`:引入了证据外的新实体、新数值、新错误; +- `contradicted`:与证据冲突。 + +输出示例: + +```json +{ + "digest_checks": [ + { + "digest_id": "digest-1", + "verification": "faithful" + } + ], + "claim_checks": [ + { + "claim_id": "claim-2", + "verification": "reasonable_inference", + "detail": "CPU 92% 是直接观测事实;将其作为超时可能因素属于合理推导,但仍需延迟和错误率证据确认因果链。" + } + ] +} +``` + +### Final Composer Chain + +代码不应负责写语义总结;代码只负责过滤输入。 + +Final Composer 的输入应由后端根据 Verifier 结果构造: + +```json +{ + "original_query": "支付接口最近出现超时...", + "confirmed_findings": [ + { + "text": "payment-service CPU 使用率为 92%,线程数 245", + "basis": "query_logs/query_metrics" + } + ], + "reasonable_inferences": [ + { + "text": "CPU 过高可能导致接口处理变慢", + "basis": "CPU 过高 + runbook 说明 CPU 飙高会伴随慢请求" + } + ], + "insufficient_evidence": [ + { + "text": "order-service OOM 是根因", + "reason": "当前证据未发现 OOM 日志" + } + ], + "allowed_actions": [ + "排查 payment-service CPU 热点线程", + "补充查询接口 P95/P99 延迟、错误率和下游依赖耗时" + ] +} +``` + +Composer 只允许基于上述字段生成用户答案,不允许引入新的服务名、指标值、错误码或根因。 + +--- + +## 是否需要改成 SupervisorAgent + +当前结论:**暂时不需要。** + +原因: + +- 当前目标是固定 pipeline 的职责隔离; +- SupervisorAgent 主要解决动态路由和调度问题; +- 过早引入 Supervisor 可能增加不确定执行顺序,重新引入 Verifier 过早执行等问题。 + +推荐先保留明确编排: + +```text +planner.call() +collector.call() +evidenceDigestService.digest(...) +diagnosisExecutorChain.call(...) +verifier.call(...) +finalComposerChain.call(...) +``` + +当未来出现以下需求时,再考虑 Supervisor: + +- Verifier 要求补证据后自动回到 Collector; +- 不同诊断 skill 选择不同 worker; +- 需要多轮动态停止条件; +- 工具调用策略由 Supervisor 统一决策。 + +--- + +## 从 interview-guide 借鉴的设计 + +`Snailclimb/interview-guide` 不是多 Agent 编排,而是多阶段 LLM pipeline: + +```text +问答记录 + -> 分批评估 LLM + -> 结构化 BatchReportDTO + -> 代码合并批次结果 + -> 二次汇总 LLM + -> 结构化 SummaryDTO + -> 失败降级 + -> EvaluationReport +``` + +可借鉴点: + +1. LLM 做语义,代码做边界; +2. 中间结果必须结构化; +3. 先局部处理,再二次汇总; +4. 结构化输出调用应统一封装; +5. 每个 LLM 阶段都要有解析失败、格式错误和降级路径。 + +对应到本项目,建议新增类似 `StructuredLlmInvoker` 的基础组件,统一处理: + +- 严格 JSON prompt; +- LLM 调用; +- JSON 解析; +- schema 校验; +- 重试; +- parse status; +- fallback。 + +--- + +## 为什么最终方案比规则适配器更合适 + +规则适配器适合做工程边界: + +- 过滤非证据工具; +- 保留 invocation id; +- 切分长输出; +- 限长; +- 保存 trace。 + +但规则不适合做语义摘要: + +- 堆栈长短和重点不固定; +- RAG 长文本需要理解上下文; +- 告警描述里可能包含“可能原因”,不能被规则粗暴改写; +- 固定字段拼接无法保证给 Verifier 提供足够语义。 + +因此,规则层只做预处理,语义摘要交给独立 Evidence Digest Chain,且 Verifier 需要校验 digest 忠实性。 + +--- + +## 推荐实施路线 + +### Phase 1:在现有 Sequential 流程旁插入 Evidence Digest + +保持现有 `Planner -> Executor -> Verifier` 基本流程,先新增: + +- `EvidenceDigestService` +- `evidence_digests` 持久化或存入 `self_evaluation` +- Verifier 输入增加 `evidence_digests` +- Verifier 判定类型扩展为可推导性校验 + +目标:先解决“非 RAG 工具事实无法被合理推导识别”和“Verifier 只做硬证据匹配过窄”的问题。 + +### Phase 2:拆分 Executor + +将当前 Executor 拆为: + +- `CollectorAgent` +- `DiagnosisExecutorChain` + +DiagnosisExecutor 不再调用工具,不再直接看 raw 工具输出,只基于 Evidence Digest 做诊断。 + +目标:拆掉 Executor 自证循环。 + +### Phase 3:引入 Final Composer Chain + +不再直接将 Executor 原文作为最终答案。 + +后端根据 Verifier 结果构造 Composer 输入,由 Composer LLM 生成最终用户答案。 + +目标:避免 unsupported / overstated 内容绕过 Verifier 重新出现在用户答案里。 + +### Phase 4:按需考虑 SupervisorAgent + +只有当补证据循环、动态 worker 路由或多轮停止条件变复杂时,再迁移到 SupervisorAgent。 + +--- + +## 验收方向 + +- Executor 不再同时负责证据摘要和诊断结论; +- Diagnosis Executor 不能调用工具; +- Diagnosis Executor 不能引用不存在的 digest; +- Verifier 能区分 `direct_observation`、`reasonable_inference`、`overstated`、`unsupported`、`external_unknown`、`contradicted`; +- Final Composer 只能看到 Verifier 允许使用或降级后的材料; +- LOW_CONFID 输出不再把 unsupported claim 表达成确认结论; +- 对 mock 工具、真实 RAG、日志、指标均能保持同一套可审计链路; +- 结构化输出失败时有明确降级路径。 + +--- + +## 结论 + +本次讨论确认:单纯加强 Executor prompt 或增加规则式 evidence text 不能根治幻觉问题。 + +最终认可的方向是: + +**将证据收集、证据摘要、诊断推理、推理校验、最终表达拆成独立阶段;LLM 负责语义任务,代码负责边界、结构、过滤和降级。** + +这套设计既保留 Agent 工具调用能力,也避免 Executor 自己生成证据再证明自己的自证循环。