170 lines
8.2 KiB
Markdown
170 lines
8.2 KiB
Markdown
## 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
|
||
|
||
无。
|