8.2 KiB
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、AppendStrategyCompiledGraph.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=COMPLETEDeffective_verdict=LOW_CONFIDverifier_verdict_ceiling=PASSverifier_output.facts_checked至少一项具有非空 fact 且 verification 为no_evidence或indirect_supportevidence_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:
- 要求 events 非空且类型正确。
- 相邻 events 生成 transition,transition reason/attempt 取 source event。
- 最后 event 决定 finalNode 和 terminationReason。
- final node 为 Fallback 时 degraded=true;其他质量由 effective verdict 表达。
- evidence retry count 从 state 读取。
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
- 添加纯 Java 状态、router、actions、factory 和 trace types。
- 先用 Fake actions 编译和执行完整 Graph。
- 阶段 1 archive + commit 后,阶段 2 基于 ports 实现真实 nodes。
- 如需回滚,revert 阶段 1 commit;当前生产路径不受影响。
Open Questions
无。