【Spring AI Graph:从0到Supervisor·预告】动手之前,先定好3个架构决策

起因

我的代码审查Agent跑了两个月,单链路一直没问题。直到需要加RAG查最佳实践------同一个入口要走不同检索路径,单链路搞不定,必须上Graph。

但Spring AI Graph的资料太少了,官方文档偏概念,社区文章偏Demo。我花了2周从零啃到能写出第一个子图,这里先说整体架构和技术亮点,周末发完整代码。

最终形态:三层拼装的Supervisor Graph

我要搭的不是"一个Graph",而是一个三层拼装系统

plaintext

yaml 复制代码
Layer 3: Supervisor Graph(底层StateGraph手写)
         意图识别 → 条件路由 → 子图分发
              ↓
Layer 2: SubGraph(高层API快搭 / 底层手写,按需选择)
         ├─ RAG问答子图:意图分类 → 条件路由 → keyword/hybrid双路 → rerank → 生成
         ├─ 代码审查子图:解析 → 并行扫描 → 风险评估 → HITL → 报告
         └─ 通用对话子图:简单链路
              ↓
Layer 1: Node(最小执行单元,实现NodeAction/EdgeAction接口)
         IntentAnalyzeNode / KeywordSearchNode / HybridSearchNode / ...

为什么要分三层?因为不同层的关注点不同

  • Layer 3 关心路由和编排,必须灵活,所以用底层StateGraph
  • Layer 2 关心业务流程,能用高层API就别手写
  • Layer 1 关心单一职责,每个Node只做一件事

这三层不是我自己发明的------LangGraph的图编排、Spring的DI分治、Erlang的Supervisor Tree,思想来源是这些。但用Spring AI Graph落地的时候,怎么拼、在哪层用什么API,没有现成答案。

3个架构亮点

亮点1:两层API混搭------不是全底层也不是全高层

Spring AI Alibaba提供两层API:

  • 高层:SequentialAgent / ParallelAgent / LlmRoutingAgent
  • 底层:StateGraph + Node + Edge

社区文章要么全用高层(搭个Demo没问题,复杂场景撑不住),要么全用底层(能干但开发慢)。

我的做法:Supervisor用底层手写,子图内部用高层快搭

为什么?因为Supervisor需要两样东西,高层API给不了:

  1. 条件路由:根据意图动态分发到不同子图,LlmRoutingAgent只能做一次路由,不支持多级条件判断
  2. HITL中断恢复:高风险代码暂停等人工确认,高层API没有"暂停→等人→继续"的机制

java

dart 复制代码
// Supervisor必须底层手写
StateGraph supervisor = new StateGraph(OverAllState.class)
    .addNode("intent", intentRouterNode)
    .addNode("code_review", codeReviewSubGraph)
    .addNode("rag_qa", ragQaSubGraph)
    .addNode("human_review", humanReviewNode)
    .addConditionalEdge("intent", this::routeByIntent,
        Map.of("code_review", "code_review", "rag_qa", "rag_qa"))
    .addConditionalEdge("code_review", this::needHumanReview,
        Map.of("yes", "human_review", "no", END));

但子图内部没必要全手写。代码审查的质量检查+安全扫描,ParallelAgent两行搞定。按需选择API层级,才是工程化的思路

亮点2:有状态编排------从"一次性调用"到"可恢复流程"

大多数Agent Demo是这样的:输入 → 模型 → 输出,无状态,跑完就完了。

但真实场景不是一次调用。代码审查需要:解析→并行扫描→风险评估→可能要等人确认→生成报告。中间任何一步都可能失败,需要从断点恢复而不是从头重跑。

StateGraph的OverAllState就是这个问题的解法------每个Node执行完,状态持久化到共享容器。下一个Node从容器读状态,不依赖上游传递。

java

ini 复制代码
// 每个子图注册自己需要的key,全部ReplaceStrategy
KeyStrategyFactory keyStrategyFactory = () -> {
    HashMap<String, KeyStrategy> strategies = new HashMap<>();
    strategies.put(DreamSaaSOverAllState.userInput, new ReplaceStrategy());
    strategies.put(DreamSaaSOverAllState.queryIntent, new ReplaceStrategy());
    strategies.put(DreamSaaSOverAllState.searchResult, new ReplaceStrategy());
    strategies.put(DreamSaaSOverAllState.finalAnswer, new ReplaceStrategy());
    return strategies;
};

Replace策略保证下游读到的始终是最新值。子图按需注册key,不跨层------RAG子图不需要riskLevel,代码审查子图不需要searchResult

这和Java里的Event Sourcing是同一个思路:状态是第一公民,流程是状态驱动的

亮点3:HITL不是confirm,是架构级的断点设计

"Human-in-the-Loop"很容易被理解成"弹个确认框"。但HITL在Graph架构里不是确认框,是架构级的断点

区别在哪?

  • 确认框:流程继续跑,弹个框让人点一下
  • 架构断点:流程暂停,状态持久化,等人回复后从断点恢复

这意味着:

  1. Agent不会在等人的时候占着资源
  2. 人工确认的结果会写回State,影响后续路由
  3. 断点可以跨Session------今天确认不了,明天接着来

代码审查场景:检测到SQL注入风险 → 暂停 → 等安全工程师确认 → 确认后才进报告生成。不是AI说了算,架构层面保证人可控

这个设计目前Spring AI Graph的Checkpoint机制还在完善中,我用的是手动实现的State快照方案,中篇会详细展开。

技术栈版本

spring-ai-bom 1.1.6 + spring-ai-alibaba 1.1.2.2。API还在迭代,跟着做的话版本对齐是前提。

连载计划

  • 上篇(本周末):【Spring AI Graph:从0到Supervisor·上】条件路由与RAG子图
  • 中篇(下周末):【Spring AI Graph:从0到Supervisor·中】HITL与并行执行
  • 下篇(6/6):【Spring AI Graph:从0到Supervisor·下】Supervisor三层拼装

每篇都放完整代码,不是snippet也不是伪代码。读者看完能直接写出同样的东西。

相关推荐
Maynor99612 分钟前
AI Coding 零基础实战教程|第五部分:完整项目案例实操
java·前端·人工智能·claude code·ai coding
wangruofeng16 分钟前
当 AI 打开浏览器:一次 LinkedIn 抓取背后的 8 层协议栈
scrapy·agent·ai编程
生戎马29 分钟前
Tomcat Container容器之Engine:StandardEngine _
java·tomcat
Coffeeee38 分钟前
22道AI Agent 工程师必会的面试题,别啥都不会就出去找工作
程序员·agent·ai编程
zhangjw3443 分钟前
第29篇:Java伪共享与对象分配:并发性能优化的关键
java·开发语言·性能优化
宠友信息1 小时前
内容社区源码多端数据协议与 Spring Boot 状态流转实现思路
java·spring boot·websocket·uni-app
一路向北North2 小时前
Spring Security OAuth2.0(19):JWT令牌
java·后端·spring
玛卡巴卡ldf2 小时前
【LeetCode 手撕算法】(细节知识点总结)
java·数据结构·算法·leetcode·力扣
一叶飘零_sweeeet2 小时前
IDEA 插件 Trae AI 最新全攻略(基于 TRAE AI: Coding Assistant 1.7.0.0)
java·intellij-idea·trae
KobeSacre3 小时前
CyclicBarrier 源码
java·jvm·算法