diff --git a/devflow/glossary/CONTEXT.md b/devflow/glossary/CONTEXT.md index 02d5796..0d5133b 100644 --- a/devflow/glossary/CONTEXT.md +++ b/devflow/glossary/CONTEXT.md @@ -103,6 +103,11 @@ - 使用场景:Trace API、Trace UI、Verifier 审计、评测 fixture 和人工排查。 - 边界:Diagnosis Trace 是聚合视图,不要求单独的 trace 主表;当前 trace 明细由 `agent_step` 和 `tool_invocation` 表承载。 +### Diagnosis Orchestration Trace +- 定义:一次 Diagnosis Run 的紧凑编排审计摘要,记录实际节点路径、条件边原因、技术重试、降级和终止原因。 +- 使用场景:解释诊断编排为何进入某个节点、为何重试或为何提前终止,并支撑路由验收和人工审计。 +- 边界:它是 Diagnosis Trace 的编排维度,不是完整事件日志、自评估结果或持久恢复检查点;不保存 Prompt、模型思考、工具原文和完整编排上下文快照。 + ### Flyway - 定义:数据库版本迁移工具,管理 SQL 脚本的版本化执行 - 配置:spring.flyway.enabled=true, baseline-on-migrate=true @@ -156,14 +161,14 @@ - 边界:最终诊断结论必须被 evidence tools 支撑,不能仅由 skill 正文支撑。 ### Verifier Skill Isolation -- 定义:Chat Verifier 与 skill 系统隔离,只校验 Executor 答案和 `tool_trace_summary`。 +- 定义:Chat Verifier 与 skill 系统隔离,只校验 Gatekeeper 投影后的 `verified_executor_output` 和 `verified_evidence`。 - 使用场景:防止 Verifier 把 playbook 指令当作事实证据;Verifier 只判断已有证据是否支持结论。 -- 边界:Verifier 不接收 `skill_catalog`,不暴露 `read_skill`,不读取 `SKILL.md`。 +- 边界:Verifier 不接收 `skill_catalog`,不暴露 `read_skill`,不读取 `SKILL.md`、完整 `tool_trace_summary` 或未经验真的 Executor 自由文本。 ## Diagnosis Playbook Business Rules - Planner 只看 skill metadata,输出 `selected_skill`、`selection_reason` 和 plan。 - Executor 才能调用 `read_skill(selected_skill)`,并且读取 skill 后仍必须调用 evidence tools。 - Skill 正文不得替代 `lookup_knowledge`、日志、指标或告警数据。 -- Verifier 只基于 `tool_trace_summary` 校验事实,不基于 skill 正文校验事实。 +- Verifier 只基于 Gatekeeper 通过的 `verified_executor_output` 和 `verified_evidence` 校验事实,不基于 skill 正文、完整工具 Trace 或未验真输出校验事实。 - 当前阶段保留单 active skill 白名单:`diagnose-mysql-connection-pool`。 diff --git a/devflow/index.md b/devflow/index.md index a1842fb..e6c59ea 100644 --- a/devflow/index.md +++ b/devflow/index.md @@ -4,6 +4,7 @@ | 日期 | slug | 说明 | 领域 | 关键词 | 关联 OpenSpec | 状态 | |---|---|---|---|---|---|---| +| 2026-07-17 | chat-diagnosis-stategraph-design-freeze | 冻结 ISS-011 的 Graph State、条件边、有限重试、安全降级、审计和测试迁移边界。 | Chat diagnosis orchestration/design | StateGraph, runId, Gatekeeper, verified evidence, fallback, orchestration trace, test migration | openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze | archived | | 2026-07-10 | session-run-trace-isolation | 拆分会话态和运行态,引入 runId 隔离 Trace、Feedback、AIOps 和 demo 链路。 | Trace/session/run isolation | chat_session, diagnosis_run, runId, trace exact run, feedback fallback, AIOps SSE metadata, baseline drift | openspec/changes/archive/2026-07-10-session-run-trace-isolation | archived | | 2026-07-09 | interview-demo-quality-audit | 增加面试演示前置质量审计,覆盖 prompt、Gatekeeper 和评测基线。 | Agent eval/demo/Prompt audit | interview demo preflight, prompt_audit, gatekeeper rules, diagnosis baseline, 12 fixtures | openspec/changes/archive/2026-07-09-interview-demo-quality-audit | archived | | 2026-07-08 | executor-composer-final-answer | 引入 Composer 生成最终回答,只使用 Verifier 允许的结论材料。 | Chat quality gate/evidence attribution | chat_composer, final answer, allowed_claims, allowed_hypotheses, safe fallback, composer_output | openspec/changes/archive/2026-07-08-executor-composer-final-answer | archived | diff --git a/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/acceptance.md b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/acceptance.md new file mode 100644 index 0000000..81b326b --- /dev/null +++ b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/acceptance.md @@ -0,0 +1,70 @@ +# Chat Diagnosis StateGraph Design Freeze Acceptance + +## 结果 + +已接受。阶段 0 完成设计冻结,没有修改运行时实现。 + +## 验证 + +### 静态验证 + +- 命令:`git diff --check` +- 结果:passed +- 备注:仅有现有 LF/CRLF 提示,无 whitespace error。 + +- 检查:Git changed/untracked 路径运行时拒绝列表。 +- 结果:passed,12 个路径,`src/`、Maven、运行配置、脚本、数据库迁移命中 0。 +- 备注:阶段 0 只包含 Issue、glossary、OpenSpec/devflow 和执行记录。 + +- 检查:proposal → design → specs → tasks 四向对齐。 +- 结果:passed,gap=0。 +- 备注:状态、路由、Fallback、审计、测试迁移和六阶段边界均闭环。 + +### 脚本验证 + +- 命令:`openspec validate chat-diagnosis-stategraph-design-freeze --type change --strict --json` +- 结果:passed,1/1。 +- 备注:当前 change 无结构或场景格式问题。 + +- 命令:`openspec validate --specs --strict --json` +- 结果:passed,11/11。 +- 备注:归档前主规格基线未回归。 + +### 浏览器/人工验证 + +- 结果:not run。 +- 原因:阶段 0 无 UI 或运行行为。 + +### 未验证 + +- 单元测试:not run。阶段 0 无代码行为,新增或运行单元测试没有新的验收价值。 +- Maven E2E:not run。用户明确要求仅阶段 5 在全部实现完成后统一执行。 +- `logs/`:not inspected for stage acceptance;保留到阶段 5。 +- 数据库:not queried for stage acceptance;保留到阶段 5。 +- 备注:此前改造前 live baseline 仅为 research,不计入本阶段验收。 + +## 已完成范围 + +- 冻结最小 Graph State、所有条件边、四类独立计数和终止路径。 +- 冻结 verified-input、两类 Fallback、Run 状态与 orchestration trace 边界。 +- 冻结 L4 消费者、兼容、迁移、回滚和测试替换策略。 +- 创建 ADR-001,并规定阶段 1–5 引用本阶段 archive。 +- OpenSpec apply tasks 7/7 完成。 + +## 已知限制 + +- 运行时仍为旧 Sequential 编排,这是阶段 0 的有意状态。 +- 新设计尚未经过 Graph 编译、Node 单测、Chat 集成或最终 E2E;后续阶段逐项证明。 +- 主运行时 specs 暂时仍描述旧行为,只新增设计基线 capability。 + +## Bug 修复和诊断 + +- 更正此前错误流程模型:删除未提交的总 change,改为六个独立 sm-flow/change。 +- 更正此前错误 E2E 门禁:阶段 0–4 不做 E2E,阶段 5 统一验证。 + +## 交接 + +- 下一步:阶段 0 Git commit;提交完成后才能创建阶段 1 change。 +- Delta sync:新增 `chat-diagnosis-stategraph-design-freeze` 主 spec,共 6 个 requirements,无现有 spec 修改/删除。 +- OpenSpec 归档确认:用户已在目标中明确要求每阶段 archive,并在后续澄清中再次确认,视为已授权。 +- OpenSpec 归档结果:已同步主 spec,并归档到 `openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/`。 diff --git a/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/adr/ADR-001-stategraph-run-control-boundary.md b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/adr/ADR-001-stategraph-run-control-boundary.md new file mode 100644 index 0000000..ade4201 --- /dev/null +++ b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/adr/ADR-001-stategraph-run-control-boundary.md @@ -0,0 +1,27 @@ +# ADR-001: StateGraph owns run-scoped diagnosis control + +**状态**:已接受 +**日期**:2026-07-17 + +复杂 Chat 使用 Spring AI Alibaba StateGraph 管理一次 Diagnosis Run 内的顺序、条件边、有限重试、终止和降级;ReactAgent 只执行 Planner、Executor、Verifier、Composer 的语义任务,Gatekeeper、verified-input builder、evidence retry prepare 和固定 Fallback 使用确定性 Java Node。这样可以精确恢复失败位置并形成可审计路径,同时继续复用现有 Agent、工具和证据协议。 + +Gatekeeper 从 `VerifierInputHook` 的隐式执行迁为显式且唯一的 Graph Node。Verifier 只能消费 Gatekeeper passed checked bindings 投影出的 `verified_executor_output` 和 `verified_evidence`;前置验证失败的 Fallback 不得输出 Executor claim。 + +编排审计只属于当前 `runId`:有界 `orchestration_events` 压缩后写入 `diagnosis_run.orchestration_trace`,Trace API 只在 `run.orchestrationTrace` 暴露解析对象。它不替代 self evaluation、AgentStep、ToolInvocation 或持久 Graph checkpoint,也不新增 trace 明细表。 + +## Considered Options + +- 继续在 `ChatService` 外层叠加 `for/if`:拒绝,失败恢复位置、循环上限和路由原因仍然隐式。 +- 使用 SupervisorAgent:拒绝,当前是固定诊断 Pipeline,不需要动态选择专科 Agent。 +- 直接将父 Graph State 交给 `ReactAgent.asNode(...)`:首版拒绝,无法证明 messages、outputKey 和私有执行上下文隔离。 +- 长期保留 Sequential/StateGraph feature flag 双轨:拒绝,会形成两个编排真理源并增加安全规则漂移。 + +## Compatibility And Migration + +`/api/chat`、`executor_evidence_v2`、Verifier 和 Composer 输出协议保持不变。Trace API 只增加 run-scoped 字段;数据库只增加 nullable JSON 列,历史 Run 不回填。 + +实施必须按六个独立 sm-flow/change 依次完成:设计冻结、路由骨架、真实节点、ChatService/Trace 切换、测试体系、清理与最终验收。阶段 1–5 必须读取阶段 0 archive,偏离本 ADR 时通过当阶段 OpenSpec 显式修正。 + +## Rollback + +每阶段使用独立 Git commit,可整体 revert 当前阶段。生产切换后回滚代码时允许保留 nullable `orchestration_trace` 列;不得用配置重新形成长期双轨。 diff --git a/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/brief.md b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/brief.md new file mode 100644 index 0000000..025af35 --- /dev/null +++ b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/brief.md @@ -0,0 +1,20 @@ +# Chat Diagnosis StateGraph Design Freeze Brief + +## 背景 + +- 用户目标:将 ISS-011 阶段 0–5 分别作为独立 sm-flow,前一阶段 archive 并 Git commit 后才进入下一阶段。 +- 当前问题:复杂 Chat 的跨 Agent 状态机分散在 ChatService、SequentialAgent、VerifierInputHook 和 ThreadLocal 中;实现前需先冻结统一设计。 +- 关联 OpenSpec:`openspec/changes/chat-diagnosis-stategraph-design-freeze/` +- devflow 分档:complex + +## 范围 + +- 本次要做:冻结最小 Graph State、完整路由、有限重试、安全 Fallback、run-scoped 审计、L4 接口影响和测试替换边界。 +- 本次不做:任何 Java、SQL、Prompt、配置、运行时 spec 或运行行为修改;不运行 Maven E2E。 +- 影响区域:ISS-011、glossary、OpenSpec 设计基线、阶段 0 ADR 和后续五阶段交接契约。 + +## OpenSpec 对齐 + +- proposal 覆盖状态:已覆盖阶段 0 目标、范围、非目标、验收和风险。 +- specs 覆盖状态:新增设计基线 capability,不提前修改运行时 capabilities。 +- tasks 覆盖状态:7/7 完成,且仅包含文档、ADR 和静态验证。 diff --git a/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/decisions.md b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/decisions.md new file mode 100644 index 0000000..d8fbb28 --- /dev/null +++ b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/decisions.md @@ -0,0 +1,120 @@ +# Chat Diagnosis StateGraph Design Freeze Decisions + +## Question Pool + +| # | 维度 | 问题 | 模式 | 状态 | +|---|---|---|---|---| +| Q1 | 术语 | Diagnosis Orchestration Trace 与 Diagnosis Trace、self evaluation、Graph checkpoint 的边界是什么? | evidence-driven | 已解决 | +| Q2 | 边界 | ISS-011 是一个总 sm-flow,还是阶段 0–5 各自独立 sm-flow? | user-interview | 已解决 | +| Q3 | 验收 | Maven E2E 在每阶段执行还是只在最终阶段执行? | user-interview | 已解决 | +| Q4 | 验收 | 每阶段是否强制新增并运行单元测试? | user-interview | 已解决 | +| Q5 | 技术 | 锁定的 StateGraph 版本是否提供条件边、config-aware Node/Edge、recursion limit 和 threadId? | evidence-driven | 已解决 | +| Q6 | 架构 | Gatekeeper、Verifier 输入与 Composer 的可信材料边界应复用什么现有契约? | evidence-driven | 已解决 | +| Q7 | 接口 | 阶段 0 本身和最终设计分别属于什么接口影响等级? | evidence-driven | 已解决 | +| Q8 | 归档 | 每阶段是否应在进入下一阶段前独立 archive 和 Git commit? | user-interview | 已解决 | + +## Evidence-driven + +| 结论 | 证据来源 | 是否已汇报用户 | +|---|---|---| +| Orchestration Trace 是 run 级紧凑编排摘要,不是事件日志、自评估或 Graph checkpoint | `devflow/glossary/CONTEXT.md`、ISS-011 §7.5、session-run-trace-isolation decisions | 已汇报 | +| Gatekeeper 从 Hook 迁为显式 Node 是有意替换旧内部架构,不允许双入口 | executor-gatekeeper-hook decisions、ISS-011 §2.3/§7.2/§14 | 已汇报 | +| Verifier verified evidence 必须来自 Gatekeeper 通过的 checked bindings;Composer 不得读取 raw Executor/tool output | verifier-evidence-reference-fidelity、executor-composer-final-answer 历史档案、ISS-011 §7.4 | 已汇报 | +| 本地 1.1.2.0 API 支持条件边、config-aware Node/Edge、recursion limit、RunnableConfig.threadId 和 ReactAgent.call(input, config) | Maven dependency tree 与本地 JAR `javap` 研究记录 | 已汇报 | +| 阶段 0 是文档/规格交付,无运行时接口变化;其冻结的最终目标涉及状态机、DB 和 Trace API,属于 L4 | sm-flow operating-rules、ISS-011 §10/§14 | 已汇报 | +| 主 OpenSpec 的外层 groundedness round、Hook Gatekeeper 和完整 tool_trace_summary 输入与冻结设计冲突 | `openspec/specs/chat-verifier-agent/spec.md` 等主规格 | 已汇报 | + +## User-interview + +| 问题原文 | 用户原话 | 确认状态 | OpenSpec 回写 | +|---|---|---|---| +| ISS-011 的阶段关系如何映射 sm-flow? | “iss-011里每个阶段,都是一个sm-flow,而不是将一整个iss-011打包进一个sm-flow中” | 已确认 | 已回写 proposal 范围与非目标 | +| Maven E2E 何时执行? | “端到端只在最后阶段全部完成后才验证” | 已确认 | 已回写 acceptance 与 out of scope | +| 阶段单元测试是否强制? | “每个阶段如果有必要添加单元测试验收的话,就加,没有必要的话就跳过单元测试” | 已确认 | 已回写 acceptance | +| 每阶段如何进入下一阶段? | “每个阶段需要归档完并提交才能进入下一个阶段” | 已确认 | 已回写阶段门禁 | + +## OpenSpec Input Context + +### devflow index + +已命中并读取: + +- `session-run-trace-isolation` +- `executor-gatekeeper-hook` +- `verifier-evidence-reference-fidelity` +- `executor-evidence-output-contract` +- `executor-v2-output-contract` +- `executor-verifier-claim-checks` +- `executor-composer-final-answer` +- `mvp-demo-trace-acceptance` + +### Historical constraints entering OpenSpec + +- `runId` 是运行态和 Trace 的所有权边界。 +- AgentStep、ToolInvocation 与 self evaluation 的既有 run 绑定必须保留。 +- checked binding 是 verified evidence 的复用来源。 +- Composer 的 allowed-material 安全边界不得削弱。 +- 旧 Gatekeeper-in-Hook 决策在本设计中被显式废止,不能保留并行入口。 +- 主 OpenSpec 的旧运行时要求只能在对应实现阶段修改,阶段 0 不提前宣称代码已切换。 + +## Key Decisions + +### 六个独立交付单元 + +阶段 0–5 分别使用独立 slug、OpenSpec change、devflow 项目、Archive 和 Git commit。前一 change 归档并提交后才创建下一 change。 + +### 阶段 0 不承载后续实现 + +阶段 0 只冻结设计并归档长期上下文。阶段 1–5 的代码、数据库、测试和清理任务不进入本 change 的 tasks。 + +### 验收分层 + +阶段 0 没有运行时行为变化,不新增或运行单元测试,也不运行 Maven E2E。验收使用 OpenSpec strict validation、结构检查和文档一致性检查。Maven E2E、`logs/` 与数据库只在阶段 5 统一执行。 + +### 接口影响 + +- 当前 change:L1 文档/设计交付,不改变调用方可观察行为。 +- 冻结目标:L4,涉及内部状态机语义、Trace API 加字段、数据库契约、迁移和回滚。 +- 兼容:保持 `/api/chat` 和 Agent 输出协议;Trace API 为加法式 run 字段。 +- 回滚:后续运行时代码可按阶段 Git revert;nullable DB 列可保留,不引入双轨配置。 +- 消费者:ChatController/ChatService、Trace DTO/Service/UI/demo、数据库迁移、评测与运维审计。 + +## Risks Accepted + +- 阶段 0 archive 后,冻结设计已完成但运行时仍为旧 Sequential;这是有意的阶段性状态。 +- 六个 changes 的一致性由后续每次 context/commit gate 读取并审计本档案保证。 +- 不为阶段 0 的纯设计变更新增无行为价值的单元测试。 + +## Cross-Artifact 对齐检查 + +| 上游 → 下游 | 检查内容 | 状态 | +|---|---|---| +| brief/prd → proposal | Issue 的阶段 0 目标、范围、非目标和验收已进入 proposal | 已对齐 | +| proposal → 设计产物 | 状态、路由、重试、Fallback、审计、测试迁移和六阶段边界均进入 design | 已对齐 | +| 设计产物 → specs/tasks | L4 影响、安全边界、终止规则和阶段 0 文档工作均进入 spec 或 tasks | 已对齐 | +| specs → tasks | 设计基线、源文档、ADR、严格验证和无运行时变更检查均有可执行任务 | 已对齐 | + +Gap:无。 + +## Architecture Audit + +输入链为 `ChatController -> ChatService`,目标处理链为 Run 生命周期 → Graph orchestrator → 白名单 Agent/Java Nodes,输出仍为 `ChatResult + DiagnosisRun + exact RunTrace`。Graph State 只属于当前 run,verified evidence 只来自当前 run 的 passed checked bindings,编排摘要只写当前 Diagnosis Run。旧“Gatekeeper stays in VerifierInputHook”决策被 ISS-011 的显式 Node 有意替代,但旧 Gatekeeper 规则本身继续复用;旧“不新增 trace 主表”和 run ownership 决策保持成立。主要耦合风险是 Hook/Node 双执行、阶段性主 spec 漂移和 Trace 投影重复,design 已分别通过单入口迁移、按阶段修改运行时 specs、唯一 `run.orchestrationTrace` 投影缓解。架构风险已由用户确认的 ISS-011 冻结决策和六阶段交付口径接受,无需返回 grill。 + +## Commit Preflight + +- proposal、design、specs、tasks 完整:通过。 +- 所有 user-interview 已确认:通过。 +- 未汇报 evidence-driven 结论:无。 +- L4 接口影响、消费者、兼容、迁移和回滚:已在 design 独立章节记录。 +- OpenSpec strict validation:change 1/1 通过,主 specs 11/11 通过。 +- Apply 授权:用户启动目标时要求分阶段完整执行、archive 和 Git commit,后续又确认六个独立 sm-flow;视为当前阶段连续执行授权。 + +## Apply Evidence + +- Task 1.1:ISS-011 §3–14 与 committed design 的 State 字段、条件边、四类计数、Fallback、安全审计和测试迁移语义一致;未发现需回写 OpenSpec 的 drift,未修改运行时代码。 +- Task 1.2:将 glossary 中新 Orchestration Trace 的具体 StateGraph/Graph State 表述收敛为“诊断编排/编排上下文快照”;Verifier 的 verified-output/evidence 名称作为跨节点协议保留。 +- Task 2.1:创建 ADR-001,记录 StateGraph 控制边界、显式 Gatekeeper、run-scoped trace、替代方案、兼容、迁移和回滚。 +- Task 2.2:六个独立 change 的唯一边界已写入 design、design-baseline spec、ADR 和根计划;阶段 1–5 必须引用阶段 0 archive。 +- Task 3.1:最终 strict validation 为 change 1/1、主 specs 11/11;proposal/design/spec/tasks 对状态、Fallback、审计、运行时非目标和测试迁移均形成闭环,cross-artifact gap 为 0。 +- Task 3.2:Git 枚举 12 个 changed/untracked 路径,运行时拒绝列表命中 0;无 `src/`、Maven、运行配置、脚本或数据库迁移变更。 +- Task 3.3:单元测试未运行,因为阶段 0 无代码行为;Maven E2E、`logs/` 和数据库核验未运行,因为用户明确要求只在阶段 5 全部实现后统一执行。改造前 research baseline 不计入阶段验收。 diff --git a/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/evidence.md b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/evidence.md new file mode 100644 index 0000000..e66c619 --- /dev/null +++ b/devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze/evidence.md @@ -0,0 +1,30 @@ +# Chat Diagnosis StateGraph Design Freeze Evidence + +## 证据 + +| 来源 | 证据 | 结论 | 是否已汇报 | +|---|---|---|---| +| ISS-011 §3–14 | 已定义目标流程、26 个 State 字段、有限回边、Fallback、Trace 和测试策略 | 可形成无开放分支的阶段 0 设计基线 | 是 | +| `ChatService` / Controller 引用核查 | 生产链为 Controller → ChatService → SequentialAgent,细粒度状态散布在 ChatService | StateGraph 应只接管 Run 内控制,ChatService 保留生命周期 | 是 | +| `VerifierInputHook` / `VerifierContextHolder` 引用核查 | Gatekeeper、工具摘要和解析结果通过 Hook/ThreadLocal 隐式传播 | Gatekeeper 与 verified-input 必须迁为显式 Node,不能保留双入口 | 是 | +| executor-gatekeeper-hook 历史 decisions | 旧阶段曾决定 Gatekeeper 留在 Hook 且不重试 | 本设计有意替换入口,但继续复用确定性规则 | 是 | +| session-run-trace-isolation 历史 decisions | runId 是执行/Trace 所有权边界,AgentStep/ToolInvocation 已承载明细 | orchestration trace 只附着当前 Run,不新增明细主表 | 是 | +| Verifier/Composer 历史档案 | checked bindings 和 allowed material 是可信边界 | Builder 不重复验真,Fallback 不泄漏 raw claim | 是 | +| 本地 Graph Core 1.1.2.0 JAR | 已验证条件边、config-aware Node/Edge、recursion limit、threadId 与 Agent call API | 阶段 1 可基于锁定签名实现,不采用网上漂移示例 | 是 | +| 主 OpenSpec 核查 | 旧 spec 仍描述 Hook Gatekeeper、完整 tool trace 和外层 groundedness round | 阶段 0 不提前改运行时 spec,后续对应实现阶段再修改 | 是 | +| 用户原话 | 六个独立 sm-flow;仅阶段 5 E2E;单测按必要性 | 阶段门禁与测试口径已固定 | 是 | + +## Evidence-driven 结论 + +- 结论:阶段 0 运行时影响为 L1,但冻结目标为 L4。 + - 证据:本阶段 changed paths 无 `src/`/SQL/配置;最终目标改变状态机、Trace API 和 DB。 + - 风险:设计完成不等于运行时完成。 + - 用户确认:已确认分阶段交付。 +- 结论:旧 Gatekeeper-in-Hook 决策应被显式 Node 有意替代。 + - 证据:当前 Hook 引用与 ISS-011 条件边要求冲突。 + - 风险:迁移期双执行。 + - 用户确认:ISS-011 冻结决策已确认。 +- 结论:阶段 0 不需要单元测试或 E2E。 + - 证据:Git 运行时拒绝列表命中 0,OpenSpec strict 和文档一致性已覆盖本阶段可观察产物。 + - 风险:运行时正确性仍未证明,将由阶段 1–5 测试和最终 E2E 证明。 + - 用户确认:已确认。 diff --git a/mvp/issues/active/ISS-011-chat-diagnosis-stategraph-orchestration.md b/mvp/issues/active/ISS-011-chat-diagnosis-stategraph-orchestration.md index 9423dd3..c366d06 100644 --- a/mvp/issues/active/ISS-011-chat-diagnosis-stategraph-orchestration.md +++ b/mvp/issues/active/ISS-011-chat-diagnosis-stategraph-orchestration.md @@ -60,9 +60,9 @@ Planner -> Executor -> Verifier | 失败位置 | 期望恢复位置 | |---|---| | Planner 输出非法 | 重试 Planner | -| Executor 输出结构非法 | 保留计划,只重试 Executor | +| Executor 输出结构非法 | 不重试 Executor,直接进入固定安全降级 | | Gatekeeper 引用验真失败 | 直接进入固定安全降级 | -| Verifier LOW_CONFID | 将 missing evidence 返回 Planner 进行有限补证据 | +| Verifier LOW_CONFID | 从 `facts_checked` 提取 `evidence_gaps`,返回 Planner 进行一次有限补证据 | | 工具权限或能力阻断 | 不重试,直接降级 | 当前 `SequentialAgent + 外层 round` 无法自然表达这些回边。 @@ -122,13 +122,14 @@ Java Node ```mermaid flowchart TD Start["START"] --> Planner["Planner"] - Planner -- "成功" --> Executor["Executor"] - Planner -- "失败" --> Fallback["Fallback"] + Planner -- "COMPLETED" --> Executor["Executor"] + Planner -- "INVALID_OUTPUT / RETRYABLE_FAILED 且本阶段未重试" --> Planner + Planner -- "NON_RETRYABLE_FAILED 或再次失败" --> Fallback["Fallback"] - Executor -- "结构有效" --> Gatekeeper["Gatekeeper"] - Executor -- "失败或阻断" --> Fallback + Executor -- "COMPLETED" --> Gatekeeper["Gatekeeper"] + Executor -- "INVALID_OUTPUT / TOOL_BLOCKED / FAILED" --> Fallback - Gatekeeper -- "PASS" --> Verifier["Verifier"] + Gatekeeper -- "PASS" --> VerifiedInput["Build Verified Verifier Input"] Gatekeeper -- "LOW_CONFID 且存在已验真 binding" --> VerifiedInput["Build Verified Verifier Input"] Gatekeeper -- "LOW_CONFID 且零条已验真 binding" --> Fallback Gatekeeper -- "REJECT" --> Fallback @@ -136,16 +137,19 @@ flowchart TD Verifier -- "PASS" --> Composer["Composer"] Verifier -- "REJECT" --> Composer - Verifier -- "LOW_CONFID 且有预算" --> EvidenceRetry["Prepare Evidence Retry"] - Verifier -- "LOW_CONFID 且无预算" --> Composer - Verifier -- "执行失败" --> Fallback + Verifier -- "LOW_CONFID 且存在有效 evidence_gaps" --> EvidenceRetry["Prepare Evidence Retry"] + Verifier -- "LOW_CONFID 且无有效 evidence_gaps 或已补查" --> Composer + Verifier -- "INVALID_OUTPUT / RETRYABLE_FAILED 且未重试" --> Verifier + Verifier -- "NON_RETRYABLE_FAILED 或再次失败" --> Fallback EvidenceRetry --> Planner - Composer --> End["END"] + Composer -- "COMPLETED" --> End["END"] + Composer -- "INVALID_OUTPUT / RETRYABLE_FAILED 且未重试" --> Composer + Composer -- "NON_RETRYABLE_FAILED 或再次失败" --> Fallback Fallback --> End ``` -### 4.1 本期只保留 Verifier LOW_CONFID 补证据 +### 4.1 本期只保留 Verifier LOW_CONFID 诊断补证据 Gatekeeper REJECT 本期不重试,直接进入 Fallback。 @@ -159,15 +163,88 @@ Verifier LOW_CONFID 保留当前有限补证据能力: 只有同时满足以下条件时才允许补证据: -- Verifier 提供了可执行的 missing evidence。 +- Verifier 返回 LOW_CONFID,且编排层能够从现有 `facts_checked` 中提取至少一个关键证据缺口。 - LOW_CONFID 不是由 Gatekeeper verdict ceiling 导致。 -- 所需工具存在且当前 Agent 有权限。 -- 仍有 Run 级预算。 - 本 Run 尚未执行过补证据轮次。 -Prepare Evidence Retry Node 由代码实现,负责把已确认事实、证据缺口、已调用工具、已执行 Query、当前 Skill 和剩余预算整理为受限 retry context,然后返回 Planner。 +Verifier 不新增 `missing_evidence`、`suggested_tool` 等输出字段,也不负责规划补查方式。Prepare Evidence Retry Node 由代码实现,从 `facts_checked` 中提取 `no_evidence` 或只有 `indirect_support` 的关键事实,并整理为受限 retry context: -Planner 第二轮使用 `EVIDENCE_GAP_ONLY` 模式,只生成增量补证据计划,不允许重新开始完整诊断、扩大服务范围或重复已经成功执行的查询。 +```json +{ + "mode": "EVIDENCE_GAP_ONLY", + "prior_verified_output": {}, + "prior_verified_evidence": [], + "evidence_gaps": [ + { + "claim_id": "claim-2", + "fact": "库存服务响应时间升高", + "verification": "no_evidence", + "reason": "缺少数据库和下游依赖证据" + } + ], + "completed_queries": [], + "constraints": { + "max_retry": 1, + "do_not_repeat_successful_queries": true, + "only_execute_incremental_queries": true, + "preserve_prior_verified_claims": true + } +} +``` + +如果 LOW_CONFID 无法提取出有效 `evidence_gaps`,则不触发补查。工具选择、查询范围和工具可用性判断继续由第二轮 Planner / Executor 负责,Verifier 只判断证据是否支持结论。 + +Planner 第二轮使用 `EVIDENCE_GAP_ONLY` 模式,只读取 `evidence_gaps`、`completed_queries` 和必要的已确认事实摘要,生成增量补证据计划,不允许重新开始完整诊断、扩大服务范围或重复已经成功执行的查询。基础 Planner Prompt 和 Verifier 输出协议保持不变,Planner Adapter 允许注入这段编排模式约束。 + +第二轮 Executor 使用“增量执行、完整输出”策略: + +- `prior_verified_output` 和 `prior_verified_evidence` 作为只读可信输入传给 Executor。 +- Executor 只执行 Planner 新增的查询,不重复 `completed_queries`。 +- Executor 最终输出完整 `executor_evidence_v2` 快照,包含应保留的第一轮可信 claims 和本轮新增 claims,而不是只输出本轮增量。 +- Gatekeeper 对第二轮完整快照中的全部 binding 重新验真,Verifier 只消费新一轮生成的完整 `verified_executor_output` 和 `verified_evidence`。 +- Java 编排层不对两轮 claim 文本做语义合并,避免通过代码错误处理重复、冲突和包含关系。 + +### 4.2 Planner 只允许一次无副作用技术重试 + +Planner 不调用工具,因此模型超时或输出格式非法时,允许使用相同业务输入进行一次技术重试。技术重试不代表重新诊断,也不允许改变问题范围。 + +Planner 状态定义: + +| 状态 | 含义 | 路由 | +|---|---|---| +| `COMPLETED` | 输出符合 Planner JSON 契约 | Executor | +| `INVALID_OUTPUT` | 输出为空、JSON 非法或缺少必要计划结构 | 本 Planner 阶段首次出现时重试一次,否则 Fallback | +| `RETRYABLE_FAILED` | 模型超时或明确的临时调用失败 | 本 Planner 阶段首次出现时重试一次,否则 Fallback | +| `NON_RETRYABLE_FAILED` | 配置、认证或其他明确不可重试失败 | 直接 Fallback | + +约束: + +- 每次进入 Planner 阶段最多进行一次技术重试。 +- 从 Prepare Evidence Retry Node 重新进入 Planner 时,开始新的 Planner 阶段,并重置当前阶段的 `planner_retry_count`。 +- Planner 技术重试使用原始输入和固定格式提醒,不注入新的诊断事实。 +- `planner_retry_count` 与 `evidence_retry_count` 分开维护和审计。 +- Planner 不因计划“看起来不够好”进行语义重试;只有明确执行失败或输出契约失败才能重试。 + +### 4.3 Verifier 与 Composer 各允许一次无副作用技术重试 + +Verifier 和 Composer 都不调用工具,且重试输入可以固定,因此允许对模型临时失败或输出格式非法进行一次技术重试: + +```text +Verifier INVALID_OUTPUT / RETRYABLE_FAILED +-> 使用相同 verified_executor_output + verified_evidence 重试一次 +-> 不重新执行 Gatekeeper、Executor 或工具 + +Composer INVALID_OUTPUT / RETRYABLE_FAILED +-> 使用相同 effective_verdict + allowed claims 重试一次 +-> 不重新执行 Verifier 或前序节点 +``` + +第二次技术失败或 `NON_RETRYABLE_FAILED` 的处理: + +- Verifier 进入前置验证无法完成的固定 Fallback,不输出未经 Verifier 允许的 Executor claim。 +- Composer 使用已经过 Verifier 允许的材料进入确定性安全模板。 + +`verifier_retry_count`、`composer_retry_count` 和 `evidence_retry_count` 分开维护,技术重试不得增加补证据轮次。 --- @@ -189,18 +266,28 @@ Planner 第二轮使用 `EVIDENCE_GAP_ONLY` 模式,只生成增量补证据计 ```text Executor -> Gatekeeper - -> PASS / LOW_CONFID:进入 Verifier + -> PASS:构造已验真 Verifier 输入后进入 Verifier + -> LOW_CONFID 且存在通过 binding:过滤失败 binding,构造已验真 Verifier 输入后进入 Verifier -> REJECT:直接 Fallback ``` -Fallback 最终表达只保留: +Executor 输出尚未经过 Verifier 时,如果因为 `INVALID_OUTPUT`、Gatekeeper REJECT 或零条可信 binding 进入 Fallback,固定表达不得输出任何 Executor claim。即使 Gatekeeper 结果中同时存在少量通过的 binding,也不能假设整段 claim 文本都被这些 binding 支撑。 -最终表达只保留: +此前置验证失败 Fallback 只保留: -- 已确认且能够独立验证的事实。 -- 无法验证的证据引用问题。 -- 当前诊断限制。 -- 建议人工检查的下一步。 +- 当前诊断未通过结构或证据引用校验。 +- 工具调用成功、失败和阻断情况的代码汇总,不包含工具原文。 +- 当前无法给出可信根因。 +- 建议人工查看 Trace 或补充证据。 + +示例: + +```text +本次诊断已完成部分证据采集,但诊断结果未通过证据引用校验, +当前无法给出可信根因。建议结合 Trace 中的工具调用记录进行人工复核。 +``` + +Composer 在已经取得 Verifier 允许材料后执行失败,仍可使用现有基于 VerifierDecision 的安全模板降级;它不属于上述“未经过 Verifier 的前置验证失败”。 --- @@ -248,7 +335,26 @@ Trace 至少记录: - 跳转 Fallback 的原因。 - Gatekeeper、Verifier 未执行的原因。 -Executor 原始输出只允许保留在受控审计中,不得直接作为用户答案。 +Executor 原始输出只允许保留在受控审计中,不得直接作为用户答案。固定 Fallback 不得从无法解析的 Executor 输出中提取或展示任何 claim。 + +#### 5.1.1 Executor 状态边界 + +Executor 状态表达是否完成输出契约,不表达所有工具调用是否成功: + +| 状态 | 定义 | 路由 | +|---|---|---| +| `COMPLETED` | 返回合法 `executor_evidence_v2`,包括合法 no-evidence 或工具限制说明 | Gatekeeper | +| `INVALID_OUTPUT` | Agent 已返回,但输出为空或无法解析为最小结构 | 固定 Fallback | +| `TOOL_BLOCKED` | 工具层明确返回无权限、工具不存在或能力禁止,并且 Executor 未形成合法结构化输出 | 固定 Fallback | +| `FAILED` | 模型调用、Agent 框架或其他非工具阻断异常,并且未形成合法结构化输出 | 固定 Fallback | + +边界规则: + +- 工具查询返回空数据属于合法 no-evidence,不是 `TOOL_BLOCKED`。 +- 单次或多次工具超时、报错后,只要 Executor 最终输出合法结构,状态仍为 `COMPLETED`,工具失败作为证据限制进入 Gatekeeper / Verifier。 +- 多个工具失败但 Executor 能合法表达 no-evidence 和诊断限制时,状态仍为 `COMPLETED`。 +- 只有工具层明确阻止执行且没有合法 Executor 输出时,才标记 `TOOL_BLOCKED`。 +- `INVALID_OUTPUT`、`TOOL_BLOCKED` 和 `FAILED` 本期均不重试 Executor。 --- @@ -276,12 +382,50 @@ Verifier Input Builder 必须: - 删除失败 binding。 - 没有有效 binding 的 claim 不得作为确认性 claim 进入 Verifier。 - 不向 Verifier 传递 Executor 未经验真的自由文本作为事实来源。 -- 将 `verifier_verdict_ceiling` 设置为 `LOW_CONFID`。 +- 从通过的 `checked_bindings` 中提取 `claim_id`、`source_invocation_id`、`tool_name`、`raw_path` 和 `matched_text`,构造 `verified_evidence`。 +- 不向 Verifier 传递当前 run 的完整 `tool_trace_summary`;Verifier 只能读取 `verified_evidence`。 +- Gatekeeper PASS 时将 `verifier_verdict_ceiling` 设置为 `PASS`;Gatekeeper LOW_CONFID 时设置为 `LOW_CONFID`。 -即使模型 Verifier 返回 PASS,编排层也必须将有效 verdict 限制为 LOW_CONFID。 +即使模型 Verifier 返回 PASS,编排层也必须将 `effective_verdict` 限制为 LOW_CONFID。 LOW_CONFID 进入 Verifier 的目标是保留部分真实证据,而不是让 Verifier 修复 Gatekeeper 失败。 +Gatekeeper PASS 也必须经过 Verifier Input Builder。PASS 只表示所有 binding 的引用真实性检查通过,不代表 Verifier 可以跳过证据投影或查看未被 Executor 引用的其他工具结果。 + +### 5.3 Gatekeeper 与 Verifier 状态标准化 + +Gatekeeper 原始审计结果和 Graph 路由状态分开保存: + +```text +gatekeeper_result.status=pass +-> gatekeeper_status=PASS + +gatekeeper_result.status=fail + severity=low_confid +-> gatekeeper_status=LOW_CONFID + +gatekeeper_result.status=fail + severity=reject +-> gatekeeper_status=REJECT +``` + +Gatekeeper 原始结果缺失、无法识别或发生内部错误时,按安全默认映射为 `REJECT`。 + +Verifier 同样分离执行状态和诊断结论: + +- `verifier_status` 使用 COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED,表示节点是否正常完成输出契约以及失败是否可进行一次技术重试。 +- `verifier_model_verdict` 保存模型原始 PASS / LOW_CONFID / REJECT。 +- `effective_verdict` 应用 `verifier_verdict_ceiling` 后生成,并作为 Graph 路由和 Composer 的唯一 verdict 输入。 +- 任何执行失败状态都只能出现在 `verifier_status`,不得作为诊断 verdict。 + +例如模型返回 PASS,但 Gatekeeper LOW_CONFID 将 ceiling 设置为 LOW_CONFID 时: + +```text +verifier_model_verdict=PASS +verifier_verdict_ceiling=LOW_CONFID +effective_verdict=LOW_CONFID +``` + +持久化时,现有 `self_evaluation.verifier_evaluation.verdict` 继续保存 `effective_verdict`;模型原始 verdict 可作为 `model_verdict` 审计字段保存,不参与下游路由。 + --- ## 6. Graph State 最小契约 @@ -292,19 +436,28 @@ Graph State 保存跨节点需要的结构化状态,不复制完整工具原 |---|---|---| | `diagnosis_context` | 当前 Query、history、sessionId/runId 等统一上下文 | Replace | | `planner_plan` | Planner 结构化计划 | Replace | -| `planner_status` | Planner 执行状态 | Replace | +| `planner_status` | COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED | Replace | +| `planner_retry_count` | 当前 Planner 阶段的技术重试次数;进入补证据 Planner 阶段时重置 | Replace | | `planner_mode` | NORMAL / EVIDENCE_GAP_ONLY | Replace | | `executor_output` | `executor_evidence_v2` | Replace | | `executor_status` | COMPLETED / INVALID_OUTPUT / TOOL_BLOCKED / FAILED | Replace | | `gatekeeper_result` | 当前轮验真结果 | Replace | | `gatekeeper_status` | PASS / LOW_CONFID / REJECT | Replace | | `verified_executor_output` | 只保留 Gatekeeper 通过 binding 的 Verifier 输入 | Replace | +| `verified_evidence` | 从通过 binding 的 `matched_text` 构造的最小证据集合 | Replace | | `verified_binding_count` | 当前可供 Verifier 使用的 binding 数量 | Replace | | `verifier_verdict_ceiling` | PASS 或 LOW_CONFID | Replace | | `verifier_output` | Verifier 结构化结果 | Replace | -| `verdict` | PASS / LOW_CONFID / REJECT / FAILED | Replace | +| `verifier_status` | COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED | Replace | +| `verifier_retry_count` | Verifier 固定输入技术重试次数 | Replace | +| `verifier_model_verdict` | Verifier 模型原始 PASS / LOW_CONFID / REJECT | Replace | +| `effective_verdict` | 应用 Gatekeeper ceiling 后的 PASS / LOW_CONFID / REJECT | Replace | +| `composer_output` | Composer 结构化输出 | Replace | +| `composer_status` | COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED | Replace | +| `composer_retry_count` | Composer 固定输入技术重试次数 | Replace | | `evidence_retry_count` | 补证据轮次 | Replace | -| `retry_context` | missing evidence 和补查约束 | Replace | +| `retry_context` | 从 `facts_checked` 提取的 `evidence_gaps` 和补查约束 | Replace | +| `orchestration_events` | 节点执行结果、稳定 reason code 和 attempt 的有界运行时事件 | Append | | `final_answer` | 最终用户表达 | Replace | | `failure_reason` | 当前确定性失败原因 | Replace | @@ -312,6 +465,7 @@ Graph State 保存跨节点需要的结构化状态,不复制完整工具原 - `agent_step` 和 `tool_invocation` 继续作为完整 Trace 数据源。 - Graph State 只保存控制流程所需的最新状态。 +- `orchestration_events` 是例外的有界追加状态,用于保留 Java Node 和 Agent Node 的实际执行路径;它不包含 Prompt、工具原文或模型输出。 - `no_evidence` 属于合法 Executor 输出,不等于 `FAILED`,仍然进入 Gatekeeper 和 Verifier。 --- @@ -369,9 +523,9 @@ Graph State 和 Agent 输入契约稳定后,再评估直接使用 `asNode(fals | 节点 | 允许读取 | 不允许直接读取 | |---|---|---| | Planner | query、history、retry context、Skill metadata | Executor、Gatekeeper、Verifier 的历史输出 | -| Executor | query、planner plan、selected skill、executor retry context | Verifier 结论、其他轮次未筛选 messages | +| Executor | query、planner plan、selected skill、executor retry context;补证据阶段可读取 prior verified output / evidence | Verifier 自由推理文本、失败 binding、其他轮次未筛选 messages | | Gatekeeper | executor structured output、当前 run 的 tool invocation | Planner 思考、其他 run 工具记录 | -| Verifier | query、executor structured output、gatekeeper result、verified tool trace | Planner 计划、Executor 思考、未经 Gatekeeper 验真的自由文本 | +| Verifier | query、verified executor output、gatekeeper result、verified evidence | Planner 计划、Executor 思考、完整 tool trace、未经 Gatekeeper 验真的自由文本 | | Composer | allowed claims、missing info、recommended actions | 原始工具结果、Executor 原始答案和未允许 claims | 实现约束: @@ -387,7 +541,7 @@ Graph State 和 Agent 输入契约稳定后,再评估直接使用 `asNode(fals **决策状态**:已确认,2026-07-16。 -Graph 路由记录不写入 `self_evaluation`。本期在 `diagnosis_run` 新增 nullable JSON 字段 `orchestration_trace`,用于保存当前 run 的紧凑编排摘要。 +Graph 路由记录不写入 `self_evaluation`。本期在 `diagnosis_run` 新增 nullable JSON 字段 `orchestration_trace`,用于保存当前 run 的紧凑编排摘要。数据库列允许 nullable 仅用于安全执行增量迁移,所有新 StateGraph Chat run 在应用契约上必须写入非空值。 职责边界: @@ -430,9 +584,38 @@ tool_invocation - 只写入 `diagnosis_run`,不写入会话级 `diagnosis_session`,因为 Graph 执行边界是 `runId`。 - 使用稳定的 `reason_code`,不保存完整 Prompt、模型思考、工具原文或 Graph State 快照。 - `transitions` 受 Graph 最大循环次数约束,不作为无限增长的事件日志。 -- Trace API 将其作为与 `self_evaluation` 平级的可选字段返回;历史 run 可以为 `null`。 +- Trace API 只在 `run.orchestrationTrace` 返回解析后的 JSON 对象,不在顶层或兼容 `session` 投影中重复返回,也不增加 raw 字段。 +- 本期不为历史 run 回填或设计兼容读取行为;新 StateGraph Chat run 的 `run.orchestrationTrace` 必须非空。 - 第一版不新增 orchestration 明细表,后续只有在需要跨 run 检索单次节点事件时再单独评估。 +运行时来源: + +```text +每个 Graph Node 完成或捕获可处理失败 +-> 向 orchestration_events 追加 node / outcome / reason_code / attempt +-> Graph 正常结束或进入外层异常处理 +-> 按事件顺序生成 transitions、final_node 和 termination_reason +-> 持久化 diagnosis_run.orchestration_trace +``` + +事件最小结构: + +```json +{ + "node": "gatekeeper", + "outcome": "REJECT", + "reason_code": "gatekeeper_reject", + "attempt": 1 +} +``` + +实现约束: + +- 使用 Graph State 的追加策略保存事件,不通过应用日志反推路径。 +- 每个节点每次 attempt 最多追加一条终态事件;事件总数同时受一次补查上限和 Graph recursion limit 约束。 +- 节点 Adapter 需要捕获可处理异常并追加失败事件;外层异常处理对已产生的事件进行 best-effort 持久化。 +- 最终 `transitions` 由相邻事件推导,不在 State 中同时维护第二份重复路径结构。 + --- ## 8. 分阶段实施计划 @@ -463,16 +646,25 @@ tool_invocation - 创建诊断 Graph State 和状态常量。 - 创建 Graph 拓扑。 +- 创建 `orchestration_events` 追加策略和最终 trace 构造器。 - 创建 Fake Planner、Executor、Gatekeeper、Verifier、Composer。 - 为所有条件边编写路由测试。 完成标准: +- Planner INVALID_OUTPUT / RETRYABLE_FAILED 在当前 Planner 阶段最多重试一次。 +- Planner NON_RETRYABLE_FAILED 或第二次技术失败直接进入 Fallback。 +- Verifier 和 Composer 的 INVALID_OUTPUT / RETRYABLE_FAILED 各最多技术重试一次。 +- Verifier 和 Composer 的 NON_RETRYABLE_FAILED 或第二次技术失败进入各自安全降级路径。 - Executor FAILED 后不调用 Gatekeeper、Verifier。 - Executor INVALID_OUTPUT 直接进入固定 Fallback,不触发任何模型重试。 +- Executor TOOL_BLOCKED 只有在工具层明确阻断且没有合法结构化输出时成立,并直接进入 Fallback。 +- 工具空结果或工具失败后仍形成合法结构时,Executor 状态为 COMPLETED 并继续 Gatekeeper。 - 任意 Gatekeeper REJECT 直接进入 Fallback,不调用 Verifier。 +- Executor INVALID_OUTPUT、Gatekeeper REJECT 和零条可信 binding 的 Fallback 不输出任何 Executor claim。 - LOW_CONFID 只允许一次 Planner 补证据。 - Graph 不存在无限循环。 +- 每条测试路径生成与实际节点顺序一致的 orchestration events 和 transitions。 ## 阶段 2:接入真实节点 @@ -482,20 +674,28 @@ tool_invocation 任务: - 创建 Planner、Executor、Verifier、Composer Node Adapter。 +- Planner Adapter 解析现有 Planner JSON 契约,并区分 INVALID_OUTPUT、RETRYABLE_FAILED 和 NON_RETRYABLE_FAILED。 - 创建显式 Gatekeeper Node。 -- 创建 Verifier Input Builder Node,过滤未通过的 binding。 -- 创建 Evidence Retry Prepare Node。 -- 创建固定 Fallback Node。 -- 保持现有 Prompt、工具和证据协议不变。 +- Gatekeeper Node 保留原始 result,并标准化生成 PASS / LOW_CONFID / REJECT 路由状态。 +- 创建 Verifier Input Builder Node,过滤未通过的 binding,并仅从通过 binding 的 `matched_text` 构造 `verified_evidence`。 +- 创建 Evidence Retry Prepare Node,从现有 `facts_checked` 提取结构化 `evidence_gaps`。 +- 创建固定 Fallback Node,并区分前置验证失败与 Composer 已取得 Verifier 安全材料后的降级输入。 +- 保持现有基础 Prompt、工具和证据协议不变;允许 Planner Adapter 注入 `EVIDENCE_GAP_ONLY` 编排约束。 完成标准: - Agent Hook 和 ToolCallback 正常工作。 - Gatekeeper 只执行一次,不再由 Verifier Hook 重复触发。 - no-evidence 继续进入 Gatekeeper 和 Verifier。 +- Gatekeeper PASS 和可继续的 LOW_CONFID 都必须经过 Verifier Input Builder。 - Gatekeeper LOW_CONFID 只向 Verifier 提供已验真 binding,并限制 verdict 上限。 -- Verifier LOW_CONFID 只有存在可执行缺口时才返回 Planner。 +- Verifier 不再接收完整 `tool_trace_summary`,只能接收 `verified_executor_output` 和 `verified_evidence`。 +- Verifier Adapter 分开输出 `verifier_status`、`verifier_model_verdict` 和应用 ceiling 后的 `effective_verdict`。 +- Verifier 和 Composer Adapter 使用固定输入实现各一次技术重试,且不得重新执行任何前序节点。 +- Verifier LOW_CONFID 只有能够从 `facts_checked` 提取有效 `evidence_gaps` 时才返回 Planner。 - 第二轮 Planner 只生成 `EVIDENCE_GAP_ONLY` 增量计划。 +- 第二轮 Executor 只执行增量查询,但输出包含第一轮可信 claims 和新增 claims 的完整 `executor_evidence_v2` 快照。 +- 第二轮 Gatekeeper 对完整快照中的全部 binding 重新验真,不复用第一轮 Gatekeeper verdict。 ## 阶段 3:替换 ChatService 编排 @@ -510,13 +710,16 @@ tool_invocation - 将 Graph 的节点跳转、reason code、重试和终止摘要持久化到当前 run。 - 保留 Run 创建、状态更新、耗时、Token、工具数和 Eval 调用。 - 将 Graph 最终状态映射为现有 `ChatResult`。 +- 按执行生命周期回填 `diagnosis_run.status`,不使用 status 表达诊断置信度。 完成标准: - `/api/chat` 请求和响应协议不变。 - `diagnosis_run.agent_flow` 仍为 `CHAT`。 - Trace 明细继续归属于当前 runId。 -- Trace API 将 `orchestration_trace` 作为与 `self_evaluation` 平级的可选字段返回。 +- Trace API 在 `run.orchestrationTrace` 返回当前 run 的非空编排摘要,不在其他层级重复返回。 +- 正常结束和可处理异常路径都能持久化已产生的 orchestration events 摘要。 +- Composer、LOW_CONFID、Verifier REJECT 和固定安全 Fallback 只要成功生成安全响应,Run 终态均为 SUCCESS;只有无法生成安全响应的未处理失败才为 FAILED。 - Executor 失败路径不产生 Verifier Agent step。 ## 阶段 4:创建新测试体系 @@ -534,17 +737,32 @@ tool_invocation - PASS 正常路径。 - Planner 失败。 +- Planner INVALID_OUTPUT / RETRYABLE_FAILED 技术重试后成功。 +- Planner NON_RETRYABLE_FAILED 和第二次技术失败直接降级。 +- Verifier INVALID_OUTPUT / RETRYABLE_FAILED 技术重试后成功。 +- Verifier 第二次技术失败或 NON_RETRYABLE_FAILED 进入无 Executor claim 的固定 Fallback。 +- Composer INVALID_OUTPUT / RETRYABLE_FAILED 技术重试后成功。 +- Composer 第二次技术失败或 NON_RETRYABLE_FAILED 使用 Verifier 安全材料进行确定性模板降级。 - Executor FAILED / TOOL_BLOCKED / INVALID_OUTPUT。 +- 工具返回空数据但 Executor 合法输出 no-evidence。 +- 工具调用失败但 Executor 仍形成合法结构并继续 Gatekeeper。 - 合法 no-evidence。 - Gatekeeper REJECT 直接降级。 +- Gatekeeper REJECT 降级不展示部分通过 binding 对应的 Executor claim。 - Gatekeeper LOW_CONFID 且存在已验真 binding。 - Gatekeeper LOW_CONFID 且不存在已验真 binding。 +- Gatekeeper PASS 路径同样只向 Verifier 提供已引用且验真的 evidence。 +- Verifier 输入不包含未被通过 binding 引用的工具结果。 +- Gatekeeper 原始结果缺失或无法识别时安全映射为 REJECT。 +- Verifier 执行失败只写入 `verifier_status`,不得把任何失败状态写成诊断 verdict。 - Verifier LOW_CONFID 补证据。 +- 补证据第二轮不重复成功查询,并保留第一轮可信 claims 进入最终完整快照。 +- 第二轮完整快照重新经过 Gatekeeper,失败 binding 不因第一轮已通过而跳过校验。 - Gatekeeper ceiling 导致的 LOW_CONFID 不触发补证据。 -- LOW_CONFID 缺少可执行 missing evidence 时不触发补证据。 +- LOW_CONFID 无法从 `facts_checked` 提取有效 `evidence_gaps` 时不触发补证据。 - 第二次 LOW_CONFID 不再补证据。 - Verifier REJECT 安全表达。 -- Composer 失败固定降级。 +- Composer 技术重试耗尽后固定降级。 - 同 session 多 run 隔离。 旧测试处理: @@ -615,8 +833,9 @@ Eval baseline ### Trace API 加法式变化 -- Trace 聚合响应新增与 `self_evaluation` 平级的可选字段 `orchestration_trace`。 -- 现有字段语义不变,历史 run 或未经过 StateGraph 的 run 返回 `null`。 +- Trace 聚合响应的 `run` 对象新增 `orchestrationTrace` 字段。 +- 新 StateGraph Chat run 必须返回非空的解析后 JSON 对象;不增加顶层、`session` 投影或 raw 重复字段。 +- 本期不为历史 run 回填数据,也不增加历史兼容读取分支。 - 这是有意的内部审计持久化和 Trace 查询协议扩展,不影响 `/api/chat` 消费方。 ### 明确的内部行为变化 @@ -626,11 +845,24 @@ Eval baseline - Agent 调用顺序不再固定。 - Gatekeeper REJECT 直接安全降级,不进行自动修复和重试。 - Gatekeeper LOW_CONFID 只在存在已验真 binding 时进入 Verifier,且 verdict 最高为 LOW_CONFID。 -- LOW_CONFID 根据 missing evidence 返回 Planner,而不是无差别完整重跑。 +- LOW_CONFID 根据从 `facts_checked` 提取的 `evidence_gaps` 返回 Planner,而不是无差别完整重跑。 - 第二轮 Planner 使用增量补证据模式,不重新执行完整 Playbook。 - Agent step 数量、Token 和耗时可能减少。 - Trace 需要能解释条件边选择和终止原因。 +### Run 状态与诊断质量分离 + +`diagnosis_run.status` 只表达执行生命周期,不表达结论可信度: + +| 场景 | Run status | 质量表达 | +|---|---|---| +| Graph 正常到达 Composer | SUCCESS | Verifier verdict | +| LOW_CONFID 或 Verifier REJECT 后生成安全答案 | SUCCESS | Verifier verdict | +| 按设计进入固定 Fallback 并成功生成安全答案 | SUCCESS | `orchestrationTrace.degraded=true` 和 termination reason | +| Graph 未处理异常、持久化失败或无法生成任何安全响应 | FAILED | failure reason 和已有 orchestration events | + +本期不新增 `DEGRADED` Run status,避免将执行状态和诊断质量混在同一个字段中。 + 这些是有意的编排行为变化,不是对外协议变化。 --- @@ -639,19 +871,38 @@ Eval baseline ### 编排 +- [ ] 每次进入 Planner 阶段时,INVALID_OUTPUT / RETRYABLE_FAILED 最多触发一次技术重试。 +- [ ] Planner NON_RETRYABLE_FAILED 或当前阶段第二次技术失败直接进入 Fallback。 +- [ ] Planner 技术重试不增加 `evidence_retry_count`,补证据重新进入 Planner 时重置当前阶段的 `planner_retry_count`。 - [ ] Executor FAILED / TOOL_BLOCKED 后不会执行 Gatekeeper 和 Verifier。 - [ ] Executor INVALID_OUTPUT 不重试,不执行 Gatekeeper、Verifier 和模型 Composer。 +- [ ] TOOL_BLOCKED 只用于工具层明确阻断且不存在合法 Executor 输出的场景。 +- [ ] 工具空结果或工具失败后仍形成合法 Executor 输出时状态为 COMPLETED,并继续 Gatekeeper。 - [ ] Executor 合法 no-evidence 会继续执行 Gatekeeper 和 Verifier。 - [ ] Gatekeeper REJECT 直接进入 Fallback,不执行 Verifier。 - [ ] Gatekeeper LOW_CONFID 且零条已验真 binding 时直接进入 Fallback。 - [ ] Gatekeeper LOW_CONFID 且存在已验真 binding 时,Verifier 只接收通过校验的 binding。 -- [ ] Gatekeeper LOW_CONFID 路径的最终 verdict 不得升级为 PASS。 +- [ ] Gatekeeper PASS 和可继续的 LOW_CONFID 都经过 Verifier Input Builder。 +- [ ] Verifier 只接收通过 binding 对应的 `verified_evidence`,不接收完整 `tool_trace_summary`。 +- [ ] 未被 Executor 引用或未通过 Gatekeeper 的工具结果不能进入 Verifier 输入。 +- [ ] Gatekeeper LOW_CONFID 路径的 `effective_verdict` 不得升级为 PASS。 +- [ ] Gatekeeper 原始 pass/fail + severity 正确标准化为 PASS / LOW_CONFID / REJECT,未知状态安全映射为 REJECT。 +- [ ] Verifier 执行状态与诊断 verdict 分离,任何失败状态不得出现在 model/effective verdict 中。 +- [ ] Composer 和 Graph 条件边只读取 `effective_verdict`。 +- [ ] Verifier INVALID_OUTPUT / RETRYABLE_FAILED 使用相同 verified input 最多技术重试一次,且不重新执行 Gatekeeper、Executor 或工具。 +- [ ] Composer INVALID_OUTPUT / RETRYABLE_FAILED 使用相同安全输入最多技术重试一次,且不重新执行 Verifier 或前序节点。 +- [ ] Verifier 第二次技术失败或 NON_RETRYABLE_FAILED 的 Fallback 不输出 Executor claim。 +- [ ] Composer 第二次技术失败或 NON_RETRYABLE_FAILED 使用确定性安全模板。 +- [ ] `verifier_retry_count`、`composer_retry_count` 和 `evidence_retry_count` 互相独立。 - [ ] Verifier LOW_CONFID 最多触发一次 Planner 补证据。 -- [ ] LOW_CONFID 补证据循环受次数和 Run 总预算限制。 +- [ ] LOW_CONFID 补证据循环受一次补查上限和 Graph recursion limit 限制。 - [ ] Gatekeeper verdict ceiling 导致的 LOW_CONFID 不触发补证据。 -- [ ] 不存在可执行 missing evidence 时不触发补证据。 +- [ ] 无法从 `facts_checked` 提取有效 `evidence_gaps` 时不触发补证据。 - [ ] 第二轮 Planner 只输出增量计划,不扩大诊断范围或重复成功查询。 -- [ ] Composer 失败使用固定模板结束。 +- [ ] 第二轮 Executor 只执行增量查询,但输出完整 `executor_evidence_v2` 快照,而不是仅输出新增片段。 +- [ ] 第二轮完整快照包含需要保留的第一轮可信 claims,并由 Gatekeeper 对全部 binding 重新验真。 +- [ ] Java 编排层不对两轮 claim 文本进行语义合并。 +- [ ] Composer 技术重试耗尽或不可重试失败时使用固定模板结束。 ### 证据和安全 @@ -659,6 +910,8 @@ Eval baseline - [ ] Verifier 只消费已验真证据。 - [ ] no-evidence 不得表达为已排除或问题不存在。 - [ ] REJECT 降级不泄漏 Executor 原始答案和未验证根因。 +- [ ] Executor INVALID_OUTPUT、Gatekeeper REJECT 和零条可信 binding 的固定 Fallback 不输出任何 Executor claim。 +- [ ] 前置验证失败 Fallback 只展示校验状态、工具执行概况、诊断限制和人工复核建议。 ### 数据与审计 @@ -666,9 +919,13 @@ Eval baseline - [ ] Agent step、tool invocation 和 self_evaluation 仍绑定正确 runId。 - [ ] `orchestration_trace` 只写入当前 diagnosis run,不污染其他 run 或 session 级数据。 - [ ] `orchestration_trace` 不包含 Prompt、模型思考、工具原文和 Graph State 快照。 +- [ ] `orchestration_trace.transitions` 由有界 `orchestration_events` 生成,与实际节点执行顺序一致。 +- [ ] 可处理异常发生时,已经产生的 orchestration events 能够 best-effort 写入当前 run。 - [ ] Trace 能展示实际节点路径、重试原因和终止原因。 -- [ ] 历史 run 的 `orchestration_trace=null` 时 Trace API 仍能正常返回。 +- [ ] 每个新 StateGraph Chat run 的 `run.orchestrationTrace` 非空,且顶层和兼容 `session` 投影不重复该字段。 - [ ] Run 最终状态、答案、耗时、Token 和工具调用数正确回填。 +- [ ] 所有成功生成安全响应的终止路径将 Run 标记为 SUCCESS,并通过 verdict 或 `orchestrationTrace.degraded` 表达质量。 +- [ ] 只有未处理异常、持久化失败或无法生成安全响应时将 Run 标记为 FAILED。 ### 工程质量 @@ -676,7 +933,7 @@ Eval baseline - [ ] `ChatServiceSequentialAgentTest` 已由新测试替换。 - [ ] 不保留长期重复的 Sequential 和 Graph 两套实现。 - [ ] 数据库 schema 仅新增 `diagnosis_run.orchestration_trace` nullable JSON 字段。 -- [ ] `/api/chat` 和证据协议不变;Trace API 仅新增可选的 `orchestration_trace` 字段。 +- [ ] `/api/chat` 和证据协议不变;Trace API 仅在 `run` 对象新增必有的 `orchestrationTrace` 字段。 --- @@ -698,7 +955,7 @@ Eval baseline 风险:Verifier LOW_CONFID 持续返回 Planner,形成重复规划和工具调用。 -缓解:保留当前最多一次补证据约束,并设置 Run 级总预算与 Graph recursion limit。 +缓解:保留当前最多一次补证据约束,并设置 Graph recursion limit 作为最终保险。 ### Trace 与 Graph Checkpoint 语义混淆 @@ -720,7 +977,7 @@ Eval baseline - 并行工具或并行 Agent。 - EvidenceRepairNode 和 Gatekeeper REJECT 自动修复。 - Gatekeeper REJECT 返回 Executor 的重试回边。 -- 修改 Prompt 业务语义。 +- 除 Planner Adapter 注入 `EVIDENCE_GAP_ONLY` 编排约束外,修改 Prompt 核心业务语义。 - 修改 `executor_evidence_v2`、Verifier、Composer 协议。 - 除 `diagnosis_run.orchestration_trace` 外的其他数据库表或字段。 @@ -735,14 +992,25 @@ Eval baseline - 第一阶段保留现有 ReactAgent,通过 Adapter Node 调用。 - 父 Graph State 不直接传给 ReactAgent,使用每个 Agent 独立的 Input DTO 做白名单投影。 - 第一阶段不直接依赖 `ReactAgent.asNode(...)` 实现父子编排。 +- Planner 仅对 INVALID_OUTPUT 或 RETRYABLE_FAILED 进行每阶段最多一次技术重试;NON_RETRYABLE_FAILED 和第二次技术失败直接 Fallback,且该计数与补证据轮次隔离。 - Gatekeeper REJECT 本期不重试,直接进入 Fallback。 - 本期不增加 EvidenceRepairNode。 - Executor INVALID_OUTPUT 本期不重试,直接进入固定 Fallback。 +- Executor 状态按输出契约定义:合法结构统一为 COMPLETED;只有明确工具阻断且无合法输出为 TOOL_BLOCKED,其他无合法输出的执行异常为 FAILED,三类失败均不重试。 +- Executor INVALID_OUTPUT、Gatekeeper REJECT 或零条可信 binding 进入前置验证失败 Fallback 时,不展示任何 Executor claim;部分通过 binding 不用于拼接用户答案。 - Gatekeeper LOW_CONFID 有已验真 binding 时进入 Verifier,零条时直接 Fallback。 - Gatekeeper LOW_CONFID 路径的最终 verdict 上限为 LOW_CONFID。 +- Gatekeeper 原始 result 与标准化 `gatekeeper_status` 分开保存;Verifier 使用 `verifier_status`、`verifier_model_verdict` 和 `effective_verdict` 分离执行状态、模型结论和编排生效结论,任何执行失败状态都不作为诊断 verdict。 +- Verifier 和 Composer 对 INVALID_OUTPUT / RETRYABLE_FAILED 各允许一次固定输入技术重试;不得重新执行前序节点,第二次失败或 NON_RETRYABLE_FAILED 进入对应安全降级,并与 `evidence_retry_count` 分开计数。 +- Gatekeeper PASS 和可继续的 LOW_CONFID 都先经过 Verifier Input Builder;Verifier 只接收通过 binding 对应的 `matched_text` 所构成的 `verified_evidence`,不再接收完整 `tool_trace_summary`。 - Verifier LOW_CONFID 的补证据路径返回 Planner,并使用 `EVIDENCE_GAP_ONLY` 增量规划模式。 -- Gatekeeper ceiling、无可执行缺口、无预算或已经补查时不触发 LOW_CONFID 重试。 +- Gatekeeper ceiling、无法从 `facts_checked` 提取有效缺口或已经补查时不触发 LOW_CONFID 重试。 +- Verifier 不新增 missing evidence 或工具规划字段;Prepare Evidence Retry Node 将现有 `facts_checked` 转换为结构化 `evidence_gaps`,第二轮 Planner 决定工具和增量查询计划。 +- 补证据采用“Planner 增量规划、Executor 增量查询但完整输出”的策略;第一轮已验证材料作为只读输入传给第二轮 Executor,Java 不做 claim 语义合并,第二轮完整快照重新经过 Gatekeeper 和 Verifier。 - Graph 路由摘要写入独立的 `diagnosis_run.orchestration_trace`,不耦合进 `self_evaluation`。 +- Trace API 只通过 `run.orchestrationTrace` 暴露解析后的编排摘要,不复制到顶层或兼容 `session` 投影;本期不处理历史 run 回填和兼容读取。 +- `diagnosis_run.status` 只表达执行生命周期:安全 Fallback 仍为 SUCCESS 并设置 `orchestrationTrace.degraded=true`;只有无法生成安全响应的未处理失败才为 FAILED,不新增 DEGRADED 状态。 +- Graph State 使用有界追加的 `orchestration_events` 作为路由事实来源,结束时压缩为 `orchestration_trace`,不通过日志反推路径。 - 不保留 Sequential / StateGraph 双链路配置开关;在专用 Git 分支完成实现和验收后,直接以 StateGraph 替换旧复杂诊断编排。 - 实现期间允许代码处于分支内的阶段性状态,但合并前必须删除旧 Sequential 编排和不再使用的迁移代码,不形成长期双轨维护。 diff --git a/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/.archive-ready b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/.archive-ready new file mode 100644 index 0000000..8515d16 --- /dev/null +++ b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/.archive-ready @@ -0,0 +1,3 @@ +ready_at: 2026-07-17 +devflow: devflow/projects/2026-07-17-chat-diagnosis-stategraph-design-freeze +authorization: user-requested-per-stage-archive diff --git a/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/.committed b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/.committed new file mode 100644 index 0000000..20ff779 --- /dev/null +++ b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/.committed @@ -0,0 +1,3 @@ +committed_at: 2026-07-17 +scope: iss-011-stage-0-design-freeze +validation: openspec-strict-pass diff --git a/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/design.md b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/design.md new file mode 100644 index 0000000..bb00b36 --- /dev/null +++ b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/design.md @@ -0,0 +1,226 @@ +## Context + +复杂 Chat 当前由 `ChatController -> ChatService.executeChatWithStrategy -> executeChatComplex` 驱动。`executeChatComplex` 同时负责 Run 生命周期、外层 LOW_CONFID round、`SequentialAgent` 构造、Verifier 解析、Composer、Fallback、持久化和评估;`VerifierInputHook` 又解析 Executor 输出、读取当前 Run 工具摘要、执行 `ExecutorGatekeeperService` 并通过 `VerifierContextHolder` 回传隐式状态。这些职责形成无法精确表达失败恢复位置、条件边和审计路径的隐式状态机。 + +阶段 0 不改变运行时,只冻结后续五个独立 change 必须遵守的架构契约。当前 change 的运行时影响为 L1;冻结目标最终涉及状态机语义、数据库与 Trace API,属于 L4。 + +| 区域 | 当前职责 | 冻结后的职责 | +|---|---|---| +| `ChatController` | 调用 ChatService | 请求/响应协议不变 | +| `ChatService` | Run 生命周期和细粒度隐式状态机 | 只维护 Run 生命周期并调用 Graph orchestrator | +| `SequentialAgent` | 固定跨 Agent 顺序 | 不再用于复杂 Chat 编排 | +| `VerifierInputHook` / `VerifierContextHolder` | 隐式 Gatekeeper、输入构造和跨层回传 | Gatekeeper/投影迁入显式 Node;旧隐式入口删除 | +| `ExecutorGatekeeperService` | 确定性 binding 校验 | 原规则复用,由显式 Gatekeeper Node 单次调用 | +| `DiagnosisRun` / `DiagnosisTraceService` | Run 与 self evaluation 聚合 | 增加 run-scoped orchestration trace | + +## Goals / Non-Goals + +**Goals:** + +- 冻结最小 Graph State 的字段、所有权和更新策略。 +- 冻结完整、有界且可终止的条件边与 retry 语义。 +- 冻结 verified-input、Fallback、Run 状态与审计安全边界。 +- 冻结旧测试替换范围和后续五个独立 change 的交付边界。 +- 提供可被后续 OpenSpec Commit gate 机械对照的设计基线。 + +**Non-Goals:** + +- 本 change 不实现或编译任何 Java/SQL/Prompt/配置变更。 +- 不修改当前主运行时 specs 来宣称 StateGraph 已上线。 +- 不运行 Maven E2E、日志或数据库验收。 +- 不引入 SupervisorAgent、持久 Checkpointer、HITL、并行执行或 AIOps 公共 Graph。 +- 不保留 Sequential/StateGraph 双轨开关。 + +## Decisions + +### 1. StateGraph 只拥有跨节点控制状态 + +StateGraph 负责顺序、条件边、重试、终止和降级;现有 ReactAgent 继续完成语义任务;Gatekeeper、verified-input builder、evidence retry prepare 和固定 Fallback 使用确定性 Java Node。 + +替代方案: + +- 继续在 ChatService 外层叠加 if/for:无法精确恢复失败节点和审计条件边,拒绝。 +- 使用 SupervisorAgent:固定诊断 Pipeline 不需要动态选择专科 Agent,拒绝。 +- 直接把父 State 交给 `ReactAgent.asNode(...)`:存在 messages/outputKey/私有状态泄漏风险,首版拒绝。 + +### 2. 最小 Graph State + +默认 Replace;只有有界 `orchestration_events` 使用 Append。 + +| 字段 | 所有者/用途 | 策略 | +|---|---|---| +| `diagnosis_context` | query、history、sessionId、runId | Replace | +| `planner_plan` | Planner 结构化计划 | Replace | +| `planner_status` | COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED | Replace | +| `planner_retry_count` | 当前 Planner 阶段技术重试次数 | Replace | +| `planner_mode` | NORMAL / EVIDENCE_GAP_ONLY | Replace | +| `executor_output` | 完整 `executor_evidence_v2` | Replace | +| `executor_status` | COMPLETED / INVALID_OUTPUT / TOOL_BLOCKED / FAILED | Replace | +| `gatekeeper_result` | 原始确定性校验结果 | Replace | +| `gatekeeper_status` | PASS / LOW_CONFID / REJECT | Replace | +| `verified_executor_output` | 只保留通过 binding 的 claim 投影 | Replace | +| `verified_evidence` | 从通过 binding 的 `matched_text` 构造 | Replace | +| `verified_binding_count` | 当前可信 binding 数 | Replace | +| `verifier_verdict_ceiling` | PASS 或 LOW_CONFID | Replace | +| `verifier_output` | Verifier 结构化结果 | Replace | +| `verifier_status` | COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED | Replace | +| `verifier_retry_count` | 固定 verified input 技术重试次数 | Replace | +| `verifier_model_verdict` | 模型 PASS / LOW_CONFID / REJECT | Replace | +| `effective_verdict` | 应用 Gatekeeper ceiling 后的 verdict | Replace | +| `composer_output` | Composer 结构化输出 | Replace | +| `composer_status` | COMPLETED / INVALID_OUTPUT / RETRYABLE_FAILED / NON_RETRYABLE_FAILED | Replace | +| `composer_retry_count` | 固定安全输入技术重试次数 | Replace | +| `evidence_retry_count` | 诊断补证据轮次 | Replace | +| `retry_context` | evidence gaps、prior verified data、completed queries、约束 | Replace | +| `orchestration_events` | 每个 node attempt 的有界终态事件 | Append | +| `final_answer` | 最终安全用户表达 | Replace | +| `failure_reason` | 确定性失败 reason code | Replace | + +不保存 Prompt、模型思考、完整工具原文、完整 Graph State 快照或其他 Run 数据。 + +### 3. 完整路由矩阵 + +| From | Outcome / Guard | To | 计数与约束 | +|---|---|---|---| +| START | always | Planner | Planner 阶段 retry count=0 | +| Planner | COMPLETED | Executor | 不增加 retry | +| Planner | INVALID_OUTPUT / RETRYABLE_FAILED 且 count=0 | Planner | 同业务输入重试一次,count=1 | +| Planner | NON_RETRYABLE_FAILED 或技术重试耗尽 | Fallback | 不执行后续 Agent | +| Executor | COMPLETED | Gatekeeper | 合法 no-evidence 也属于 COMPLETED | +| Executor | INVALID_OUTPUT / TOOL_BLOCKED / FAILED | Fallback | Executor 永不重试 | +| Gatekeeper | PASS | Verified Input Builder | ceiling=PASS | +| Gatekeeper | LOW_CONFID 且 verified binding > 0 | Verified Input Builder | ceiling=LOW_CONFID | +| Gatekeeper | REJECT、未知状态或 LOW_CONFID 且 binding=0 | Fallback | 不执行 Verifier | +| Verified Input Builder | completed | Verifier | 只投影通过 binding | +| Verifier | INVALID_OUTPUT / RETRYABLE_FAILED 且 count=0 | Verifier | 相同 verified input 重试一次 | +| Verifier | NON_RETRYABLE_FAILED 或技术重试耗尽 | Fallback | 不输出 Executor claim | +| Verifier | PASS / REJECT | Composer | 使用 effective verdict | +| Verifier | LOW_CONFID,非 ceiling 导致,存在有效 gaps,evidence count=0 | Prepare Evidence Retry | evidence count 增加一次 | +| Verifier | LOW_CONFID 且任一补查条件不满足 | Composer | 不补查 | +| Prepare Evidence Retry | completed | Planner | mode=EVIDENCE_GAP_ONLY;新 Planner 阶段 retry count 重置 | +| Composer | COMPLETED | END | 返回 Composer 安全表达 | +| Composer | INVALID_OUTPUT / RETRYABLE_FAILED 且 count=0 | Composer | 相同 allowed material 重试一次 | +| Composer | NON_RETRYABLE_FAILED 或技术重试耗尽 | Fallback | 可使用 Verifier 已允许材料 | +| Fallback | deterministic answer produced | END | degraded=true | +| 未处理异常/持久化失败/无法生成安全答案 | failure | outer failure handling | Run=FAILED,best-effort 保存已有 events | + +Graph compile 必须设置 recursion limit 作为最终保险;业务循环仍由独立计数显式限制。 + +### 4. 四类计数互相独立 + +- `planner_retry_count`:每次进入 Planner 阶段最多一次;evidence retry 重新进入时重置。 +- `verifier_retry_count`:同一 verified input 最多一次。 +- `composer_retry_count`:同一 allowed material 最多一次。 +- `evidence_retry_count`:整个 Run 最多一次。 + +技术重试不增加 evidence retry,也不得重新执行前序节点或工具。 + +### 5. Executor 状态按输出契约定义 + +合法 `executor_evidence_v2`(包括 no-evidence、空查询结果或工具限制说明)均为 COMPLETED。只有工具层明确禁止且没有合法结构时为 TOOL_BLOCKED;空/非法结构为 INVALID_OUTPUT;其他未形成合法结构的异常为 FAILED。只有 COMPLETED 进入 Gatekeeper,其余直接 Fallback 且不重试。 + +补证据轮只执行增量查询,但输出包含应保留 prior verified claims 的完整快照;Java 不做 claim 文本语义合并,完整快照重新经过 Gatekeeper。 + +### 6. Gatekeeper 与 Verifier 输入边界 + +Gatekeeper 保留原始结果并 fail-closed 标准化为 PASS / LOW_CONFID / REJECT;未知、缺失或无法识别结果映射为 REJECT。 + +PASS 与可继续 LOW_CONFID 都经过 Verified Input Builder。Builder 复用 `checked_bindings`,仅从通过项投影 `claim_id`、`source_invocation_id`、`tool_name`、`raw_path`、`matched_text`;不得传递完整 `tool_trace_summary`、失败 binding 或 Executor 自由文本。LOW_CONFID ceiling 不得被模型 PASS 提升。 + +### 7. 两类安全 Fallback + +**前置验证失败:** Planner/Executor/Verifier 无法形成可信材料,或 Gatekeeper REJECT/零可信 binding。固定表达只包含校验状态、工具执行概况、诊断限制和人工查看 Trace 建议,不含任何 Executor claim。 + +**Composer 后置降级:** 已有 Verifier 允许材料,但 Composer 技术重试耗尽。确定性模板可以使用 allowed claims/missing info/recommended actions,仍不得读取 raw output。 + +成功生成安全答案时 Run=SUCCESS,并用 verdict 或 `orchestrationTrace.degraded=true` 表达质量;只有未处理异常、持久化失败或无法生成安全答案时为 FAILED,不新增 DEGRADED 状态。 + +### 8. Run-scoped 编排审计 + +每个 Node attempt 最多追加一个 `{node,outcome,reason_code,attempt}` 终态 event。按事件顺序压缩为 version、transitions、final_node、termination_reason、degraded、evidence_retry_count。 + +只持久化到当前 `diagnosis_run.orchestration_trace`;`RunnableConfig.threadId=runId`。Trace API 只在 `run.orchestrationTrace` 返回解析对象,不复制到顶层/session/raw 字段。它不替代 self evaluation、AgentStep、ToolInvocation 或 Graph checkpoint。 + +### 9. 测试替换边界 + +| 测试 | 处理 | +|---|---| +| `ChatServiceSequentialAgentTest` | 用 Graph 路由、Node 契约和 Chat 集成测试替换;删除固定顺序断言 | +| `VerifierInputHookTest` | 删除或改写为显式 Gatekeeper/Input Builder 契约测试 | +| `ExecutorGatekeeperServiceTest` | 保留并扩展 checked binding 与未知状态 fail-closed | +| `ChatControllerTest` | 保留,证明 `/api/chat` 兼容 | +| `DiagnosisTraceServiceTest` / Repository tests | 保留并扩展 run-scoped orchestration trace | +| Eval/Composer 安全测试 | 保留,证明 allowed-material、no-evidence 和 REJECT 不退化 | + +阶段 0 无运行时行为,不新增单元测试。阶段 1–4 按风险建立上述测试;阶段 5 统一执行 Maven E2E、日志和数据库验收。 + +### 10. 六个独立 OpenSpec change + +| 阶段 | Change | 唯一交付边界 | +|---|---|---| +| 0 | `chat-diagnosis-stategraph-design-freeze` | 冻结本设计,不改运行时 | +| 1 | `chat-diagnosis-stategraph-routing-skeleton` | Graph State、拓扑、Fake Node 路由与 trace builder | +| 2 | `chat-diagnosis-stategraph-real-nodes` | Agent Adapter、Gatekeeper、verified input、retry prepare、Fallback | +| 3 | `chat-diagnosis-stategraph-chatservice-cutover` | ChatService 生产入口、DB migration、Run/Trace API | +| 4 | `chat-diagnosis-stategraph-test-suite` | 新测试体系与旧测试替换 | +| 5 | `chat-diagnosis-stategraph-cleanup-docs` | 旧编排清理、最终回归、Maven E2E、日志/DB、文档 | + +后续 change 必须先读取阶段 0 archive;若发现设计不准,必须在当阶段 OpenSpec 中显式记录和解决。 + +## Interface Impact + +### Classification + +- 当前阶段 0 change:L1。只交付设计、术语和决策档案,不改变运行时。 +- 最终冻结目标:L4。状态机语义和 Run 终态判断变化,Trace API 与数据库契约扩展,旧内部调用路径将被删除。 + +### Changed contracts and consumers + +| 契约 | 目标变化 | 消费者 | +|---|---|---| +| `/api/chat` | 请求/响应结构保持不变,内部路径改由 Graph 驱动 | ChatController、前端/demo 客户端 | +| Agent 输出协议 | `executor_evidence_v2`、Verifier、Composer 输出保持不变 | Agent adapters、Eval | +| Verifier 输入 | 改为 `verified_executor_output + verified_evidence`,不再提供完整 tool trace | Verifier adapter、Prompt binding | +| Run 状态 | 安全 Fallback 为 SUCCESS + degraded;只有无法安全响应的未处理失败为 FAILED | Trace、Feedback、Eval、运维审计 | +| Trace API | 仅 `run.orchestrationTrace` 新增解析对象 | Trace DTO/Service、demo 脚本、Trace UI | +| 数据库 | 仅新增 nullable JSON `diagnosis_run.orchestration_trace` | Flyway、DiagnosisRun repository | +| 内部编排 | 删除复杂 Chat Sequential、外层 round、Gatekeeper-in-Hook/ThreadLocal 入口 | ChatService、Hook、测试 | + +### Compatibility, migration, and rollback + +- 兼容消费者不需要修改 `/api/chat` 调用;Trace 消费者按加法字段处理。 +- 新 StateGraph Chat Run 必须写非空 orchestration trace;历史 Run 不回填、不增加兼容读取。 +- 数据库迁移先加 nullable 列,再切换代码;nullable 只服务于安全部署,不降低新 Run 应用契约。 +- 每阶段可 Git revert;阶段 3 后回滚代码时允许保留无害 nullable 列。 +- 不以 feature flag 或配置恢复长期双轨;若切换失败,回滚完整阶段提交。 + +### Interface acceptance + +- Controller 契约测试证明 `/api/chat` 不变。 +- Node/集成测试证明 Verifier 输入与安全 Fallback 边界。 +- Repository/Trace 测试证明 run ownership 和唯一 API 投影。 +- 阶段 5 Maven E2E、`logs/` 和数据库查询证明跨层最终行为。 + +## Risks / Trade-offs + +- [阶段性 spec 与运行时差异] 阶段 0 只新增设计基线 capability,不修改运行时 specs。 +- [六个 change 漂移] 每个 Discover/Commit gate 强制引用阶段 0 archive。 +- [Graph API 漂移] 后续只使用本地 `1.1.2.0` JAR 已验证签名,并由阶段 1 编译测试锁定。 +- [Gatekeeper 双执行] 阶段 2 迁移显式 Node,阶段 5 删除旧 Hook/ThreadLocal,不形成长期双轨。 +- [安全降级泄漏] Fallback 输入类型分离并用 Node/集成测试证明。 +- [Trace 无限增长或泄密] event 每 attempt 一条,受业务次数与 recursion limit 限制,字段白名单禁原文。 + +## Migration Plan + +1. 阶段 0 archive 本设计基线并提交。 +2. 阶段 1 引入未接生产入口的 Graph 骨架和路由测试。 +3. 阶段 2 接入真实节点但不切换 ChatService。 +4. 阶段 3 切换 ChatService,增加 nullable JSON 列和 run Trace 投影。 +5. 阶段 4 用新测试体系覆盖路由和安全契约。 +6. 阶段 5 删除旧编排并执行最终 Maven E2E、`logs/` 与数据库验收。 + +回滚按阶段 Git revert。阶段 3 后回滚运行时代码时允许保留 nullable 列;不通过配置重新启用长期双轨。历史 Run 不回填。 + +## Open Questions + +无。若后续发现本设计与锁定 API 或安全契约冲突,必须在当阶段 sm-flow 中分类并按门禁处理。 diff --git a/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/proposal.md b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/proposal.md new file mode 100644 index 0000000..682a59c --- /dev/null +++ b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/proposal.md @@ -0,0 +1,77 @@ +# Chat Diagnosis StateGraph Design Freeze + +## Why + +ISS-011 将复杂 Chat 诊断从 `SequentialAgent + ChatService` 隐式状态机迁移到 Spring AI Alibaba `StateGraph`。在进入任何运行时代码实现前,需要先把跨节点状态、全部条件边、有限重试、安全降级、审计边界和旧测试替换范围冻结为单一、可引用的设计契约,避免后续五个独立 sm-flow 对同一行为产生不同解释。 + +## What Changes + +本 change 只交付阶段 0 的设计冻结: + +- 冻结最小 Graph State 及 Replace/Append 更新策略。 +- 冻结 Planner、Executor、Gatekeeper、Verifier、Composer、Evidence Retry、Fallback 的全部条件边和终止路径。 +- 冻结 Planner/Verifier/Composer 技术重试与 evidence retry 的独立计数和最大次数。 +- 冻结 Gatekeeper 后的 Verifier 白名单输入、安全 Fallback 和 Run 状态语义。 +- 冻结 `orchestration_events -> diagnosis_run.orchestration_trace -> run.orchestrationTrace` 的审计边界。 +- 冻结旧 Sequential 测试的替换范围和必须保留的安全回归边界。 +- 形成供阶段 1–5 各自独立 OpenSpec change 引用的长期决策记录。 + +本 change 不修改 Java、SQL、Prompt、配置或运行时行为。 + +## Capabilities + +### New Capabilities + +- `chat-diagnosis-stategraph-design-freeze`:为阶段 1–5 提供版本化、可审计的 StateGraph 设计基线,覆盖状态、路由、重试、安全降级、审计和测试迁移边界。 + +### Modified Capabilities + +- 无。当前运行时 capabilities 只在对应实现阶段修改,避免阶段 0 归档后主规格提前宣称代码已切换。 + + +## Scope + +### In Scope + +- `mvp/issues/active/ISS-011-chat-diagnosis-stategraph-orchestration.md` 中阶段 0 的设计结论。 +- `devflow/glossary/CONTEXT.md` 中 Diagnosis Orchestration Trace 和 Verifier verified-evidence 边界。 +- 阶段 0 的 OpenSpec 设计、设计验收 spec、可执行文档任务和 devflow/ADR 档案。 +- 对最终目标的 L4 接口影响、迁移、回滚、兼容性与消费者边界做设计级记录。 + +### Out of Scope + +- 创建 StateGraph、Node、Adapter、路由或测试代码。 +- 修改 ChatService、Hook、Gatekeeper、Agent、Prompt 或工具。 +- 数据库迁移和 Trace DTO/API 修改。 +- 运行 Maven E2E、日志核验或数据库核验。 +- 把阶段 1–5 的实现任务放入本 change。 +- SupervisorAgent、持久 Checkpointer、HITL、并行 Agent/工具或 AIOps 公共 Graph。 + +## Context Constraints + +- `runId` 是一次 Diagnosis Run 与 Trace 的所有权边界;编排摘要不得写入 session 级数据。 +- Gatekeeper 引用真实性检查和 `checked_bindings` 是 verified evidence 的唯一事实来源,不得在 Verifier Input Builder 重复实现另一套验真规则。 +- Composer 只能消费 Verifier 允许材料;前置验证失败的固定 Fallback 不得泄漏 Executor claim。 +- `/api/chat`、`executor_evidence_v2`、Verifier 输出协议和 Composer 输出协议保持兼容。 +- 最终 Trace API 只在 `run.orchestrationTrace` 增加解析对象;数据库只新增 nullable JSON `diagnosis_run.orchestration_trace`。 +- 不保留长期 Sequential/StateGraph 双轨或运行时切换开关。 +- 锁定依赖版本 `spring-ai-alibaba-graph-core:1.1.2.0` 已通过本地 JAR API 核验,后续实现不得使用未经锁定版本验证的示例签名。 + +## Acceptance + +- 最小 Graph State 中每个字段都有所有权、用途和更新策略。 +- 每个节点状态都有唯一条件边;所有循环均有显式上限和终止路径。 +- Planner、Verifier、Composer 技术重试各自最多一次,且与最多一次 evidence retry 分开计数。 +- Executor 不重试;Gatekeeper REJECT 与零可信 binding 直接进入不泄漏 claim 的固定 Fallback。 +- PASS 与可继续的 LOW_CONFID 都经过 verified-input builder;Verifier 不接收完整 `tool_trace_summary`。 +- `orchestration_trace`、`self_evaluation`、`agent_step`、`tool_invocation` 和 Graph checkpoint 的职责不重叠。 +- 旧测试替换清单明确区分应替换的实现测试与必须保留/扩展的契约测试。 +- 不存在“待讨论决策”、未定义循环或没有终止路径的分支。 +- 本阶段无运行时变化,因此不新增或运行单元测试;使用 OpenSpec strict validation、结构检查与文档一致性检查验收。 + +## Risks + +- 阶段 0 只冻结设计,主分支运行时仍是 Sequential;后续阶段不得把设计文档状态误认为已实现状态。 +- 六个独立 changes 可能发生契约漂移;每个后续 Discover 必须读取本 change 的 archive 和 devflow 决策,并在 Commit gate 对照冻结契约。 +- 现有主 OpenSpec 仍描述旧 `tool_trace_summary` 和外层 LOW_CONFID round;只有对应运行时切换阶段才能修改并归档这些运行时 specs。 +- 最终设计属于 L4 影响;阶段 0 必须记录迁移/回滚,但不能以文档完成替代后续代码和最终 E2E 证据。 diff --git a/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/specs/chat-diagnosis-stategraph-design-freeze/spec.md b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/specs/chat-diagnosis-stategraph-design-freeze/spec.md new file mode 100644 index 0000000..704e642 --- /dev/null +++ b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/specs/chat-diagnosis-stategraph-design-freeze/spec.md @@ -0,0 +1,121 @@ +## ADDED Requirements + +### Requirement: StateGraph design baseline SHALL be versioned and authoritative + +The project SHALL maintain an archived ISS-011 design baseline that defines Graph State, routing, retry limits, fallback classes, audit boundaries, migration stages, and test replacement scope without claiming runtime cutover is complete. + +#### Scenario: Later stage starts implementation + +- **WHEN** an ISS-011 stage 1–5 OpenSpec change is proposed +- **THEN** its context and design SHALL reference the archived stage 0 baseline +- **AND** any intentional deviation SHALL be resolved through that stage's OpenSpec before implementation + +#### Scenario: Stage zero is accepted + +- **WHEN** the design-freeze change is archived +- **THEN** no Java, SQL, Prompt, configuration, or runtime behavior SHALL have been changed by this change +- **AND** runtime specs SHALL NOT claim StateGraph cutover is already implemented + +### Requirement: Graph State design SHALL use explicit bounded control state + +Every cross-node field SHALL have one purpose, owner, and update strategy. Fields SHALL use Replace semantics except bounded `orchestration_events`, which SHALL use Append. + +#### Scenario: Node state is designed + +- **WHEN** an Agent or Java Node reads or writes parent Graph State +- **THEN** its allowed input projection and output fields SHALL be explicit +- **AND** Prompt text, model reasoning, complete tool output, and complete State snapshots SHALL NOT be control state + +#### Scenario: Orchestration event is designed + +- **WHEN** a node attempt reaches a handled terminal outcome +- **THEN** at most one event SHALL be appended for that attempt +- **AND** it SHALL contain only stable node, outcome, reason code, and attempt data + +### Requirement: Routing design SHALL be complete and terminating + +Every Planner, Executor, Gatekeeper, Verifier, Composer, evidence-retry, and Fallback outcome SHALL map to one next node or terminal result. Every loop SHALL have an explicit business limit and the Graph SHALL use a recursion limit. + +#### Scenario: Technical retry is eligible + +- **WHEN** Planner, Verifier, or Composer first returns INVALID_OUTPUT or RETRYABLE_FAILED with the same allowed input +- **THEN** only that node SHALL be retried once +- **AND** no preceding Agent, Gatekeeper, or tool SHALL be rerun + +#### Scenario: Technical retry is exhausted + +- **WHEN** Planner, Verifier, or Composer returns NON_RETRYABLE_FAILED or exhausts its retry +- **THEN** the route SHALL terminate through the defined safe Fallback +- **AND** no unbounded loop SHALL remain + +#### Scenario: Executor cannot produce a legal contract + +- **WHEN** Executor returns INVALID_OUTPUT, TOOL_BLOCKED, or FAILED without legal `executor_evidence_v2` +- **THEN** the route SHALL go directly to pre-verification Fallback +- **AND** Executor SHALL NOT be retried + +#### Scenario: Evidence retry is eligible + +- **WHEN** effective verdict is LOW_CONFID, it was not caused by Gatekeeper ceiling, valid evidence gaps exist, and the Run has not retried evidence +- **THEN** one EVIDENCE_GAP_ONLY retry SHALL return to a new Planner stage +- **AND** technical retry counters SHALL remain independent from evidence retry count + +### Requirement: Verified evidence and fallback boundaries SHALL fail closed + +PASS and eligible LOW_CONFID Gatekeeper outcomes SHALL pass through a verified-input builder. Verifier SHALL receive only claims and evidence projected from passed checked bindings, not complete `tool_trace_summary` or unverified Executor text. + +#### Scenario: Gatekeeper result is unsafe or unknown + +- **WHEN** Gatekeeper returns REJECT, unknown, or LOW_CONFID with zero verified bindings +- **THEN** the route SHALL enter pre-verification Fallback without Verifier +- **AND** the final answer SHALL NOT contain any Executor claim + +#### Scenario: Gatekeeper permits verification + +- **WHEN** Gatekeeper returns PASS or eligible LOW_CONFID +- **THEN** the builder SHALL project evidence only from passed bindings +- **AND** a LOW_CONFID ceiling SHALL NOT be upgraded to PASS + +#### Scenario: Composer fails after verification + +- **WHEN** Composer exhausts retry after receiving Verifier-allowed material +- **THEN** deterministic fallback MAY use only that allowed material +- **AND** it SHALL NOT read raw Executor or tool output + +### Requirement: Orchestration audit design SHALL preserve Run ownership + +Orchestration audit SHALL remain separate from self evaluation, AgentStep, ToolInvocation, and Graph checkpoint data. A compact summary SHALL be derived from bounded events and persisted only to the current Run. + +#### Scenario: A safe response is produced + +- **WHEN** Graph reaches Composer or handled Fallback and produces a safe answer +- **THEN** Run status SHALL be SUCCESS +- **AND** degradation SHALL be represented by verdict or `orchestrationTrace.degraded` + +#### Scenario: Trace is exposed + +- **WHEN** an exact new StateGraph Chat Run is queried +- **THEN** parsed audit SHALL appear only at `run.orchestrationTrace` +- **AND** it SHALL NOT be duplicated at top level, session projection, or raw field + +#### Scenario: Audit ownership is evaluated + +- **WHEN** events or summaries are persisted +- **THEN** `runId` SHALL be the ownership and Graph thread boundary +- **AND** no data SHALL include Prompt, reasoning, complete tool output, or another Run + +### Requirement: Test migration design SHALL preserve safety behavior + +The baseline SHALL identify Sequential/Hook implementation tests to replace and public/security contract tests to retain or extend. Fixed Agent call order SHALL NOT remain a correctness criterion. + +#### Scenario: Old tests are replaced + +- **WHEN** StateGraph tests become authoritative +- **THEN** `ChatServiceSequentialAgentTest` SHALL be replaced by route, node-contract, and Chat integration coverage +- **AND** `VerifierInputHookTest` SHALL be removed or rewritten for explicit nodes + +#### Scenario: Safety tests are retained + +- **WHEN** the new suite is assembled +- **THEN** Gatekeeper, Controller, Run/Trace, Repository, Composer, no-evidence, REJECT, and Eval safety contracts SHALL remain covered +- **AND** fixed-order-only assertions SHALL be removed diff --git a/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/tasks.md b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/tasks.md new file mode 100644 index 0000000..e6f0cbb --- /dev/null +++ b/openspec/changes/archive/2026-07-17-chat-diagnosis-stategraph-design-freeze/tasks.md @@ -0,0 +1,15 @@ +## 1. Freeze Source Documents + +- [x] 1.1 Compare ISS-011 sections 3–14 against the committed state, routing, retry, fallback, audit, and test-migration design; resolve every drift without editing runtime code. +- [x] 1.2 Verify `devflow/glossary/CONTEXT.md` defines Diagnosis Orchestration Trace and the Verifier verified-evidence boundary without implementation-detail leakage. + +## 2. Record Durable Architecture Decisions + +- [x] 2.1 Create a project ADR that records the StateGraph control boundary, explicit Gatekeeper Node, run-scoped orchestration trace, rejected alternatives, compatibility, migration, and rollback decisions. +- [x] 2.2 Record the six independent sm-flow handoff boundaries and require stages 1–5 to reference the archived stage 0 baseline. + +## 3. Validate The Design Freeze + +- [x] 3.1 Run strict OpenSpec validation and cross-artifact checks for proposal, design, specs, and tasks; resolve every validation or alignment gap. +- [x] 3.2 Verify the stage 0 diff contains no Java, SQL, Prompt, configuration, or runtime behavior changes. +- [x] 3.3 Record that unit tests and Maven E2E are intentionally not run because stage 0 changes only design artifacts; reserve Maven E2E, logs, and database evidence for stage 5. diff --git a/openspec/specs/chat-diagnosis-stategraph-design-freeze/spec.md b/openspec/specs/chat-diagnosis-stategraph-design-freeze/spec.md new file mode 100644 index 0000000..8ee7c47 --- /dev/null +++ b/openspec/specs/chat-diagnosis-stategraph-design-freeze/spec.md @@ -0,0 +1,124 @@ +# chat-diagnosis-stategraph-design-freeze Specification + +## Purpose +为 ISS-011 阶段 1–5 提供已归档、可审计的 StateGraph 设计基线,规范跨节点状态、条件边、有限重试、安全降级、Run 级编排审计和测试迁移边界,同时明确阶段 0 不代表运行时已经切换。 +## Requirements +### Requirement: StateGraph design baseline SHALL be versioned and authoritative + +The project SHALL maintain an archived ISS-011 design baseline that defines Graph State, routing, retry limits, fallback classes, audit boundaries, migration stages, and test replacement scope without claiming runtime cutover is complete. + +#### Scenario: Later stage starts implementation + +- **WHEN** an ISS-011 stage 1–5 OpenSpec change is proposed +- **THEN** its context and design SHALL reference the archived stage 0 baseline +- **AND** any intentional deviation SHALL be resolved through that stage's OpenSpec before implementation + +#### Scenario: Stage zero is accepted + +- **WHEN** the design-freeze change is archived +- **THEN** no Java, SQL, Prompt, configuration, or runtime behavior SHALL have been changed by this change +- **AND** runtime specs SHALL NOT claim StateGraph cutover is already implemented + +### Requirement: Graph State design SHALL use explicit bounded control state + +Every cross-node field SHALL have one purpose, owner, and update strategy. Fields SHALL use Replace semantics except bounded `orchestration_events`, which SHALL use Append. + +#### Scenario: Node state is designed + +- **WHEN** an Agent or Java Node reads or writes parent Graph State +- **THEN** its allowed input projection and output fields SHALL be explicit +- **AND** Prompt text, model reasoning, complete tool output, and complete State snapshots SHALL NOT be control state + +#### Scenario: Orchestration event is designed + +- **WHEN** a node attempt reaches a handled terminal outcome +- **THEN** at most one event SHALL be appended for that attempt +- **AND** it SHALL contain only stable node, outcome, reason code, and attempt data + +### Requirement: Routing design SHALL be complete and terminating + +Every Planner, Executor, Gatekeeper, Verifier, Composer, evidence-retry, and Fallback outcome SHALL map to one next node or terminal result. Every loop SHALL have an explicit business limit and the Graph SHALL use a recursion limit. + +#### Scenario: Technical retry is eligible + +- **WHEN** Planner, Verifier, or Composer first returns INVALID_OUTPUT or RETRYABLE_FAILED with the same allowed input +- **THEN** only that node SHALL be retried once +- **AND** no preceding Agent, Gatekeeper, or tool SHALL be rerun + +#### Scenario: Technical retry is exhausted + +- **WHEN** Planner, Verifier, or Composer returns NON_RETRYABLE_FAILED or exhausts its retry +- **THEN** the route SHALL terminate through the defined safe Fallback +- **AND** no unbounded loop SHALL remain + +#### Scenario: Executor cannot produce a legal contract + +- **WHEN** Executor returns INVALID_OUTPUT, TOOL_BLOCKED, or FAILED without legal `executor_evidence_v2` +- **THEN** the route SHALL go directly to pre-verification Fallback +- **AND** Executor SHALL NOT be retried + +#### Scenario: Evidence retry is eligible + +- **WHEN** effective verdict is LOW_CONFID, it was not caused by Gatekeeper ceiling, valid evidence gaps exist, and the Run has not retried evidence +- **THEN** one EVIDENCE_GAP_ONLY retry SHALL return to a new Planner stage +- **AND** technical retry counters SHALL remain independent from evidence retry count + +### Requirement: Verified evidence and fallback boundaries SHALL fail closed + +PASS and eligible LOW_CONFID Gatekeeper outcomes SHALL pass through a verified-input builder. Verifier SHALL receive only claims and evidence projected from passed checked bindings, not complete `tool_trace_summary` or unverified Executor text. + +#### Scenario: Gatekeeper result is unsafe or unknown + +- **WHEN** Gatekeeper returns REJECT, unknown, or LOW_CONFID with zero verified bindings +- **THEN** the route SHALL enter pre-verification Fallback without Verifier +- **AND** the final answer SHALL NOT contain any Executor claim + +#### Scenario: Gatekeeper permits verification + +- **WHEN** Gatekeeper returns PASS or eligible LOW_CONFID +- **THEN** the builder SHALL project evidence only from passed bindings +- **AND** a LOW_CONFID ceiling SHALL NOT be upgraded to PASS + +#### Scenario: Composer fails after verification + +- **WHEN** Composer exhausts retry after receiving Verifier-allowed material +- **THEN** deterministic fallback MAY use only that allowed material +- **AND** it SHALL NOT read raw Executor or tool output + +### Requirement: Orchestration audit design SHALL preserve Run ownership + +Orchestration audit SHALL remain separate from self evaluation, AgentStep, ToolInvocation, and Graph checkpoint data. A compact summary SHALL be derived from bounded events and persisted only to the current Run. + +#### Scenario: A safe response is produced + +- **WHEN** Graph reaches Composer or handled Fallback and produces a safe answer +- **THEN** Run status SHALL be SUCCESS +- **AND** degradation SHALL be represented by verdict or `orchestrationTrace.degraded` + +#### Scenario: Trace is exposed + +- **WHEN** an exact new StateGraph Chat Run is queried +- **THEN** parsed audit SHALL appear only at `run.orchestrationTrace` +- **AND** it SHALL NOT be duplicated at top level, session projection, or raw field + +#### Scenario: Audit ownership is evaluated + +- **WHEN** events or summaries are persisted +- **THEN** `runId` SHALL be the ownership and Graph thread boundary +- **AND** no data SHALL include Prompt, reasoning, complete tool output, or another Run + +### Requirement: Test migration design SHALL preserve safety behavior + +The baseline SHALL identify Sequential/Hook implementation tests to replace and public/security contract tests to retain or extend. Fixed Agent call order SHALL NOT remain a correctness criterion. + +#### Scenario: Old tests are replaced + +- **WHEN** StateGraph tests become authoritative +- **THEN** `ChatServiceSequentialAgentTest` SHALL be replaced by route, node-contract, and Chat integration coverage +- **AND** `VerifierInputHookTest` SHALL be removed or rewritten for explicit nodes + +#### Scenario: Safety tests are retained + +- **WHEN** the new suite is assembled +- **THEN** Gatekeeper, Controller, Run/Trace, Repository, Composer, no-evidence, REJECT, and Eval safety contracts SHALL remain covered +- **AND** fixed-order-only assertions SHALL be removed