15 KiB
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.0JAR 已验证签名,并由阶段 1 编译测试锁定。 - [Gatekeeper 双执行] 阶段 2 迁移显式 Node,阶段 5 删除旧 Hook/ThreadLocal,不形成长期双轨。
- [安全降级泄漏] Fallback 输入类型分离并用 Node/集成测试证明。
- [Trace 无限增长或泄密] event 每 attempt 一条,受业务次数与 recursion limit 限制,字段白名单禁原文。
Migration Plan
- 阶段 0 archive 本设计基线并提交。
- 阶段 1 引入未接生产入口的 Graph 骨架和路由测试。
- 阶段 2 接入真实节点但不切换 ChatService。
- 阶段 3 切换 ChatService,增加 nullable JSON 列和 run Trace 投影。
- 阶段 4 用新测试体系覆盖路由和安全契约。
- 阶段 5 删除旧编排并执行最终 Maven E2E、
logs/与数据库验收。
回滚按阶段 Git revert。阶段 3 后回滚运行时代码时允许保留 nullable 列;不通过配置重新启用长期双轨。历史 Run 不回填。
Open Questions
无。若后续发现本设计与锁定 API 或安全契约冲突,必须在当阶段 sm-flow 中分类并按门禁处理。