227 lines
15 KiB
Markdown
227 lines
15 KiB
Markdown
## 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 中分类并按门禁处理。
|