# 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 自己生成证据再证明自己的自证循环。