diff --git a/mvp/issues/executor-structured-output-v2.md b/mvp/issues/executor-structured-output-v2.md index f4d2d22..1355cb6 100644 --- a/mvp/issues/executor-structured-output-v2.md +++ b/mvp/issues/executor-structured-output-v2.md @@ -1,6 +1,6 @@ # Executor Structured Output V2 可执行设计与实施 Issue -**状态**:阶段三实施中 +**状态**:阶段四待启动,前三阶段已归档并提交 **严重程度**:高 **创建日期**:2026-07-07 **最后更新**:2026-07-08 @@ -15,61 +15,147 @@ --- -## 0. 给后续实现 Agent 的执行入口 +## 0. 执行摘要 -这份文档是 `Executor Structured Output V2` 的可执行设计 issue,不是单纯的数据结构草案。后续 agent 接手时,先按本节确认背景、当前阶段、执行纪律和验收边界,再进入后面的字段契约。 +这份文档是 `Executor Structured Output V2` 的可执行设计 issue,用来指导后续 agent 按阶段实施,不是单纯的数据结构草案。 -### 0.0 当前接手快照 +后续 agent 接手时,应先读完本节,确认“为什么做、做到哪、下一步做什么、如何验收”,再进入后面的字段契约和 prompt 细节。 -截至 2026-07-08,阶段状态如下: +### 0.0 这次改造要解决什么 + +当前 Chat 复杂诊断链路中,Executor 已经会调用 `lookup_knowledge`、`query_metrics`、`query_logs` 等证据工具,但它在最终输出时容易把三类内容混在一起: + +1. 本轮工具真实返回的事实。 +2. runbook、skill、知识库里的通用模式。 +3. 模型基于经验补全的推断。 + +这会导致 Executor 把“可能方向”写成“确认结论”,甚至在 `user_facing_answer` 中夹带未被 Verifier 校验过的根因、错误码、指标值或修复建议。Verifier 虽然会拦截一部分,但它被迫同时处理“事实抽取”和“事实校验”,职责边界不稳定。 + +本 issue 的核心目标是切断这条风险链路: + +```text +Executor 只负责收集证据并输出结构化材料; +Gatekeeper 先用代码拦截物理级幻觉; +Verifier 只判断 claim 是否能由证据合理推出; +Composer 只根据 Verifier 允许的材料生成最终用户答案。 +``` + +### 0.1 当前实现与目标链路 + +当前复杂 Chat 链路: + +```text +chat_planner -> chat_executor -> chat_verifier -> final answer +``` + +目标链路: + +```text +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 可推导性校验 | 实施中 | active change:`openspec/changes/executor-verifier-claim-checks` | -| 阶段四:Composer 输出最终答案 | 未开始 | 等阶段三归档并提交后再启动 | +| 阶段三:Verifier V2 可推导性校验 | 已完成、已归档、已提交 | `1b31e78 feat(agent): add verifier claim checks` | +| 阶段四:Composer 输出最终答案 | 下一阶段,待启动 | 建议 change id:`executor-composer-final-answer` | | 阶段五:回归评测与审计闭环 | 未开始 | 等 Composer 链路稳定后补齐 | 接手时的第一动作: -1. 先运行 `git status --short`,确认是否已有阶段三未提交改动。 -2. 如果 `openspec/changes/executor-verifier-claim-checks` 仍存在,继续阶段三,不要跳到 Composer。 -3. 阶段三完成后,必须先验证、归档 OpenSpec、回填 devflow、单独 commit,再进入阶段四。 +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. 先读 `0.1 - 0.10`:理解为什么要改、当前问题在哪里、目标链路是什么、哪些事情明确不做,以及如何按 OpenSpec/devflow 分阶段落地。 -2. 再读 `20 - 25`:理解代码影响面、阶段拆分、每阶段验收、回滚策略和最终完成标准。 -3. 最后按需查阅 `2 - 19`:这些是实现时使用的详细数据契约、Gatekeeper 规则、Verifier/Composer 输入输出。 +1. 先读 `0A - 0I`:理解背景、问题、目标、范围、阶段和执行纪律。 +2. 再读 `0.5 当前下一阶段执行卡片`、`19. Composer 设计`、`20. 当前代码影响面`、`21. 实施阶段`:理解当前下一阶段如何落地。 +3. 最后按需查阅 `2 - 18`:这些是已经讨论过的数据契约、Gatekeeper 规则和 Verifier 输入输出。 -一句话目标: +### 0.4 阶段实施纪律 + +后续实施必须按阶段串行推进: ```text -把 Executor 从“诊断 + 表达”收敛为“证据收集 + 结构化事实输出”, -在 Verifier 前加 Gatekeeper 做确定性拦截, -让 Verifier 只判断 claim 是否能由证据合理推出, -最终用户答案交给 Composer 生成。 +阶段一 archive + commit + -> 阶段二 archive + commit + -> 阶段三 archive + commit + -> 阶段四 archive + commit + -> 阶段五 archive + commit ``` 执行约束: -- 必须按阶段实施,不允许把五个阶段揉成一个大改动。 - 每个阶段都要先有 OpenSpec change,再实现、验证、归档到 devflow,并单独提交。 - 阶段未归档、未提交前,不进入下一阶段。 -- 验收失败如果是代码问题,agent 自行修复;如果是设计决策不明确,停下来问用户。 -- 本 issue 不要求一次性完成所有阶段;后续 agent 应从当前 git/OpenSpec/devflow 状态继续推进。 - -交付纪律: - - 每个阶段只解决该阶段的问题,不顺手做下一阶段。 - 每个阶段至少要有主成功路径和新增失败路径测试。 - 提交前必须确认 diff 只包含当前阶段内容。 -- 不提交、不推送,除非用户明确要求;本 issue 默认只指导阶段实现和本地提交。 +- 验收失败如果是代码问题,agent 自行修复;如果是设计决策不明确,停下来问用户。 +- 不推送,除非用户明确要求。 + +### 0.5 当前下一阶段执行卡片 + +当前下一阶段是: + +```text +阶段四:Composer 输出最终答案 +``` + +建议 OpenSpec change id: + +```text +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 用户答案。 --- -## 0.1 背景 +## 0A. 背景与问题定义 + +### 0A.1 背景 近期 Chat 复杂诊断链路中,多条诊断会话被 Verifier 判为 `LOW_CONFID`。这些会话并不是没有调用工具;Executor 通常已经调用了 `lookup_knowledge`、`query_metrics`、`query_logs` 等 evidence tools。 @@ -100,7 +186,7 @@ user_facing_answer --- -## 0.2 当前问题定义 +### 0A.2 当前问题定义 本 issue 要解决的是: @@ -127,7 +213,7 @@ Executor 证据归因幻觉 --- -## 0.3 当前实现 +### 0A.3 当前实现 当前代码中的复杂 Chat 链路是三 Agent 顺序执行: @@ -163,7 +249,7 @@ Verifier 既要校验 structured output,又要扫描自然语言答案 --- -## 0.4 目标设计 +## 0B. 目标设计 目标链路仍保持单条顺序链路,不新增 Controller,不改 Planner: @@ -207,14 +293,14 @@ Executor raw JSON --- -## 0.5 分阶段实施总览 +## 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` | +| 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. 实施阶段`。 @@ -229,11 +315,11 @@ Executor raw JSON -> 阶段五 archive + commit ``` -不能在阶段三未归档提交时启动 Composer,也不能在 Composer 未稳定时把回归评测阶段提前做成大杂烩。 +不能在 Composer 未稳定时把回归评测阶段提前做成大杂烩。 --- -## 0.6 范围与非目标 +## 0D. 范围与非目标 ### In scope @@ -258,7 +344,7 @@ Executor raw JSON --- -## 0.7 关键设计决策 +## 0E. 关键设计决策 | 决策 | 结论 | 原因 | |---|---|---| @@ -272,7 +358,7 @@ Executor raw JSON --- -## 0.8 实现约束 +## 0F. 实现约束 实现时必须遵守: @@ -285,7 +371,7 @@ Executor raw JSON --- -## 0.9 OpenSpec / devflow 落地要求 +## 0G. OpenSpec / devflow 落地要求 这个 issue 后续按 OpenSpec-first 的方式实施。每个阶段都必须留下可审计痕迹: @@ -334,7 +420,7 @@ cmd /c openspec validate --specs --- -## 0.10 推荐实现切片 +## 0H. 推荐实现切片 建议不要把所有改动塞进一个大 PR。推荐拆成以下可独立验收的切片: @@ -350,7 +436,7 @@ cmd /c openspec validate --specs --- -## 0.11 为什么这样拆阶段 +## 0I. 为什么这样拆阶段 本 issue 的改造目标不是一次性重写 Chat 诊断链路,而是逐步切断“证据不足但自然语言先下结论”的通道。阶段拆分按风险来源分层: @@ -1452,9 +1538,9 @@ Composer 输出严格 JSON。 --- -## 20. 当前代码影响面 +## 20. 代码影响面 -本 issue 按当前三 Agent 实现增量落地,不改 Planner,不引入 Controller。 +本 issue 按当前三 Agent 实现增量落地,不改 Planner,不引入 Controller。本节描述整体改造影响面;已完成阶段见 `21. 实施阶段`,当前下一步以阶段四 Composer 为准。 | 模块 | 当前职责 | 本次改动 | |---|---|---| @@ -1510,7 +1596,7 @@ Composer 输出严格 JSON。 已知阶段性债务: - `ChatService` 仍有临时 V2 renderer,用于 Composer 上线前避免 raw JSON 外泄。 -- Verifier 仍以 `facts_checked` 为主,`claim_checks` 等待阶段三。 +- Composer 上线后需要移除或降级阶段一临时 V2 renderer,避免 PASS 答案继续绕过 Composer。 ### 阶段二:Gatekeeper 接入 VerifierInputHook @@ -1572,19 +1658,13 @@ cmd /c openspec validate --specs ### 阶段三:Verifier V2 可推导性校验 -状态:实施中。 +状态:已完成并归档。 -当前 OpenSpec change: +已完成记录: -```text -openspec/changes/executor-verifier-claim-checks -``` - -接手说明: - -- 如果该 active change 仍存在,继续完成阶段三,不要启动阶段四。 -- 阶段三已有 OpenSpec proposal/design/spec/tasks,后续实现应先对照 `tasks.md` 逐项完成。 -- 阶段三完成后必须 archive、回填 devflow、单独 commit,推荐提交信息:`feat(agent): add verifier claim checks`。 +- 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 是否能由证据合理推出。 @@ -1621,7 +1701,6 @@ openspec/changes/executor-verifier-claim-checks ```powershell mvn "-Dtest=ExecutorGatekeeperServiceTest,VerifierInputHookTest,ChatServiceSequentialAgentTest" test -cmd /c openspec validate executor-verifier-claim-checks cmd /c openspec validate --specs ``` @@ -1636,7 +1715,13 @@ cmd /c openspec validate --specs ### 阶段四:Composer 输出最终答案 -状态:未开始。 +状态:下一阶段,待启动。 + +建议 OpenSpec change: + +```text +openspec/changes/executor-composer-final-answer +``` 目标:把最终用户表达从 Executor 中移出,由 Composer 基于 Verifier 允许的材料生成。 @@ -1655,6 +1740,21 @@ cmd /c openspec validate --specs - 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`。 @@ -1672,11 +1772,22 @@ cmd /c openspec validate --specs - `REJECT` 不输出 root cause 结论。 - Composer malformed 输出时有降级策略,不向用户泄露 raw JSON。 +建议验证命令: + +```powershell +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。 ### 阶段五:回归评测与审计闭环