Spring AI Alibaba Graph 实战:用图编排复杂业务,替代硬编码,支持分支、并行与人工确认
前面几篇我们分别做了单 Agent、Memory+Tools+RAG、多 Agent Supervisor。到了真实企业业务里,你会遇到一类新问题:
- 请假要先 AI 校验、再人工确认信息、再算余额、再主管审批、再通知;
- 客服邮件要读信、分类、检索知识、Bug 建单、起草、人工审核、发送;
- 研报要并行查多个数据源、汇总、人工反馈、再写报告;
- 发布流程要在"灰度/全量/回滚"之间按条件分支,关键步骤必须人工点确认。
如果把这些用 if/else、硬编码 Service 调用、线程池手动串起来,很快会变成"意大利面条":分支难加、并行难控、人工中断难恢复、出问题难复盘。Spring AI Alibaba Graph 的思路是把流程建成有向图:节点是步骤,边是流转,共享状态贯穿全程,模型可按状态做条件路由,人工节点可暂停恢复。
本文按"为什么用图 → 核心概念 → 顺序/分支/并行/人工确认四种编排 → 完整审批示例 → 生产注意"展开。
一、为什么复杂流程不用硬编码
硬编码多步 AI 业务通常有这些痛点:
1. 分支膨胀
"如果分类是 A 走检索,B 走建单,C 走人工,人工后又可能回到起草或结束"------用 switch能写,但加一个新分类就要改主流程,回归成本高。
2. 并行难做
同时查订单、查知识库、查风控,用线程池能跑,但要自己合并结果、处理异常、控制超时;Graph 可用并行节点/Fork-Join 统一编排。
3. 人工中断难恢复
"主管审批"如果放在普通 Service 里,通常只能同步等结果;要支持"先返回前端、主管后来在后台点通过并继续",就需要把流程状态落盘、按 checkpoint 恢复。Graph 的 Human-in-the-loop 正是解决这个问题。
4. 可观测性差
硬编码流程出问题时,你只知道抛了异常,不知道到底走到哪一步、状态里有什么。Graph 可导出 Mermaid/PlantUML,可打流程快照,每一步状态可查。
5. 模型自主 vs 业务确定性的平衡
纯 ReAct Agent 太自由,适合探索;企业审批、合规、工单要求"哪几步必须走、哪几步可模型决策"。Graph 把确定部分写死成边,把不确定部分放在 LLM 节点里,兼顾可控与智能。
二、Spring AI Alibaba Graph 核心概念
Graph 模块灵感接近 LangGraph 的 Java 实现,核心四件套:
- StateGraph :图定义容器,添加节点、边,最后
compile()成CompiledGraph; - Node :原子步骤,可调模型、调工具、写 Java 逻辑、做人工节点;异步用
node_async,同步实现NodeAction; - Edge :节点间流转。普通边
addEdge固定走;条件边addConditionalEdges按状态路由; - OverAllState :全局共享状态,KV 结构。各节点读输入、写结果;通过
KeyStrategy定义同名 key 的合并方式(替换/追加等)。
内置能力还包括:多 Agent(ReAct、Supervisor)、工作流节点、流式、HITL、记忆/持久化、流程快照、嵌套/并行分支、可视化导出。
一个最朴素的图:
less
StateGraph graph = new StateGraph(keyStrategyFactory)
.addNode("classify", node_async(classifyNode))
.addNode("rag", node_async(ragNode))
.addNode("ticket", node_async(ticketNode))
.addNode("human_review", node_async(humanReviewNode))
.addEdge(START, "classify")
.addConditionalEdges("classify", edge_async(state ->
(String) state.value("next").orElse("rag")),
Map.of("rag", "rag", "ticket", "ticket", "human", "human_review"))
.addEdge("rag", END)
.addEdge("ticket", END)
.addEdge("human_review", END);
START/END是内置常量。节点返回或写入 OverAllState,边再从状态里取路由字段。
三、四种典型编排
3.1 顺序编排:确定流水线
适合步骤固定、前一步输出是后一步输入的场景,比如"读邮件→分类→检索→起草→发送"。
官方客服邮件示例:
less
var readEmail = node_async(new ReadEmailNode());
var classifyIntent = node_async(new ClassifyIntentNode());
var searchDocs = node_async(new SearchDocsNode(vectorStore));
var bugTracking = node_async(new BugTrackingNode());
var draft = node_async(new DraftResponseNode(chatClientBuilder));
var humanReview = node_async(new HumanReviewNode());
var sendReply = node_async(new SendReplyNode());
StateGraph workflow = new StateGraph(createKeyStrategyFactory())
.addNode("read_email", readEmail)
.addNode("classify_intent", classifyIntent)
.addNode("search_documentation", searchDocs)
.addNode("bug_tracking", bugTracking)
.addNode("draft_response", draft)
.addNode("human_review", humanReview)
.addNode("send_reply", sendReply);
workflow.addEdge(START, "read_email");
workflow.addEdge("read_email", "classify_intent");
workflow.addEdge("send_reply", END);
workflow.addConditionalEdges("classify_intent",
edge_async(state -> (String) state.value("next_node")
.orElse("draft_response")),
Map.of(
"search_documentation", "search_documentation",
"bug_tracking", "bug_tracking",
"human_review", "human_review",
"draft_response", "draft_response"));
workflow.addEdge("search_documentation", "draft_response");
workflow.addEdge("bug_tracking", "draft_response");
workflow.addEdge("draft_response", "human_review");
workflow.addEdge("human_review", "send_reply");
分类节点把路由结果写进 next_node,条件边按它分流;简单问题走检索起草,Bug 走建单,复杂走人工。
3.2 条件分支:模型决策 + 业务规则双控
条件边不一定只靠模型。可以:
- 模型分类写状态字段,条件边路由;
- 规则节点直接读参数路由(如金额>1万走人工,否则自动);
- 两者结合:先模型抽实体,再规则判断。
示例:审批金额分支
vbnet
workflow.addConditionalEdges("extract_info",
edge_async(state -> {
Double amount = (Double) state.value("amount").orElse(0d);
String risk = (String) state.value("risk_level").orElse("low");
if ("high".equals(risk)) return "human_legal";
if (amount != null && amount > 10000) return "manager_approve";
return "auto_approve";
}),
Map.of(
"human_legal", "human_legal",
"manager_approve", "manager_approve",
"auto_approve", "auto_approve"));
这样既有模型抽数,又有确定规则,避免模型自己决定是否绕开合规。
3.3 并行编排:Fork-Join
Graph 支持并行节点,但官方当前实现有明确限制:
- 采用 Fork-Join 模型:一个父节点分多个子节点,全部完成后汇合到下一节点;
- 通常只放一个并行步骤,不要在并行分支里再嵌套复杂条件边;
- 要真正并发,需要给并行节点配
Executor,否则可能顺序调度; - 通过
RunnableConfig.addParallelNodeExecutor配置线程池。
示例:同时做"营销文案、规格抽取、风控评分"
arduino
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> map = new HashMap<>();
map.put("query", new ReplaceStrategy());
map.put("copy", new ReplaceStrategy());
map.put("spec", new ReplaceStrategy());
map.put("riskScore", new ReplaceStrategy());
return map;
};
StateGraph g = new StateGraph(factory)
.addNode("marketingCopy", node_async(new CopyNode(chatClient)))
.addNode("specExtraction", node_async(new SpecNode(chatClient)))
.addNode("riskScoring", node_async(new RiskNode()))
.addNode("merge", node_async(new MergeNode()))
.addEdge(START, "marketingCopy")
.addEdge(START, "specExtraction")
.addEdge(START, "riskScoring")
.addEdge("marketingCopy", "merge")
.addEdge("specExtraction", "merge")
.addEdge("riskScoring", "merge")
.addEdge("merge", END);
RunnableConfig rc = RunnableConfig.builder()
.addParallelNodeExecutor("marketingCopy", ForkJoinPool.commonPool())
.addParallelNodeExecutor("specExtraction", ForkJoinPool.commonPool())
.addParallelNodeExecutor("riskScoring", ForkJoinPool.commonPool())
.build();
CompiledGraph compiled = g.compile();
compiled.execute(initialState, rc);
注意:从 START同时连三个节点表示分叉,三个都连到 merge表示汇合。若只是"逻辑并行"但不配 Executor,执行模型可能退化为顺序,具体以所用版本运行时为准。
3.4 人工确认(Human-in-the-loop)
这是企业流程最核心的能力。两种落地方式:
方式 A:同步人工节点
流程跑到人工节点,节点内部阻塞等前端/接口传入结果,写回状态后继续。适合内部工具、低频审批。
官方 HITL 示例是"扩写→人工反馈→翻译/结束":
java
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> m = new HashMap<>();
m.put("query", new ReplaceStrategy());
m.put("expandContent", new ReplaceStrategy());
m.put("feedback", new ReplaceStrategy());
m.put("humanNextNode", new ReplaceStrategy());
m.put("translateLanguage", new ReplaceStrategy());
m.put("translateContent", new ReplaceStrategy());
return m;
};
StateGraph g = new StateGraph(factory)
.addNode("expander", node_async(new ExpanderNode(chatClientBuilder)))
.addNode("humanfeedback", node_async(new HumanFeedbackNode()))
.addNode("translate", node_async(new TranslateNode(chatClientBuilder)))
.addEdge(START, "expander")
.addEdge("expander", "humanfeedback")
.addConditionalEdges("humanfeedback",
AsyncEdgeAction.edgeAsync(new HumanFeedbackDispatcher()),
Map.of("translate", "translate", END, END))
.addEdge("translate", END);
HumanFeedbackNode不直接调模型,而是把"待确认内容"返回给调用方;人工通过接口写 feedback、humanNextNode后再次 execute/resume,图从人工节点继续。
方式 B:Checkpoint 断点恢复
长流程、跨天审批用这种:人工节点触发中断,状态落库;主管后来在后台点"通过/驳回",带着新状态恢复编译图。Spring AI Alibaba Graph 支持流程快照、记忆与持久存储,HITL 可通过人工确认节点修改状态并恢复执行。
审批流可这样拆:
less
.addNode("ai_check", node_async(new LeaveInfoCheckNode(chatClient)))
.addNode("info_check", node_async(new LeaveInfoConfirmNode())) // 人工确认信息
.addNode("calc_balance", node_async(new LeaveCalcBalanceNode()))
.addNode("manager_approve", node_async(new ManagerApproveNode())) // 人工主管审批
.addNode("notify", node_async(new LeaveNotifyNode()))
.addEdge(START, "ai_check")
.addEdge("ai_check", "info_check")
.addConditionalEdges("info_check", infoCheckDispatcher,
Map.of("ok", "calc_balance", "edit", "ai_check", "cancel", END))
.addEdge("calc_balance", "manager_approve")
.addConditionalEdges("manager_approve", approveDispatcher,
Map.of("approve", "notify", "reject", "info_check", "pending", END))
.addEdge("notify", END);
info_check是人工校信息,manager_approve是人工审批;若选 pending则中断落盘,后续 resume。
四、完整示例:请假+知识+RAG+工具混合审批
把前面系列能力串起来:RAG 查制度、Tools 查假期余额、Graph 做分支与人工。
4.1 状态策略
java
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> m = new HashMap<>();
m.put("query", new ReplaceStrategy());
m.put("threadId", new ReplaceStrategy());
m.put("leaveForm", new ReplaceStrategy()); // 结构化请假单
m.put("policyContext", new ReplaceStrategy()); // RAG返回的制度片段
m.put("balance", new ReplaceStrategy()); // 剩余假期
m.put("infoConfirmed", new ReplaceStrategy()); // 人工确认信息 Y/N
m.put("managerDecision", new ReplaceStrategy()); // 主管 approve/reject/pending
m.put("next", new ReplaceStrategy());
return m;
};
4.2 节点
policyRag:用 QuestionAnswerAdvisor/VectorStore 检索"年假规则",写policyContext;extractForm:LLM 从用户自然语言抽姓名、起止日期、类型,写leaveForm;infoConfirm:人工节点,返回确认结果;calcBalance:调用假期系统工具,返回balance;managerApprove:人工审批;notify:调用通知工具。
less
.addNode("policy_rag", node_async(new PolicyRagNode(chatClient, vectorStore)))
.addNode("extract_form", node_async(new ExtractFormNode(chatClient)))
.addNode("info_confirm", node_async(new InfoConfirmNode()))
.addNode("calc_balance", node_async(new CalcBalanceNode(leaveTools)))
.addNode("manager_approve", node_async(new ManagerApproveNode()))
.addNode("notify", node_async(new NotifyNode(notifyTools)))
4.3 边与分支
less
.addEdge(START, "policy_rag")
.addEdge("policy_rag", "extract_form")
.addEdge("extract_form", "info_confirm")
.addConditionalEdges("info_confirm",
edge_async(s -> (String) s.value("infoConfirmed").orElse("edit")),
Map.of("ok", "calc_balance", "edit", "extract_form", "cancel", END))
.addEdge("calc_balance", "manager_approve")
.addConditionalEdges("manager_approve",
edge_async(s -> (String) s.value("managerDecision").orElse("pending")),
Map.of("approve", "notify", "reject", "extract_form", "pending", END))
.addEdge("notify", END);
这里既有 RAG 注入制度,又有工具算余额,又在两个关键点人工确认;比硬编码 if清晰很多,新增"HR 复核"只需加节点和一条条件边。
4.4 执行与恢复(人工)
首次发起:
ini
OverAllState init = new OverAllState();
init.put("query", "我要请年假3天,下周一至周三");
init.put("threadId", "leave-1001");
CompiledGraph graph = stateGraph.compile();
CompletableFuture<OverAllState> f = graph.execute(init);
// 到 info_confirm 若设计为阻塞,就拿人工输入;若断点恢复,就持久化并返回前端
人工审批恢复(伪代码):
ini
OverAllState saved = checkpointer.load("leave-1001");
saved.put("managerDecision", "approve");
graph.execute(saved); // 从 manager_approve 后续边继续到 notify
生产里 checkpointer可接数据库;Spring AI Alibaba 支持记忆与持久存储、流程快照。
五、Graph 与多 Agent Supervisor 的关系
前面写的 Supervisor 多 Agent,也可以用 Graph 显式编排。官方 Supervisor 示例:
java
SupervisorNode supervisor = new SupervisorNode(chatModel, members);
ResearcherNode researcher = new ResearcherNode(chatModelWithTool);
CoderNode coder = new CoderNode(chatModelWithTool);
StateGraph workflow = new StateGraph(factory)
.addNode("supervisor", node_async(supervisor))
.addNode("researcher", node_async(researcher))
.addNode("coder", node_async(coder))
.addEdge(START, "supervisor")
.addConditionalEdges("supervisor",
edge_async(state -> (String) state.value("next").orElse("FINISH")),
Map.of("FINISH", END, "researcher", "researcher", "coder", "coder"))
.addEdge("researcher", "supervisor")
.addEdge("coder", "supervisor");
return workflow.compile();
要点:
- Supervisor 节点只写
next=researcher/coder/FINISH; - worker 执行完回 supervisor,形成"思考→派活→观察→再派活"的图式循环;
- 若 researcher 需要 RAG、coder 需要代码工具、reviewer 需要静态扫描,分别做成节点即可;
- 比"把所有工具注册给一个 ChatClient"更可控,也不会把不相关工具塞进同一上下文。
DeepResearch 类复杂图还可加 coordinator、background_investigator、planner、human_feedback、research_team、reporter 等节点,条件边做多层路由。
六、可视化与调试
Graph 可导出图结构,方便和产品/运维对流程:
ini
GraphRepresentation rep = stateGraph.getGraph(
GraphRepresentation.Type.PLANTUML, "leave-approval");
log.info(rep.content());
// Mermaid 同理按所用 API 切换 Type
输出可用于 Confluence、研发文档;配合节点日志(进入节点、读取状态 key、写出 key、边路由结果),定位"为什么走了人工而不是自动通过"会很轻松。
建议每个节点统一打:
nodeName、threadId;- 读取的关键 state key;
- LLM token/工具耗时;
- 条件边最终路由目标。
七、生产落地建议
1. 哪些步骤用确定边,哪些用模型路由
合规、金额、权限、状态机必走确定边;意图分类、摘要、起草、抽取可让模型写路由字段。不要把"是否走人工审批"完全交给模型。
2. 人工节点设计成"可恢复"
前端只发"待办 ID + 决策 + 修改后表单",后端从 checkpointer 恢复;不要让人把全部上下文重新粘贴一遍。
3. 并行别滥用
Graph 并行当前更偏 Fork-Join、单并行段;多个互相依赖的 AI 步骤别硬并行,否则状态合并复杂、成本难控。 真要管线并行,可在节点内部用 CompletableFuture自己聚。
4. 状态 key 合并策略要显式
列表类(审核历史、检索片段)用追加;最终结论、表单用替换。避免多节点写同一 key 互相覆盖。
5. 限流与最大步数
图里若有回边(supervisor↔worker、人工 edit↔extract),必须设最大迭代/最大人工次数,防止"模型一直重新起草"或"用户一直改表单"导致死循环。Spring AI Alibaba Agent 层还提供模型调用限制等拦截器思路,可作补充。
6. 与前面系列能力组合
- Memory:会话级用 ChatMemory,流程级用 OverAllState+checkpointer;
- Tools:节点内调用
@Tool/ToolCallback,人工节点不调模型只改状态; - RAG:policy/产品知识节点走 VectorStore+Advisor;
- 多 Agent:Supervisor/Researcher/Coder/Reviewer 各成节点,Graph 负责大流程。
八、总结
复杂业务不要用"一个 Controller 调多个 Service 再套 LLM"的硬编码方式。用 Spring AI Alibaba Graph 的落地公式:
scss
StateGraph = 确定边(固定流程) + 条件边(分支/规则/模型路由) + 并行节点(Fork-Join) + 人工节点(HITL) + OverAllState(共享上下文)
- 顺序流程用
addEdge; - 意图/风险/金额分支用
addConditionalEdges; - 多源查询用并行节点+Executor,注意 Fork-Join 限制;
- 合规/审批用人工节点,同步确认用阻塞、长流程用 checkpoint 恢复;
- 多专家用 Supervisor 回边,RAG/Tools/Memory 作为节点能力接入。