Files
SuperBizAgent-java/mvp/archive/2026-07-20-doc-cleanup/issues/design-notes/executor-structured-output-v2.md

64 KiB
Raw Permalink Blame History

Executor Structured Output V2 可执行设计与实施 Issue

归档说明:本文记录旧的分阶段实施方案,其中的兼容期和待启动状态不再适用。当前实现以 mvp/architecture/executor-evidence-pipeline-refactor.md 和 OpenSpec 主规格为准。

状态:阶段四待启动,前三阶段已归档并提交 严重程度:高
创建日期:2026-07-07
最后更新:2026-07-08
文档类型:可执行设计 issue / 分阶段实施说明
范围:Chat Executor 输出结构、Gatekeeper、Verifier、Composer 数据契约调整
关联问题:

  • executor-evidence-attribution-hallucination
  • executor-self-evidence-loop-design-note
  • chat-verifier-agent
  • executor-evidence-output-contract

0. 执行摘要

这份文档是 Executor Structured Output V2 的可执行设计 issue,用来指导后续 agent 按阶段实施,不是单纯的数据结构草案。

后续 agent 接手时,应先读完本节,确认“为什么做、做到哪、下一步做什么、如何验收”,再进入后面的字段契约和 prompt 细节。

0.0 这次改造要解决什么

当前 Chat 复杂诊断链路中,Executor 已经会调用 lookup_knowledge、query_metrics、query_logs 等证据工具,但它在最终输出时容易把三类内容混在一起:

  1. 本轮工具真实返回的事实。
  2. runbook、skill、知识库里的通用模式。
  3. 模型基于经验补全的推断。

这会导致 Executor 把“可能方向”写成“确认结论”,甚至在 user_facing_answer 中夹带未被 Verifier 校验过的根因、错误码、指标值或修复建议。Verifier 虽然会拦截一部分,但它被迫同时处理“事实抽取”和“事实校验”,职责边界不稳定。

本 issue 的核心目标是切断这条风险链路:

Executor 只负责收集证据并输出结构化材料;
Gatekeeper 先用代码拦截物理级幻觉;
Verifier 只判断 claim 是否能由证据合理推出;
Composer 只根据 Verifier 允许的材料生成最终用户答案。

0.1 当前实现与目标链路

当前复杂 Chat 链路:

chat_planner -> chat_executor -> chat_verifier -> final answer

目标链路:

chat_planner
  -> chat_executor
  -> VerifierInputHook 内 Gatekeeper
  -> chat_verifier
  -> chat_composer
  -> final answer

本次不改 Planner,不新增 Controller,不改现有 retry 机制。改造重点只放在 Executor 输出契约、Gatekeeper 确定性校验、Verifier claim 可推导性校验、Composer 最终表达。

0.2 当前接手快照

截至 2026-07-08,仓库状态应以 git status --short、openspec/changes 和最近提交为准。当前已知阶段状态如下:

阶段 状态 记录
阶段一:Executor V2 输出契约 已完成、已归档、已提交 050cbc8 feat(agent): add executor evidence v2 contract
阶段二:Gatekeeper 接入 VerifierInputHook 已完成、已归档、已提交 c5e496e feat(agent): add executor gatekeeper hook
阶段三:Verifier V2 可推导性校验 已完成、已归档、已提交 1b31e78 feat(agent): add verifier claim checks
阶段四:Composer 输出最终答案 下一阶段,待启动 建议 change id:executor-composer-final-answer
阶段五:回归评测与审计闭环 未开始 等 Composer 链路稳定后补齐

接手时的第一动作:

  1. 运行 git status --short,确认工作区是否干净。
  2. 运行 Get-ChildItem openspec\changes,确认是否存在未完成 active change。
  3. 如果没有 active change,下一步应创建阶段四 OpenSpec change:executor-composer-final-answer。
  4. 阶段四完成后,必须先验证、归档 OpenSpec、回填 devflow、单独 commit,再进入阶段五。

0.3 推荐阅读顺序

  1. 先读 0A - 0I:理解背景、问题、目标、范围、阶段和执行纪律。
  2. 再读 0.5 当前下一阶段执行卡片、19. Composer 设计、20. 当前代码影响面、21. 实施阶段:理解当前下一阶段如何落地。
  3. 最后按需查阅 2 - 18:这些是已经讨论过的数据契约、Gatekeeper 规则和 Verifier 输入输出。

0.4 阶段实施纪律

后续实施必须按阶段串行推进:

阶段一 archive + commit
  -> 阶段二 archive + commit
  -> 阶段三 archive + commit
  -> 阶段四 archive + commit
  -> 阶段五 archive + commit

执行约束:

  • 每个阶段都要先有 OpenSpec change,再实现、验证、归档到 devflow,并单独提交。
  • 阶段未归档、未提交前,不进入下一阶段。
  • 每个阶段只解决该阶段的问题,不顺手做下一阶段。
  • 每个阶段至少要有主成功路径和新增失败路径测试。
  • 提交前必须确认 diff 只包含当前阶段内容。
  • 验收失败如果是代码问题,agent 自行修复;如果是设计决策不明确,停下来问用户。
  • 不推送,除非用户明确要求。

0.5 当前下一阶段执行卡片

当前下一阶段是:

阶段四:Composer 输出最终答案

建议 OpenSpec change id:

executor-composer-final-answer

阶段四要做的事情:

  • 新增 chat_composer Agent 或等价 Composer 调用。
  • 新增 chat-composer-prompt.md。
  • 在 ChatService 中,Verifier 完成后组装 Composer 输入。
  • Composer 输入只能包含 Verifier 允许材料:allowed_claims、allowed_hypotheses、missing_info、recommended_actions、rationale。
  • PASS、LOW_CONFID、REJECT 的最终用户答案都不能再读取 Executor 的 user_facing_answer。
  • Composer 输出必须是严格 JSON,包含 answer_summary、recommended_actions、user_facing_answer。
  • Composer malformed 时必须走安全降级,不得向用户泄露 raw JSON 或 Executor 原文。

阶段四明确不做:

  • 不改 Planner。
  • 不改 Gatekeeper 规则集。
  • 不扩展 Verifier 判定枚举。
  • 不新增数据库表。
  • 不让 Composer 调用工具。
  • 不让 Composer 重新判断根因。
  • 不提前做阶段五 eval fixture 大扩展。

阶段四最小验收:

  • Composer 输入不包含 raw tool output。
  • 未通过 Verifier 的 claim 不进入最终答案。
  • REJECT 最终答案不出现根因结论,allowed_hypotheses=[]。
  • LOW_CONFID 最终答案区分已确认信息、可能方向和证据缺口。
  • PASS 只有在存在允许输出的 root cause claim 时,才表达根因已确认。
  • ChatService 不再依赖阶段一临时 V2 renderer 生成 PASS 用户答案。

0A. 背景与问题定义

0A.1 背景

近期 Chat 复杂诊断链路中,多条诊断会话被 Verifier 判为 LOW_CONFID。这些会话并不是没有调用工具;Executor 通常已经调用了 lookup_knowledge、query_metrics、query_logs 等 evidence tools。

真正的问题是:Executor 在综合输出阶段把三类内容混在一起写成“当前事故结论”:

  1. 本轮工具真实返回的事实。
  2. runbook、skill 或知识库里的通用模式。
  3. 模型基于经验补全的推断。

当前 Executor 输出中还包含最终面向用户的自然语言字段:

diagnosis_summary
user_facing_answer

这会诱导 Executor 提前进入“诊断报告 / 用户表达”模式,在证据不充分时把“可能方向”写成“确认结论”。后续 Verifier 虽然会拦截,但它面对的是自然语言答案和结构化字段混在一起的输出,校验边界不稳定。

典型风险包括:

  • Executor 编造不存在的 source_invocation_ids。
  • Executor 使用真实 invocation id,但 evidence_excerpt 与真实工具输出不一致。
  • Executor 把 hypothesis 写成 confirmed claim。
  • Executor 在 user_facing_answer 里夹带 claims 中没有的根因、错误码、指标值或修复建议。
  • Verifier 被迫从自然语言里逐字抽事实,校验边界不稳定。

因此,本次改造不是为了“多加几个 Agent 显得完整”,而是为了收紧证据归因链路:Executor 只产出可校验材料,Verifier 只判断材料是否被证据支撑,最终表达交给 Composer。


0A.2 当前问题定义

本 issue 要解决的是:

Executor 证据归因幻觉

更具体地说,Executor 已经调用了工具,但在最终输出时没有严格区分:

类型 定义 应进入哪里
direct evidence 本轮工具直接观测到的事实 claims
reasonable inference 能由证据合理推出但不是逐字出现的判断 claims,但需要标注 indirect,并由 Verifier 判断是否说重
hypothesis 值得排查但尚未被当前证据确认的方向 hypotheses
missing evidence 还缺少哪些证据才能确认 missing_info
user expression 面向用户的自然语言答案 Composer 输出,不由 Executor 输出

如果不拆开这些边界,Verifier 会长期承担两个不同任务:

  1. 从自然语言中抽事实。
  2. 判断事实是否有证据支撑。

这会让 Verifier 的职责过重,也让最终答案容易夹带未经校验的内容。


0A.3 当前实现

当前代码中的复杂 Chat 链路是三 Agent 顺序执行:

chat_planner -> chat_executor -> chat_verifier

对应入口在:

ChatService.executeChatComplex(...)

当前数据流:

用户问题
  -> Planner 生成 planner_plan
  -> Executor 调用工具并输出 executor_evidence_v1
  -> VerifierInputHook 构造 verifier payload
  -> Verifier 输出 facts_checked/verdict
  -> ChatService 根据 verdict 生成最终 answer

当前关键问题在 Executor 与 Verifier 之间:

Executor 同时输出结构化 claims 和 user_facing_answer
Verifier 既要校验 structured output,又要扫描自然语言答案

这会让最终答案和证据归因之间出现缝隙。


0B. 目标设计

目标链路仍保持单条顺序链路,不新增 Controller,不改 Planner:

chat_planner
  -> chat_executor
  -> VerifierInputHook 内 Gatekeeper
  -> chat_verifier
  -> chat_composer
  -> final answer

职责边界:

组件 职责
Planner 选择 skill、拆解计划;本次不改
Executor 调用工具收集证据,输出结构化 claims/hypotheses/actions/missing_info
Gatekeeper 在 Verifier 前做确定性校验,拦截伪造 ID、工具名不匹配、明显张冠李戴、非法 schema
Verifier 判断 claims 是否能由证据合理推出,不再逐字扫描最终答案
Composer 只根据 Verifier 允许的材料生成最终用户答案

目标数据流:

Executor raw JSON
  -> parse JSON
  -> Gatekeeper.validate(...)
  -> Verifier claim-level validation
  -> ChatService 过滤 allowed_claims/allowed_hypotheses
  -> Composer 生成 user_facing_answer

核心原则:

  • 单智能体优先:不为了抽象而新增 Controller;只有已经明确的职责边界才拆 Agent。
  • Planner 不改:本 issue 的问题不在计划拆解,而在 Executor 输出和 Verifier 校验边界。
  • Gatekeeper 不做诊断:只做确定性校验,拦截伪造 ID、字段退化、明显张冠李戴。
  • Verifier 不再逐字扫描最终答案:主校验对象是结构化 claims。
  • Composer 不补事实:只把 Verifier 允许的材料组织成中文答案。

0C. 分阶段实施总览

阶段 名称 目标 状态 退出条件
1 Executor V2 输出契约 移除 Executor 最终表达字段,只输出结构化诊断材料 已完成并归档 executor_evidence_v2 可运行,最终用户不再看到 raw JSON
2 Gatekeeper 接入 VerifierInputHook 在 Verifier 前加入确定性拦截与审计 已完成并归档 gatekeeper_result 进入 Verifier payload 和 self_evaluation
3 Verifier V2 可推导性校验 从 facts_checked 转向 claim_checks,判断 claims 是否可由证据推出 已完成并归档 Verifier 不再从自然语言答案抽取额外事实
4 Composer 最终表达 由 Composer 根据 Verifier 允许材料生成最终用户答案 下一阶段 PASS/LOW_CONFID/REJECT 都不读取 Executor user_facing_answer
5 回归评测与审计闭环 用测试和 eval fixtures 证明幻觉拦截链路有效 未开始 伪造 ID、张冠李戴、hypothesis 写成事实等场景都有回归覆盖

更详细的实施拆解见 21. 实施阶段。

阶段推进顺序必须串行:

阶段一 archive + commit
  -> 阶段二 archive + commit
  -> 阶段三 archive + commit
  -> 阶段四 archive + commit
  -> 阶段五 archive + commit

不能在 Composer 未稳定时把回归评测阶段提前做成大杂烩。


0D. 范围与非目标

In scope

  • 调整 Executor 输出契约,从 executor_evidence_v1 演进到 executor_evidence_v2。
  • 在 VerifierInputHook 中接入 Gatekeeper。
  • 将 Gatekeeper 结果写入 Verifier payload 和 diagnosis_session.self_evaluation。
  • 将 Verifier 主输出从 facts_checked 迁移到 claim_checks,兼容期保留旧字段。
  • 新增 Composer 表达层,最终用户答案只来自 Verifier 允许材料。
  • 补齐关键回归测试和 eval fixtures。

Out of scope

本 issue 第一版明确不做以下事情:

  • 不改 Planner 的输入输出。
  • 不引入 Controller 或新的编排层。
  • 不改变现有 retry 机制。
  • 不要求 Gatekeeper 失败后自动回退 Executor 重试。
  • 不新增数据库表。
  • 不让 Gatekeeper 做根因判断。
  • 不让 Composer 调用工具或重新诊断。

0E. 关键设计决策

决策 结论 原因
是否改 Planner 不改 当前问题主要在 Executor 输出和 Verifier 校验边界
Gatekeeper 放在哪里 放在 VerifierInputHook 保持当前 workflow,不改 Executor hook 和编排
Gatekeeper 失败是否重试 第一版不重试 先建立拦截和审计闭环,避免扩大改动面
Executor 是否继续输出最终答案 不输出 防止未经 Verifier 的自然语言结论泄露
Verifier 是否逐字扫描答案 不扫描 主校验对象改为 claims
Composer 是否可以新增事实 不可以 Composer 只是表达层,不是诊断层
facts_checked 是否保留 兼容期保留 当前低置信模板、retry_context、评测可能依赖

0F. 实现约束

实现时必须遵守:

  • 优先保持现有三 Agent 复杂链路可运行。
  • 每个阶段都应能单独测试和回滚。
  • 第一版以“降低幻觉风险”为目标,不追求一次性重构所有历史字段。
  • 所有最终用户可见答案都必须来自 Verifier 允许输出的材料。
  • 所有 Gatekeeper 校验结果都必须进入审计,至少能回溯到 self_evaluation.verifier_evaluation.gatekeeper_result。
  • 如果结构化输出 malformed,不允许回退到自然语言逐字抽取后给 PASS。

0G. OpenSpec / devflow 落地要求

这个 issue 后续按 OpenSpec-first 的方式实施。每个阶段都必须留下可审计痕迹:

OpenSpec proposal/design/spec/tasks
  -> 实现代码
  -> 最小验证
  -> archive 到 openspec/changes/archive
  -> 回填 devflow/projects
  -> git commit

阶段交付物:

交付物 要求
OpenSpec change 每个阶段一个独立 change,不能复用上个阶段的 active change
tests 至少覆盖该阶段的新增失败路径和主成功路径
devflow 记录 brief、decisions、evidence、acceptance
commit 每个阶段单独提交,提交前确认 diff 只包含本阶段内容

推荐验证命令:

mvn "-Dtest=VerifierInputHookTest,ChatServiceSequentialAgentTest" test
cmd /c openspec validate --specs

如阶段新增专门测试,应把测试类加入 Maven -Dtest 列表。

后续 agent 每个阶段的固定执行清单:

  1. 确认当前阶段:读取本 issue、git status --short、openspec status。
  2. 确认 OpenSpec:没有 active change 时先创建;已有当前阶段 active change 时继续使用。
  3. 实现阶段内代码:只改该阶段要求的 prompt/service/hook/test,不夹带下一阶段。
  4. 验证:运行该阶段建议测试和 cmd /c openspec validate ...。
  5. 回填任务:更新 OpenSpec tasks.md、decisions.md,记录实际验证命令和结果。
  6. 归档:创建或更新 devflow/projects/<date>-<change-id>/,然后 cmd /c openspec archive <change-id> -y。
  7. 复验:归档后再次运行最小测试和 cmd /c openspec validate --specs。
  8. 提交:检查 git diff --cached --stat,确认只包含当前阶段,再单独 commit。

若步骤 4 或 7 失败:

  • 代码或测试问题:自行修复并重新验证。
  • 阶段边界或协议设计问题:停止,向用户说明阻塞点,不进入下一阶段。

0H. 推荐实现切片

建议不要把所有改动塞进一个大 PR。推荐拆成以下可独立验收的切片:

切片 内容 可单独合入条件
PR-1 Executor V2 prompt + parse-only 边界调整 Executor 能输出 V2 JSON;不会再通过 Executor 直接生成最终用户答案
PR-2 Gatekeeper 基础框架 + schema/invocation 规则 Verifier payload 和审计中出现 gatekeeper_result;伪造 invocation id 可被拦截
PR-3 Verifier V2 claim_checks + facts_checked 兼容 Verifier 主校验 claims;现有低置信模板和 retry_context 不坏
PR-4 Composer 接入 + 最终答案渲染 PASS/LOW_CONFID/REJECT 最终答案都不再读取 Executor user_facing_answer
PR-5 eval fixtures + 回归测试补齐 覆盖伪造 ID、工具名不匹配、excerpt 张冠李戴、hypothesis 写成事实

每个切片都应保留当前复杂 Chat 链路可运行。若某个切片失败,优先回滚该切片,不要连带回滚已经稳定的前置切片。


0I. 为什么这样拆阶段

本 issue 的改造目标不是一次性重写 Chat 诊断链路,而是逐步切断“证据不足但自然语言先下结论”的通道。阶段拆分按风险来源分层:

阶段 切断的风险 为什么不能合并
阶段一 Executor 提前输出最终诊断话术 先移除污染源,否则后面 Verifier/Composer 仍会被旧答案影响
阶段二 伪造 ID、工具名不匹配、非法 schema 等物理级幻觉 这类问题不需要 LLM 判断,先用代码低成本拦截
阶段三 claim 与证据之间是否可推导 这是 Verifier 的语义职责,必须在 Gatekeeper 之后处理
阶段四 最终用户答案夹带未验证事实 只有 Verifier 输出稳定后,Composer 才知道哪些材料可用
阶段五 新链路是否真的防住历史幻觉场景 等链路完整后再做系统化回归,避免测试绑死临时实现

每个阶段的设计都必须满足两个条件:

  • 可以独立验证,不依赖后续阶段已经完成。
  • 失败时可以独立回滚,不破坏已经归档提交的前置阶段。

因此后续实现时不要把“顺手移除临时 renderer”“顺手扩展 Gatekeeper 规则”“顺手接 Composer”混进阶段三。阶段三只解决 Verifier claim_checks 和有效 verdict guardrail。


1. 设计目标

当前 executor_evidence_v1 同时包含结构化诊断材料和自然语言表达字段:

  • diagnosis_summary
  • user_facing_answer

这两个字段容易让 Executor 提前进入“总结报告 / 用户表达”模式,并可能把未证实内容写成确认结论。

V2 的目标是让 Executor 只输出结构化诊断材料,不负责最终用户表达。


2. 输出结构

{
  "answer_version": "executor_evidence_v2",
  "claims": [
    {
      "claim_id": "claim-1",
      "claim_type": "symptom",
      "claim_text": "payment-service 出现请求超时日志。",
      "support_level": "direct",
      "evidence_bindings": [
        {
          "source_type": "tool_trace",
          "source_id": "trace-1",
          "tool_name": "query_logs",
          "source_invocation_ids": [394],
          "evidence_excerpt": "request timeout"
        }
      ]
    }
  ],
  "hypotheses": [
    {
      "hypothesis_text": "连接池压力可能参与了超时问题。",
      "basis": "当前已有超时现象,但缺少连接池 active/idle/pending 指标。",
      "needed_evidence": ["HikariCP active/idle/pending 指标"]
    }
  ],
  "recommended_actions": [
    {
      "action_text": "补充查询连接池 active/idle/pending 指标。",
      "reason": "用于确认连接池是否达到上限。",
      "evidence_bindings": []
    }
  ],
  "missing_info": [
    "缺少连接池指标,无法确认连接池耗尽是根因。"
  ]
}

3. 字段定义

字段 类型 必填 定义
answer_version string 是 固定为 executor_evidence_v2
claims array 是 已确认或有明确支撑的事实断言
hypotheses array 否 合理怀疑但未被当前证据确认的方向
recommended_actions array 否 建议补证据、排查或处理动作
missing_info array 否 当前无法确认结论所缺少的证据

4. claims

{
  "claim_id": "claim-1",
  "claim_type": "symptom",
  "claim_text": "payment-service 出现请求超时日志。",
  "support_level": "direct",
  "evidence_bindings": []
}
字段 类型 必填 定义
claim_id string 是 claim 唯一标识
claim_type string 是 claim 类型,例如 symptom / root_cause / impact / risk
claim_text string 是 已确认或有限确认的事实断言
support_level string 是 direct / indirect
evidence_bindings array 是 支撑该 claim 的证据绑定,不能为空

5. evidence_bindings

{
  "source_type": "tool_trace",
  "source_id": "trace-1",
  "tool_name": "query_logs",
  "source_invocation_ids": [394],
  "evidence_excerpt": "request timeout"
}
字段 类型 必填 定义
source_type string 是 证据来源类型,例如 tool_trace
source_id string 否 evidence block id、trace_ref 或可定位标识;第一版不作为硬校验依据
tool_name string 是 来源工具名
source_invocation_ids array 是 来源 tool_invocation.id
evidence_excerpt string 是 从工具返回中摘取的原话、指标值、日志片段或关键数据

6. hypotheses

{
  "hypothesis_text": "连接池压力可能参与了超时问题。",
  "basis": "当前已有超时现象,但缺少连接池 active/idle/pending 指标。",
  "needed_evidence": ["HikariCP active/idle/pending 指标"]
}
字段 类型 必填 定义
hypothesis_text string 是 未证实但值得排查的方向
basis string 是 它基于哪些已知证据,以及为什么仍只是推测
needed_evidence array 是 要确认该假设还缺少的证据
{
  "action_text": "补充查询连接池 active/idle/pending 指标。",
  "reason": "用于确认连接池是否达到上限。",
  "evidence_bindings": []
}
字段 类型 必填 定义
action_text string 是 建议动作
reason string 是 建议原因
evidence_bindings array 否 建议动作关联的证据绑定,可为空

8. 移除字段

V2 移除以下字段:

字段 移除原因
diagnosis_summary 容易让 Executor 提前总结诊断故事
user_facing_answer 容易夹带 claims 之外的确认式事实

最终用户答案应由 ChatService 或后续专门渲染阶段基于结构化字段生成。


9. Gatekeeper 设计

Executor 输出后、Verifier 接收前,增加一层确定性校验:

Executor raw output
  -> parse JSON
  -> Gatekeeper
  -> Verifier payload

Gatekeeper 的目标是拦截物理级幻觉,而不是替代 Verifier 做语义判断。

9.1 职责边界

组件 职责
Gatekeeper schema、引用 ID、工具名、excerpt、黑名单措辞等确定性校验
Verifier 判断 claim 是否被证据语义支撑、是否说重、是否缺关键证据

Gatekeeper 不判断根因是否正确,也不做新的检索。

9.2 接入位置

V1 接入位置保持在 VerifierInputHook 内,不改 Executor hook,不改 Chat workflow 编排:

parseExecutorOutput(...)
  -> buildVerifierTraceSummary(...)
  -> Gatekeeper.validate(...)
  -> verifierInput.put("gatekeeper_result", ...)

VerifierInputHook 的职责收敛为:

  • 解析 Executor raw output。
  • 构造 executor_output_parse_status。
  • 构造 tool_trace_summary。
  • 调用 Gatekeeper。
  • 组装 Verifier payload。

除 JSON parse 与最小可解析性判断外,VerifierInputHook 不再维护独立校验规则。

后续持久化时,将 gatekeeper_result 写入:

diagnosis_session.self_evaluation.verifier_evaluation.gatekeeper_result

10. Gatekeeper 规则加载

Gatekeeper 采用“规则代码稳定、规则配置可调”的设计。

Rule Index
  -> Rule Metadata
  -> Rule Implementation

10.1 索引层

索引层列出所有可用规则名和一句话描述,用于加载、展示和审计。

rules:
  - id: schema.executor_v2
    name: Executor V2 Schema
    description: 校验 Executor 输出是否符合 V2 schema
    enabled: true

  - id: evidence.invocation_ref
    name: Invocation Reference Check
    description: 校验 source_invocation_ids 是否属于当前 session 且工具名匹配
    enabled: true

  - id: evidence.excerpt_similarity
    name: Excerpt Similarity Check
    description: 校验 evidence_excerpt 与真实工具输出是否相似
    enabled: true

  - id: claim.hallucination_phrases
    name: Hallucination Phrase Check
    description: 禁止 confirmed claims 使用经验化、推测化措辞
    enabled: true

  - id: claim.evidence_utilization
    name: Evidence Utilization Check
    description: 检查 claim_text 与 evidence_excerpt 的关键词覆盖率
    enabled: false

10.2 元数据层

元数据层定义规则参数、阈值、严重级别和适用字段。调整阈值或黑名单时只改元数据,不改代码。

ruleMetadata:
  schema.executor_v2:
    severity: error
    verdictImpact: low_confid_on_fail
    params:
      requiredFields:
        - answer_version
        - claims
      deprecatedFields:
        - diagnosis_summary
        - user_facing_answer

  evidence.excerpt_similarity:
    severity: error
    verdictImpact: low_confid_on_fail
    params:
      passThreshold: 0.5
      warnThreshold: 0.3
      compareAgainst:
        - output_preview
        - retrieval_details
        - tool_trace_summary

  claim.hallucination_phrases:
    severity: error
    verdictImpact: low_confid_on_fail
    params:
      targetFields:
        - claims[].claim_text
      blacklist:
        - 通常情况下
        - 根据经验
        - 一般来说
        - 我认为
        - 理论上
        - 可能是
        - 推测
      allowInFields:
        - hypotheses[].hypothesis_text
        - hypotheses[].basis

  claim.evidence_utilization:
    severity: warning
    verdictImpact: warn_only
    params:
      minKeywordCoverage: 0.5

  evidence.invocation_ref:
    severity: error
    verdictImpact: reject_on_fail

10.3 规则实现层

规则实现使用代码中的固定接口。新增全新类型规则需要代码;调整阈值、词表、启用状态不需要改代码。

GatekeeperRule
  id()
  validate(context, metadata)
  -> RuleResult

10.4 与 VerifierInputHook 原有校验的关系

V2 中,VerifierInputHook 原有的结构判断只保留 parse-only 能力:

raw text
  -> sanitized JSON
  -> JsonNode / Map
  -> parse status

以下校验统一迁移到 Gatekeeper:

  • schema 字段是否完整。
  • answer_version 是否正确。
  • claims 是否为空或类型错误。
  • claims[].evidence_bindings 是否为空。
  • source_invocation_ids 是否存在。
  • tool_name 是否匹配。
  • evidence_excerpt 是否可信。
  • 是否出现已移除字段 diagnosis_summary / user_facing_answer。

这样避免 VerifierInputHook 和 Gatekeeper 出现两套规则、两套错误口径。


11. 推荐规则集

11.1 Schema 校验

规则 id:

schema.executor_v2

校验内容:

  • 输出必须是 JSON object。
  • answer_version 必须为 executor_evidence_v2。
  • claims 必须存在且为 array。
  • hypotheses、recommended_actions、missing_info 缺省时按空数组处理。
  • 不允许出现 diagnosis_summary。
  • 不允许出现 user_facing_answer。

Gatekeeper 输出给 Verifier 前应 normalize 结构:

missing hypotheses -> []
missing recommended_actions -> []
missing missing_info -> []

也就是说,Verifier 和 Composer 可以假定这三个字段存在且为数组。

11.2 Invocation 引用校验

规则 id:

evidence.invocation_ref

校验内容:

  • claims[].evidence_bindings 不能为空。
  • source_invocation_ids 必须为非空数组。
  • 每个 invocation id 必须属于当前 session。
  • tool_name 必须和对应 tool_invocation.tool_name 匹配。
  • 不允许引用其它 session 的工具调用。

该规则只检查 claims[].evidence_bindings。recommended_actions[].evidence_bindings 可以为空,不参与硬失败判定。

11.3 Excerpt 相似度校验

规则 id:

evidence.excerpt_similarity

校验内容:

  • 将 evidence_excerpt 与真实工具输出进行相似度比对。
  • 可比较来源包括:
    • tool_invocation.output_preview
    • tool_invocation.retrieval_details
    • tool_trace_summary.output_summary

建议阈值:

相似度 结果
>= 0.5 pass
0.3 - 0.5 warn
< 0.3 fail

如果只是 excerpt 相似度低,第一版判为 LOW_CONFID 级风险,不直接 REJECT,因为 output_preview 可能被截断,tool_trace_summary 也可能是摘要。

只有以下情况才进入 REJECT 级别:

  • invocation id 不存在。
  • invocation id 不属于当前 session。
  • tool_name 与 invocation 的真实工具名不匹配。
  • invocation 存在但 excerpt 明显指向另一个服务、错误码或指标值。

11.4 幻觉措辞校验

规则 id:

claim.hallucination_phrases

校验内容:

  • claims[].claim_text 不允许出现经验化、推测化措辞。
  • hypotheses 中允许出现推测措辞。

默认黑名单:

[
  "通常情况下",
  "根据经验",
  "一般来说",
  "可能是",
  "推测",
  "我认为",
  "理论上"
]

11.5 证据利用率校验

规则 id:

claim.evidence_utilization

校验内容:

  • 提取 claim_text 中的实词。
  • 提取 evidence_excerpt 中的实词。
  • 计算关键词覆盖率。

伪代码:

def check_evidence_utilization(claim_text, excerpt):
    claim_keywords = extract_keywords(claim_text)
    excerpt_keywords = extract_keywords(excerpt)
    overlap = claim_keywords & excerpt_keywords
    utilization = len(overlap) / len(claim_keywords)
    return utilization >= 0.5

第一版建议作为 warning,不作为硬拒绝条件。


12. Gatekeeper 输出

Gatekeeper 第一版只保留必要审计字段。

{
  "gatekeeper_result": {
    "status": "fail",
    "failed_rules": ["evidence.invocation_ref"],
    "warnings": ["evidence.excerpt_similarity"],
    "errors": [
      {
        "rule_id": "evidence.invocation_ref",
        "target": "claims[0].evidence_bindings[0]",
        "message": "source_invocation_ids not found in current session"
      }
    ]
  }
}

12.1 字段定义

字段 类型 定义
status string pass / warn / fail
failed_rules array 失败规则 id 列表
warnings array warning 规则 id 列表
errors array 最小错误明细
errors[].rule_id string 失败规则 id
errors[].target string 出问题的字段路径
errors[].message string 人类可读原因

12.2 总状态计算

任一规则 fail -> gatekeeper status = fail
否则任一规则 warn -> gatekeeper status = warn
否则 gatekeeper status = pass

第一版不记录 version、rule_set、summary、severity、details、checked_at 等字段,避免审计结构过重。

Verifier 如需判断 Gatekeeper fail 是否应导致 REJECT,使用规则元数据中的 verdictImpact,不依赖落库字段。


13. 审计落点

Gatekeeper 结果需要进入两处:

13.1 Verifier payload

{
  "executor_structured_output": {},
  "executor_output_parse_status": {},
  "tool_trace_summary": [],
  "gatekeeper_result": {}
}

Verifier 可根据 gatekeeper_result 调整判定,尤其是:

  • gatekeeper_result.status=fail 时,不应输出 PASS。
  • schema 或 invocation 引用失败时,应倾向 LOW_CONFID 或 REJECT。

13.1.1 parse status 与 schema status 边界

executor_output_parse_status 只表示 JSON 解析结果:

parse status 含义
valid raw output 可解析为 JSON object
missing raw output 为空或不是 JSON
malformed raw output 像 JSON 但解析失败

schema 合法性不再由 parse status 表达,统一交给 Gatekeeper:

JSON parse valid but schema invalid
  -> executor_output_parse_status.status = valid
  -> gatekeeper_result.status = fail
  -> failed_rules contains schema.executor_v2

13.2 self_evaluation

持久化位置:

diagnosis_session.self_evaluation.verifier_evaluation.gatekeeper_result

审计链路:

executor_raw_output
  -> executor_output_parse_status
  -> gatekeeper_result
  -> verifier_evaluation
  -> final_answer

14. Verifier 输入结构

V2 中 Verifier 以结构化输出为主输入,不再逐字抽取自然语言事实。

{
  "original_query": "用户原始问题",
  "executor_raw_output": "{raw executor output, debug/fallback only}",
  "executor_structured_output": {
    "answer_version": "executor_evidence_v2",
    "claims": [],
    "hypotheses": [],
    "recommended_actions": [],
    "missing_info": []
  },
  "executor_output_parse_status": {
    "status": "valid",
    "detail": "parsed executor evidence contract"
  },
  "tool_trace_summary": [],
  "gatekeeper_result": {
    "status": "pass",
    "failed_rules": [],
    "warnings": [],
    "errors": []
  },
  "retry_context": null
}
字段 用途
original_query 判断结构化诊断是否围绕用户问题
executor_raw_output 原始输出,仅用于 debug/fallback,不作为主校验来源
executor_structured_output Verifier 主校验对象
executor_output_parse_status 判断结构化输出是否可用
tool_trace_summary 证据索引
gatekeeper_result Gatekeeper 物理级校验结果
retry_context 第二轮时判断缺口是否被补足

兼容期可以保留旧字段名:

executor_final_answer

第一期 payload 可以同时包含 executor_raw_output 和 executor_final_answer,二者内容相同。executor_final_answer 语义上视为 deprecated;Verifier 不应在结构化输出合法时从该字段抽取额外事实。


15. Verifier 字段使用规则

15.1 executor_structured_output

Verifier 主要校验:

  • claims
  • hypotheses
  • recommended_actions
  • missing_info

重点是 claims:

claim_text 是否能由 evidence_bindings 指向的证据合理推出?
support_level 是否说重?
是否引入证据外的新实体、新指标、新错误码或新根因?

15.2 tool_trace_summary

tool_trace_summary 用于判断 evidence binding 是否有语义支撑。

Verifier 不要求 claim 与证据逐字一致,而是判断可推导性:

证据:payment-service CPU 92%,线程数 245
claim:payment-service 当前存在 CPU 使用率过高现象
=> direct_observation

claim:CPU 过高可能导致支付接口超时
=> reasonable_inference

claim:CPU 过高是支付超时的唯一根因
=> overstated

15.3 gatekeeper_result

Gatekeeper 负责物理合法性,Verifier 消费结果:

status = pass
  -> 正常校验 claims

status = warn
  -> 正常校验 claims,但 rationale 可提及 warning

status = fail
  -> 不允许 PASS
  -> 相关 claim 至少应判 unsupported / external_unknown / contradicted

15.4 executor_output_parse_status

status = valid
  -> 使用结构化可推导性校验

status = missing / malformed
  -> 不再回退到自然语言逐字抽取
  -> verdict = LOW_CONFID
  -> groundedness_score = 0

15.5 executor_raw_output

executor_raw_output 只用于审计和 debug。

当 executor_structured_output 合法时,Verifier 不应从 executor_raw_output 中抽取额外事实。


16. Verifier 输出结构

V2 Verifier 输出从 facts_checked 转向 claim_checks。

{
  "verdict": "LOW_CONFID",
  "groundedness_score": 0.62,
  "claim_checks": [
    {
      "claim_id": "claim-1",
      "verification": "direct_observation",
      "detail": "query_metrics 显示 CPU 使用率为 92%,可直接支撑 CPU 过高现象。",
      "evidence_refs": [
        {
          "trace_ref": "trace-1",
          "tool_name": "query_metrics",
          "source_invocation_ids": [394],
          "note": "指标摘要包含 CPU=92%"
        }
      ]
    }
  ],
  "hypothesis_checks": [
    {
      "hypothesis_index": 0,
      "verification": "reasonable_hypothesis",
      "detail": "该假设明确标注为未证实,并列出需要补充的证据。"
    }
  ],
  "rationale": "已有证据支持超时现象,但根因仍缺少直接证据。"
}

兼容期建议同时保留 facts_checked:

  • claim_checks 作为 V2 主字段。
  • facts_checked 由 claim_checks 映射生成,供现有 Trace Workbench、eval 和降级输出继续使用。
  • 待前端和评测全部迁移后,再移除 facts_checked。

16.1 字段定义

字段 类型 定义
verdict string PASS / LOW_CONFID / REJECT
groundedness_score number 结构化 claim 的证据支撑评分
claim_checks array 对 claims 的可推导性校验结果
claim_checks[].claim_id string 被校验的 claim id
claim_checks[].verification string 可推导性判定
claim_checks[].detail string 判定说明
claim_checks[].evidence_refs array 使用的证据引用
hypothesis_checks array 对 hypotheses 的边界校验
hypothesis_checks[].hypothesis_index number hypothesis 数组下标
hypothesis_checks[].verification string reasonable_hypothesis / unsupported_hypothesis / overstated_as_fact
hypothesis_checks[].detail string 判定说明
rationale string 总体判定理由

16.1.1 facts_checked 兼容映射

兼容期内,ChatService 需要从 claim_checks 生成旧字段 facts_checked。

映射规则:

claim_checks[].verification facts_checked[].verification
direct_observation direct_evidence
reasonable_inference indirect_support
overstated indirect_support
unsupported no_evidence
external_unknown no_evidence
contradicted contradicted

facts_checked[].fact 建议格式:

{claim_id}: {claim_text}

facts_checked[].is_critical 规则:

claim_type in ["root_cause", "symptom", "impact", "risk"] -> true
其它 -> false

facts_checked[].evidence_refs 直接复用 claim_checks[].evidence_refs。

16.2 claim verification 枚举

verification 含义
direct_observation 证据直接观测到该事实
reasonable_inference 证据没有逐字说明,但可以合理推出
overstated 有部分依据,但 claim 说得太满
unsupported 证据不足
external_unknown 引入证据外的新服务、数值、错误码、根因等
contradicted 与证据冲突

external_unknown 需要区分严重程度:

场景 建议 verdict
引入证据外的核心服务名、订单号、错误码、关键指标值、根因 REJECT
引入非核心背景实体或表达不清 LOW_CONFID

17. Verifier 判定矩阵

PASS

必须同时满足:

  • gatekeeper_result.status != fail
  • 所有核心 claims 均为 direct_observation 或 reasonable_inference
  • 至少一个关键 claim 为 direct_observation
  • 不存在 overstated
  • 不存在 unsupported
  • 不存在 external_unknown
  • 不存在 contradicted

LOW_CONFID

满足任一条件:

  • 存在 unsupported
  • 存在 external_unknown
  • 存在 overstated
  • 所有核心 claims 都只是 reasonable_inference
  • executor_output_parse_status.status 为 missing 或 malformed
  • gatekeeper_result.status = fail,但失败不属于严重伪造

REJECT

满足任一条件:

  • 任一核心 claim 为 contradicted
  • Gatekeeper 发现严重伪造:
    • 编造 source_invocation_ids
    • 跨 session 引用
    • tool_name 与 invocation 不匹配
    • 使用真实 id 但 excerpt 明显张冠李戴
  • Verifier 发现核心 external_unknown:
    • 新增证据外服务名
    • 新增证据外订单号
    • 新增证据外错误码
    • 新增证据外关键指标值
    • 新增证据外根因

严重伪造由 Gatekeeper 规则元数据中的 verdictImpact=reject_on_fail 定义。


18. 与 V1 Verifier 的差异

项目 V1 V2
主校验对象 executor_final_answer 和 structured output executor_structured_output
校验方式 从自然语言中抽取 facts 并逐条校验 对 claims 做可推导性校验
原始文本作用 事实抽取来源之一 debug/fallback only
输出字段 facts_checked claim_checks
Gatekeeper 无 前置物理级校验
malformed 输出 可回退自然语言校验 直接 LOW_CONFID

19. Composer 设计

Composer 是最终表达层。它不负责诊断,不调用工具,不补充新事实。

Executor structured output
  -> Gatekeeper
  -> Verifier
  -> Composer
  -> final answer

19.1 职责边界

组件 职责
Executor 输出结构化诊断材料
Gatekeeper 做物理级确定性校验
Verifier 判断 claims 是否可由证据合理推出
Composer 将 Verifier 允许输出的材料组织成用户可读中文答案

Composer 禁止:

  • 调用工具。
  • 重新诊断。
  • 重新判断根因。
  • 新增服务名、订单号、时间、指标值、错误码、根因。
  • 把 hypothesis 写成 confirmed claim。
  • 把相关性写成因果。

19.2 Composer 输入

Composer 输入应由 ChatService 根据 Executor、Gatekeeper、Verifier 的结果过滤得到。

{
  "original_query": "用户原始问题",
  "verdict": "LOW_CONFID",
  "allowed_claims": [
    {
      "claim_id": "claim-1",
      "claim_type": "symptom",
      "claim_text": "订单123支付失败期间出现 ERR_TIMEOUT,请求耗时 5.3 秒。"
    }
  ],
  "allowed_hypotheses": [
    {
      "hypothesis_text": "网关超时阈值可能偏低。",
      "needed_evidence": ["网关超时阈值配置"]
    }
  ],
  "missing_info": [
    "缺少网关侧具体配置参数,无法确认阈值是否过低。"
  ],
  "recommended_actions": [
    {
      "action_text": "检查网关侧超时阈值配置",
      "reason": "当前缺少网关配置参数,需确认阈值是否低于实际请求耗时。"
    }
  ],
  "rationale": "已有证据支持请求超时现象,但根因仍缺少直接证据。"
}
字段 类型 定义
original_query string 用户原始问题
verdict string Verifier verdict
allowed_claims array Verifier 允许作为确认事实输出的 claims
allowed_hypotheses array Verifier 允许作为合理推测输出的 hypotheses
missing_info array 证据缺口
recommended_actions array 允许输出的建议动作
rationale string Verifier 总体判定理由

Composer 不应接收 raw tool output,也不应接收未经筛选的完整 Executor 输出。

19.2.1 Composer 输入组装规则

ChatService 根据 Verifier 输出组装 Composer 输入:

Verifier 结果 Composer 输入
claim_checks[].verification = direct_observation 放入 allowed_claims
claim_checks[].verification = reasonable_inference 可放入 allowed_claims,但表达时不得写成唯一根因
claim_checks[].verification = overstated 不放入 allowed_claims,可降级为 allowed_hypotheses 或 missing_info
claim_checks[].verification = unsupported 不输出为事实,放入 missing_info
claim_checks[].verification = external_unknown 不输出,必要时放入 missing_info
claim_checks[].verification = contradicted 不输出,触发 REJECT 降级表达

allowed_hypotheses 只允许来自:

  • 原始 hypotheses。
  • hypothesis_checks 判为 reasonable_hypothesis 的条目。
  • 被 Verifier 判为 overstated 后降级的 claim。

Composer 执行条件:

Verifier 输出有效 verdict 后才执行 Composer。
executor_output_parse_status = missing/malformed 时,可跳过 Composer,直接使用固定低置信模板。
gatekeeper_result.status = fail 且 verdict = REJECT 时,Composer 只能接收 allowed_claims、missing_info、recommended_actions,不接收 allowed_hypotheses。

REJECT 场景下:

  • allowed_hypotheses 必须为空。
  • user_facing_answer 不得出现根因结论。
  • 推荐动作只能是补证据或人工复核类动作。

19.3 Composer 输出

Composer 输出严格 JSON。

{
  "answer_summary": "当前已确认订单123支付失败期间出现 ERR_TIMEOUT,请求耗时 5.3 秒;网关阈值是否过低尚未确认。",
  "recommended_actions": [
    {
      "action_text": "检查网关侧超时阈值配置",
      "reason": "当前缺少网关配置参数,需确认阈值是否低于实际请求耗时。"
    }
  ],
  "user_facing_answer": "您的订单123在支付失败期间出现了请求超时,记录显示 ERR_TIMEOUT,耗时约 5.3 秒。目前还不能确认网关阈值配置就是根因,因为缺少网关侧具体配置参数。建议下一步检查网关超时阈值,并与该请求耗时进行比对。"
}
字段 类型 定义
answer_summary string 一句话摘要,只能总结 Verifier 允许输出的内容
recommended_actions array 面向用户的建议动作
recommended_actions[].action_text string 建议动作
recommended_actions[].reason string 建议原因
user_facing_answer string 最终面向用户的中文答案

19.4 Verdict 表达规则

PASS

  • 可以表达确认结论。
  • 只能使用 allowed_claims 和 recommended_actions。
  • 只有当 allowed_claims 中存在 root cause 类型 claim 时,才能使用“根因已确认”类表述。

LOW_CONFID

  • 必须说明当前证据仍有缺口。
  • 必须区分“已确认信息”和“可能方向”。
  • 不得把 allowed_hypotheses 写成确认结论。
  • 必须包含至少一个证据缺口或下一步建议。

REJECT

  • 必须说明当前无法基于已获取证据生成可靠结论。
  • 不得输出根因结论。
  • 只输出已确认信息和下一步建议。

19.5 Composer Prompt 草案

你是 Answer Composer。你的职责是把 Verifier 允许输出的结构化材料,组织成用户可读的中文答案。

边界:
- 你不负责诊断。
- 你不调用工具。
- 你不补充新事实。
- 你不重新判断根因。
- 你只能使用输入中的 allowed_claims、allowed_hypotheses、missing_info、recommended_actions、rationale。

输入字段:
- original_query:用户原始问题
- verdict:PASS / LOW_CONFID / REJECT
- allowed_claims:允许作为确认事实输出的结论
- allowed_hypotheses:允许作为合理推测输出的内容
- missing_info:证据缺口
- recommended_actions:建议动作
- rationale:Verifier 判定理由

表达规则:
1. 如果 verdict=PASS:
   - 可以表达确认结论。
   - 只能使用 allowed_claims 和 recommended_actions。
2. 如果 verdict=LOW_CONFID:
   - 必须说明当前证据仍有缺口。
   - 必须区分“已确认”和“可能方向”。
   - 不得把 allowed_hypotheses 写成确认结论。
3. 如果 verdict=REJECT:
   - 必须说明当前无法基于已获取证据生成可靠结论。
   - 不得输出根因结论。
   - 只输出已确认信息和下一步建议。

禁止:
- 禁止新增输入中不存在的服务名、订单号、时间、指标值、错误码、根因。
- 禁止把相关性写成因果。
- 禁止把假设写成事实。
- 禁止输出 Markdown。
- 禁止输出 JSON 之外的任何文字。

输出格式:
{
  "answer_summary": "...",
  "recommended_actions": [
    {
      "action_text": "...",
      "reason": "..."
    }
  ],
  "user_facing_answer": "..."
}

20. 代码影响面

本 issue 按当前三 Agent 实现增量落地,不改 Planner,不引入 Controller。本节描述整体改造影响面;已完成阶段见 21. 实施阶段,当前下一步以阶段四 Composer 为准。

模块 当前职责 本次改动
chat-planner-prompt.md 选择 skill、拆解计划 不改
chat-executor-prompt.md 执行工具并输出 executor_evidence_v1 改为输出 executor_evidence_v2,移除最终表达字段
VerifierInputHook 解析 Executor 输出、构造 Verifier payload 收敛为 parse + trace summary + Gatekeeper + payload
ToolTraceSummaryService 从 tool_invocation 汇总证据索引 原则上不改;只有 Gatekeeper 缺少比对字段时才补最小字段
chat-verifier-prompt.md 校验 executor_final_answer 与证据 改为校验 executor_structured_output.claims 的可推导性
ChatService.parseVerifierDecision(...) 解析 facts_checked 增加 claim_checks 解析和 facts_checked 兼容生成
ChatService.persistVerifierEvaluation(...) 写入 verifier 审计结果 增加 gatekeeper_result、claim_checks、Composer 结果
ChatService 最终答案渲染 PASS 使用 user_facing_answer,LOW_CONFID/REJECT 用模板 改为由 Composer 或固定降级模板生成最终答案

第一版落地原则:

  • Planner 不改。
  • Gatekeeper 仍放在 Verifier 的 hook,即 VerifierInputHook。
  • VerifierInputHook 原有结构校验迁移到 Gatekeeper,hook 自身只保留 JSON parse。
  • 兼容期保留 executor_final_answer,但只作为 raw debug 字段。
  • 兼容期保留 facts_checked,但由 claim_checks 映射生成。

21. 实施阶段

每个阶段都必须作为独立 OpenSpec change 落地。后续 agent 不应直接从本节复制代码实现,而应把本节转成该阶段的 proposal.md、design.md、spec.md 和 tasks.md。

阶段一:Executor V2 输出契约

状态:已完成并归档。

已完成记录:

  • OpenSpec archive:openspec/changes/archive/2026-07-07-executor-v2-output-contract
  • devflow:devflow/projects/2026-07-07-executor-v2-output-contract
  • commit:050cbc8 feat(agent): add executor evidence v2 contract

目标:先让 Executor 不再输出最终诊断话术,只输出结构化诊断材料。

改动范围:

  • 更新 chat-executor-prompt.md。
  • answer_version 从 executor_evidence_v1 改为 executor_evidence_v2。
  • 移除 diagnosis_summary 和 user_facing_answer。
  • 保留 claims、hypotheses、recommended_actions、missing_info 的整体形状。

验收标准:

  • Executor 示例输出可被 JSON 解析。
  • 输出中不再出现 diagnosis_summary、user_facing_answer。
  • claims[].evidence_bindings 仍要求非空。
  • 现有流程即使尚未接入 Composer,也不会把 Executor raw JSON 直接当最终答案泄露给用户。

已知阶段性债务:

  • ChatService 仍有临时 V2 renderer,用于 Composer 上线前避免 raw JSON 外泄。
  • Composer 上线后需要移除或降级阶段一临时 V2 renderer,避免 PASS 答案继续绕过 Composer。

阶段二:Gatekeeper 接入 VerifierInputHook

状态:已完成并归档。

已完成记录:

  • OpenSpec archive:openspec/changes/archive/2026-07-07-executor-gatekeeper-hook
  • devflow:devflow/projects/2026-07-07-executor-gatekeeper-hook
  • commit:c5e496e feat(agent): add executor gatekeeper hook

目标:在 Verifier 之前用确定性规则拦截物理级幻觉。

改动范围:

  • 新增 Gatekeeper 校验服务,第一版先覆盖必要规则。
  • 在 VerifierInputHook 中调用 Gatekeeper。
  • 将 gatekeeper_result 写入 Verifier payload。
  • 将 gatekeeper_result 持久化到 self_evaluation.verifier_evaluation。
  • 更新 chat-verifier-prompt.md,明确 Gatekeeper fail 时不得 PASS。

第一版规则范围:

规则 必须实现 说明
schema.executor_v2 是 校验 V2 基础字段、移除字段、数组字段
evidence.invocation_ref 是 校验 invocation id 存在于当前 session 且工具名匹配
evidence.excerpt_similarity 否 可作为后续阶段或 warn-only 增强
claim.hallucination_phrases 否 可作为后续配置化规则增强
claim.evidence_utilization 否 第一版不建议硬启用,避免中文分词误杀

验收标准:

  • JSON parse 成功但 schema 不合法时,executor_output_parse_status.status=valid,gatekeeper_result.status=fail。
  • 编造不存在的 source_invocation_ids 时,failed_rules 包含 evidence.invocation_ref。
  • tool_name 与真实 invocation 不匹配时,Gatekeeper fail。
  • 缺省 hypotheses、recommended_actions、missing_info 时,传给 Verifier 前会 normalize 为 []。
  • 旧的 VerifierInputHook 结构校验不再和 Gatekeeper 重复维护。
  • diagnosis_session.self_evaluation.verifier_evaluation.gatekeeper_result 可回溯到本轮校验结果。

建议测试:

  • ExecutorGatekeeperServiceTest
  • VerifierInputHookTest
  • ChatServiceSequentialAgentTest

建议验证命令:

mvn "-Dtest=ExecutorGatekeeperServiceTest,VerifierInputHookTest,ChatServiceSequentialAgentTest" test
cmd /c openspec validate --specs

阶段退出条件:

  • OpenSpec change 已 archive。
  • devflow 已回填 brief、evidence、decisions、acceptance。
  • 本阶段代码和文档已单独 commit。

阶段三:Verifier V2 可推导性校验

状态:已完成并归档。

已完成记录:

  • OpenSpec archive:openspec/changes/archive/2026-07-07-executor-verifier-claim-checks
  • devflow:devflow/projects/2026-07-07-executor-verifier-claim-checks
  • commit:1b31e78 feat(agent): add verifier claim checks

目标:Verifier 不再逐字扫描自然语言,而是校验 claim 是否能由证据合理推出。

改动范围:

  • 更新 chat-verifier-prompt.md。
  • Verifier 主输入改为 executor_structured_output。
  • 输出新增 claim_checks。
  • 兼容期继续输出或由代码生成 facts_checked。
  • parseVerifierDecision(...) 能处理 claim_checks 和旧 facts_checked。

核心设计:

  • Verifier 主校验对象是 executor_structured_output.claims。
  • executor_raw_output / executor_final_answer 只作为 debug/fallback 字段,不作为新增事实来源。
  • Verifier 不要求 claim 与证据逐字一致,而是判断是否可以合理推出。
  • Gatekeeper fail 时,Verifier 不允许输出 PASS。

验收标准:

  • direct_observation、reasonable_inference、overstated、unsupported、external_unknown、contradicted 均有测试覆盖。
  • gatekeeper_result.status=fail 时 Verifier 不允许输出 PASS。
  • malformed/missing Executor 输出不回退自然语言抽事实,直接进入 LOW_CONFID。
  • facts_checked 兼容字段能继续支撑现有低置信模板、retry_context 和评测用例。

建议测试重点:

  • parseVerifierDecision(...) 能解析 claim_checks。
  • facts_checked 能从 claim_checks 兼容映射。
  • gatekeeper_result.status=fail + Verifier 返回 PASS 时,代码侧应降级或测试 prompt 禁止该行为。
  • unsupported / overstated 能进入低置信模板需要的 missing evidence 语义。

建议验证命令:

mvn "-Dtest=ExecutorGatekeeperServiceTest,VerifierInputHookTest,ChatServiceSequentialAgentTest" test
cmd /c openspec validate --specs

阶段退出条件:

  • Verifier prompt 已不再要求逐字扫描最终自然语言答案。
  • 审计中同时可见 claim_checks 和兼容 facts_checked。
  • 现有低置信重试链路不回归。
  • OpenSpec change 已 archive。
  • devflow 已回填 brief、evidence、decisions、acceptance。
  • 本阶段代码和文档已单独 commit。

阶段四:Composer 输出最终答案

状态:下一阶段,待启动。

建议 OpenSpec change:

openspec/changes/executor-composer-final-answer

目标:把最终用户表达从 Executor 中移出,由 Composer 基于 Verifier 允许的材料生成。

改动范围:

  • 新增 chat-composer-prompt.md 或等价 Composer 调用。
  • ChatService 在 Verifier 之后组装 Composer 输入。
  • Composer 只接收 allowed_claims、allowed_hypotheses、missing_info、recommended_actions、rationale。
  • PASS/LOW_CONFID/REJECT 均不再读取 Executor 的 user_facing_answer。

核心设计:

  • Composer 是表达层,不是诊断层。
  • Composer 不接收 raw tool output。
  • Composer 不接收未经筛选的完整 Executor output。
  • ChatService 负责根据 claim_checks 过滤出 allowed_claims。
  • overstated claim 不得作为确认事实输出,可降级为 hypothesis 或 missing_info。

建议 OpenSpec 内容:

  • proposal.md:说明为什么需要 Composer,重点写清“最终表达不能再读 Executor 输出”。
  • design.md:说明 Composer 输入过滤、输出解析、malformed fallback、审计落点。
  • specs/.../spec.md:补充 Composer 最终答案的行为场景,能力归属可沿用现有 chat-verifier-agent 或新增更合适的 capability,但必须保持命名清晰。
  • tasks.md:按 prompt、ChatService 组装、输出解析、审计、测试、归档拆任务。
  • decisions.md:记录 PASS/LOW_CONFID/REJECT 的降级策略和 Composer 失败 fallback。

建议代码入口:

  • src/main/resources/prompts/chat-composer-prompt.md
  • src/main/java/com/superbiz/agent/service/ChatService.java
  • src/test/java/com/superbiz/agent/service/ChatServiceSequentialAgentTest.java
  • 如需要新增专门测试,可新增 Composer 输入过滤或输出解析的 focused test。

验收标准:

  • Composer 输出严格 JSON,包含 answer_summary、recommended_actions、user_facing_answer。
  • Composer 不接收 raw tool output。
  • REJECT 时 allowed_hypotheses=[],最终答案不出现根因结论。
  • LOW_CONFID 时必须区分已确认信息和可能方向。
  • PASS 时只有存在 root cause 类型 allowed claim,才允许表达“根因已确认”。
  • ChatService 不再依赖临时 V2 renderer 生成 PASS 用户答案。

建议测试重点:

  • Composer 输入不包含 raw tool output。
  • 未通过 Verifier 的 claim 不进入最终答案。
  • LOW_CONFID 能输出已确认信息、缺口、下一步建议。
  • REJECT 不输出 root cause 结论。
  • Composer malformed 输出时有降级策略,不向用户泄露 raw JSON。

建议验证命令:

mvn "-Dtest=ChatServiceSequentialAgentTest" test
cmd /c openspec validate executor-composer-final-answer
cmd /c openspec validate --specs

阶段退出条件:

  • 最终用户答案唯一来源是 Composer 或固定降级模板。
  • Executor 的 user_facing_answer 不再参与任何最终答案路径。
  • Stage 1 的临时 renderer 被移除或明确只作为关闭 Composer 时的安全降级,不恢复 Executor 表达。
  • OpenSpec change 已 archive。
  • devflow 已回填 brief、evidence、decisions、acceptance。
  • 本阶段代码和文档已单独 commit。

阶段五:回归评测与审计闭环

状态:未开始。

目标:确认新链路真的降低证据归因幻觉,而不是只改变字段名。

改动范围:

  • 扩展 VerifierInputHookTest。
  • 扩展 ChatServiceSequentialAgentTest。
  • 增加诊断 eval fixtures,覆盖伪造 ID、张冠李戴、过度推断、缺证据降级。
  • 检查 diagnosis_session.self_evaluation 中的审计字段。

验收标准:

  • 伪造 invocation id 必须进入 REJECT 或至少不可 PASS。
  • excerpt 相似度不足但 invocation 合法时,默认进入 LOW_CONFID,不直接误杀为严重伪造。
  • Executor 把 hypothesis 写进 claim 时,Verifier 至少判 overstated 或 unsupported。
  • 最终答案中不再出现未通过 Verifier 的 claim。
  • 审计链路能从 final answer 回溯到 Composer 输入、Verifier 判定、Gatekeeper 结果、tool invocation。

建议 fixture 场景:

场景 预期
伪造不存在的 invocation id Gatekeeper fail,最终不可 PASS
引用真实 id 但 tool_name 不匹配 Gatekeeper fail,倾向 REJECT
excerpt 与真实工具输出明显不符 Gatekeeper fail 或 warn,最终不可 PASS
hypothesis 写成 root_cause claim Verifier 判 overstated 或 unsupported
Composer 输入不含某错误码但输出包含该错误码 测试失败

阶段退出条件:

  • 关键回归测试已进入 CI 可运行测试集合。
  • eval fixture 覆盖证据归因幻觉的主路径。
  • 文档、OpenSpec、devflow 与实际代码行为一致。

22. 兼容与回滚策略

22.1 兼容字段

第一版保留以下兼容字段,降低一次性改动风险:

字段 保留原因 后续处理
executor_final_answer 当前 Verifier payload、fallback、日志中仍使用该名称 作为 deprecated raw 字段保留一版
facts_checked 当前低置信模板、retry_context、评测可能依赖 从 claim_checks 映射生成
traceability_version 当前审计结构已有字段 V2 可改为 v2,但不作为功能判断依据

22.2 回滚开关

建议保留最小运行时开关:

structuredOutputV2.enabled
gatekeeper.enabled
composer.enabled

回滚策略:

  • 仅 Executor V2 出问题:关闭 structuredOutputV2.enabled,回到 V1 prompt。
  • Gatekeeper 误杀:关闭 gatekeeper.enabled,Verifier 仍可按 V2 claim 校验运行。
  • Composer 输出异常:关闭 composer.enabled,回到固定 LOW_CONFID/REJECT 模板;PASS 暂不直接使用 Executor 输出。

即使回滚 Composer,也不能恢复使用 Executor 的 user_facing_answer,否则会重新引入本 issue 要解决的问题。


23. 数据库与审计最小字段

不新增表,第一版继续写入 diagnosis_session.self_evaluation。

建议结构:

{
  "verifier_evaluation": {
    "verdict": "LOW_CONFID",
    "groundedness_score": 0.62,
    "critical_fact_count": 1,
    "claim_checks": [],
    "facts_checked": [],
    "rationale": "...",
    "round": 1,
    "traceability_version": "v2",
    "executor_output_parse_status": {},
    "executor_structured_output": {},
    "tool_trace_summary": [],
    "gatekeeper_result": {},
    "composer_output": {}
  }
}

字段控制原则:

  • Gatekeeper 只落 status、failed_rules、warnings、errors。
  • 不在数据库中保存规则元数据完整快照。
  • 不新增 checked_at、rule_set_version、details 等重字段。
  • 如果后续需要复盘规则版本,再单独设计审计版本字段。

24. 实施前待确认点

以下问题不阻塞第一阶段,但实施前需要明确默认答案:

问题 建议默认
Composer 是第四个 Agent 还是普通服务调用? 作为 chat_composer Agent,但由 ChatService 在 Verifier 后过滤输入再调用
schema.executor_v2 失败是否 REJECT? 第一版 LOW_CONFID,因为它可能是格式退化,不一定是伪造
evidence.excerpt_similarity 失败是否 REJECT? 默认 LOW_CONFID;只有明显张冠李戴才 REJECT
claim.evidence_utilization 是否启用? 第一版禁用或 warn-only,避免中文分词误杀
是否改 Planner? 不改
是否改现有重试机制? 不改;Gatekeeper 失败第一版不触发自动回退重试

25. Definition of Done

本 issue 完成时,需要同时满足:

  • Executor 不再输出 diagnosis_summary 和 user_facing_answer。
  • Gatekeeper 已接入 VerifierInputHook,并进入 Verifier payload 与 self_evaluation。
  • Verifier 以 claim_checks 为主输出,并保留 facts_checked 兼容。
  • Composer 负责最终 user_facing_answer。
  • PASS 答案不包含未通过 Verifier 的 claim。
  • LOW_CONFID/REJECT 答案不泄露 Executor 原始结论。
  • 关键回归测试覆盖伪造 ID、工具名不匹配、excerpt 张冠李戴、hypothesis 写成事实、schema 退化。