## 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 中分类并按门禁处理。