## Context 阶段 0 已归档 StateGraph 设计基线,当前仓库仍没有 Graph 实现。阶段 1 只引入可编译、可用 Fake Node 执行的内部骨架,不连接 Spring Bean、ChatService、真实 Agent 或数据库。 锁定依赖 `spring-ai-alibaba-graph-core:1.1.2.0` 已通过本地 JAR 验证: - `StateGraph.addNode(... AsyncNodeActionWithConfig)` - `StateGraph.addConditionalEdges(... AsyncEdgeActionWithConfig, mappings)` - `CompileConfig.builder().recursionLimit(...)` - `KeyStrategyFactoryBuilder`、`ReplaceStrategy`、`AppendStrategy` - `CompiledGraph.invoke(input, RunnableConfig)` - `RunnableConfig.builder().threadId(...)` 阶段 2 将消费 Node action ports,阶段 3 将消费 compiled graph。阶段 1 自身没有生产调用方。 ## Goals / Non-Goals **Goals:** - 建立明确的状态、状态枚举、节点、route 和 reason code 常量。 - 建立 config-aware Node action ports 和可编译 Graph 工厂。 - 以冻结计数和 guard 实现完整条件边。 - 验证 AppendStrategy 和实际 CompiledGraph 行为。 - 从有界 events 构造确定性的 orchestration trace。 - 用 Fake Node 测试所有路由和终止边界。 **Non-Goals:** - 不接 ReactAgent、Gatekeeper Service、ToolTrace 或 Prompt。 - 不修改 ChatService、Hook、ThreadLocal 或生产 Spring 配置。 - 不持久化 trace,不修改 DB/DTO/API。 - 不删除旧 Sequential 实现或测试。 - 不运行 Maven E2E。 ## Decisions ### 1. Package and class boundaries 新代码位于 `com.superbiz.agent.graph.diagnosis`: | 类型 | 职责 | |---|---| | `DiagnosisGraphState` | 26 个 state key、默认 Replace + events Append strategy、typed reads | | `DiagnosisGraphStatus` | Planner/Executor/Gatekeeper/Verifier/Composer status、Verdict、PlannerMode | | `DiagnosisGraphTopology` | Node id、route key、reason code 常量 | | `DiagnosisGraphActions` | 八个 `AsyncNodeActionWithConfig` ports | | `DiagnosisGraphRouter` | 纯确定性 edge guard,不修改 state | | `DiagnosisGraphFactory` | 注册 Node/Edge、retry wrapper、evidence retry wrapper、recursion limit | | `OrchestrationEvent` | node/outcome/reasonCode/attempt | | `OrchestrationTransition` | from/to/reasonCode/attempt | | `DiagnosisOrchestrationTrace` | version/transitions/finalNode/terminationReason/degraded/evidenceRetryCount | | `DiagnosisOrchestrationTraceBuilder` | events → transitions/summary | Fake Node 和 script fixture 只存在于 test 源集。 替代方案:把 router 和状态读取放进 ChatService 或每个 Adapter。拒绝,因为会复制 guard 并阻碍 Fake Node 独立验证。 ### 1.1 Direct dependency ownership 代码直接 import Graph Core API,因此 `pom.xml` 显式声明 `com.alibaba.cloud.ai:spring-ai-alibaba-graph-core`。版本继续由现有 Spring AI Alibaba BOM 管理为 1.1.2.0,不重复写版本。依赖 Agent Framework 的传递依赖虽可编译,但会让上游依赖图调整无意中破坏本模块,拒绝。 ### 2. State strategy and typed reads `KeyStrategyFactoryBuilder.defaultStrategy(new ReplaceStrategy())`,仅对 `orchestration_events` 使用 `new AppendStrategy()`。Node 每次返回 `List.of(event)`,Graph 合并后保持有序列表。 typed reads 对 absent、null、错误类型和未知 enum 返回安全默认,不抛出边路由异常。未知 status/verdict 的路由默认 Fallback。 ### 3. Config-aware action ports 所有 port 使用 `AsyncNodeActionWithConfig`,即使 Fake Node 当前只需要 state。原因是阶段 2 必须读取 `RunnableConfig.threadId/metadata` 并向 Agent 传递 run context;现在锁定接口可以避免随后重写 Graph topology。 `DiagnosisGraphActions` 构造时对八个 action 做 non-null 校验。 ### 4. Retry counter ownership Edge 只选 route,不修改 state。Factory 用 wrapper 管理计数: - Planner 重入前,如果上次 `planner_status` 是 INVALID_OUTPUT/RETRYABLE_FAILED,count + 1。 - Verifier/Composer 同理。 - 第一次失败后的 state count 为 0,允许 self-loop;重入执行后 count 为 1,再失败则 Fallback。 - Evidence Retry Node 完成时将 `evidence_retry_count + 1`、`planner_retry_count=0`、`planner_mode=EVIDENCE_GAP_ONLY`。 - Verified Input Node 完成时重置 `verifier_retry_count=0`,因为它产生新的 verified input。 - 技术重试不修改 evidence count。 替代方案:由每个 Fake/real action 自行维护计数。拒绝,因为遗漏会造成无限 self-loop,骨架必须拥有控制计数。 ### 5. Conditional routing | Source | Route keys | |---|---| | Planner | executor / retry_planner / fallback | | Executor | gatekeeper / fallback | | Gatekeeper | verified_input / fallback | | Verifier | composer / retry_verifier / evidence_retry / fallback | | Composer | end / retry_composer / fallback | Verified Input 固定到 Verifier,Evidence Retry 固定到 Planner,Fallback 固定到 END。 Verifier evidence-retry guard 同时要求: - `verifier_status=COMPLETED` - `effective_verdict=LOW_CONFID` - `verifier_verdict_ceiling=PASS` - `verifier_output.facts_checked` 至少一项具有非空 fact 且 verification 为 `no_evidence` 或 `indirect_support` - `evidence_retry_count < 1` 否则 LOW_CONFID 到 Composer。router 只检查现有 Verifier 输出,不选择工具或构造 retry context;后者属于阶段 2 Evidence Retry Node。 ### 6. Recursion limit 合法最坏路径低于 20 次 Node 执行;compile recursion limit 固定 32。测试断言 compiled graph 的 max iterations/compile config,并覆盖第二次技术失败和第二次 LOW_CONFID 均终止。 recursion limit 是最后保险,不替代业务 guard。 ### 7. Event and trace contract `OrchestrationEvent` 构造时拒绝 blank node/outcome/reason 或 attempt<1。每个 Fake/真实 Node attempt 返回一个 event;AppendStrategy 累积实际路径。 Builder: 1. 要求 events 非空且类型正确。 2. 相邻 events 生成 transition,transition reason/attempt 取 source event。 3. 最后 event 决定 finalNode 和 terminationReason。 4. final node 为 Fallback 时 degraded=true;其他质量由 effective verdict 表达。 5. evidence retry count 从 state 读取。 6. `toMap()` 使用冻结 snake_case 字段,transition 同样提供 map。 不从应用日志推导路径,也不在 state 维护第二份 transitions。 ### 8. Test architecture `DiagnosisGraphRoutingTest` 使用真实 `CompiledGraph` + scriptable Fake actions: - 每个 node 有 outcome 队列和调用计数。 - Fake action 只输出本 node 的状态字段、必要 guard payload 和一个 event。 - 每个用例使用唯一 `RunnableConfig.threadId`。 - 断言 node sequence、调用次数、最终 state、events、trace transitions。 - 参数化覆盖同类技术失败,独立用例覆盖 evidence retry/counter reset 和 unknown fail-closed。 `DiagnosisOrchestrationTraceBuilderTest` 覆盖空 events、非法 event、顺序、fallback degraded 和 map shape。 ## Interface Impact - 等级:L2 internal interface。 - 新接口消费者:阶段 2 adapters、阶段 3 orchestrator、阶段 4 test suite。 - 构建消费者:Maven 直接声明 Graph Core,版本仍由既有 BOM 统一管理。 - 当前生产消费者:无。 - 外部 API/DTO/数据库/状态:无变化。 - 兼容策略:后续 actions 实现 ports;Graph State 不直接传给 Agent。 - 回滚:revert 本阶段提交,无数据迁移。 ## Risks / Trade-offs - [Graph merge 行为与假设不同] → 使用真实 CompiledGraph 单元测试,不 mock StateGraph。 - [计数 wrapper 与 action 更新冲突] → wrapper 最后写入控制计数,actions 不拥有 retry counters。 - [Verifier gap 解析过早耦合] → router 只识别现有 facts_checked 最小字段,retry context 构造留到阶段 2。 - [未接生产入口被误认为完成] → proposal/spec/acceptance 明确 skeleton-only,阶段 3 才切换。 - [事件无限追加] → 所有业务循环有硬上限且 recursion limit=32。 ## Migration Plan 1. 添加纯 Java 状态、router、actions、factory 和 trace types。 2. 先用 Fake actions 编译和执行完整 Graph。 3. 阶段 1 archive + commit 后,阶段 2 基于 ports 实现真实 nodes。 4. 如需回滚,revert 阶段 1 commit;当前生产路径不受影响。 ## Open Questions 无。