Spring AI Alibaba Graph 工作流:节点和边到底是怎么回事?
摘要:搞懂 Spring AI Alibaba Graph 里的节点、边、State 这三个核心概念,才能玩转工作流编排。本文从实际项目踩坑经验出发,带你彻底搞清楚 DAG 是怎么跑起来的。
最近在啃 Spring AI Alibaba Graph 的源码,刚开始真的有点懵。
节点是啥?边又是啥?State 怎么传的?条件分支怎么搞?
查了一圈文档,发现讲概念的不少,但真正告诉你「为什么这么设计」「企业里怎么用」的并不多。
所以这篇文章,门主把自己踩过的坑、翻过的源码,整理出来分享给你。
一、Graph 到底是个啥?
Spring AI Alibaba Graph 本质上就是一个 DAG(有向无环图) 引擎。
一句话:把一个复杂的 Agent 任务,拆成多个小步骤,每个步骤是一个「节点」,节点之间用「边」连起来规定顺序,整个流程就跑起来了。
先看个图感受一下:
核心三件套:
| 概念 | 作用 | 通俗理解 |
|---|---|---|
| State(状态) | 节点之间传递的数据结构,Map<String, Object> |
共享的数据包,每个节点都能读写 |
| Node(节点) | 执行逻辑的单元,接收 State → 处理 → 返回新 State | 流水线上的一个工人 |
| Edge(边) | 定义节点之间的控制流,决定下一步去哪 | 流水线上的传送带 |
记住这句话:节点负责干活,边负责指挥往哪走,State 负责传数据。
二、两种开发模式,别搞混了
Spring AI Alibaba Graph 提供了两套 API:
| 模式 | 适合场景 | 灵活度 |
|---|---|---|
| Agentic API | 快速开发,声明式定义 Agent | 低,框架帮你搞定细节 |
| Graph API | 需要精细控制每个步骤 | 高,节点、边全部自己定义 |
简单说:
- 如果你的 Agent 就是「接收输入 → 调用 LLM → 返回结果」,用 Agentic API,几行代码搞定
- 如果你的流程有分支、有并行、需要人工干预,老老实实用 Graph API
企业里面,复杂场景居多,所以 Graph API 是重点。
三、节点(Node)是怎么干活的?
3.1 节点的本质
节点就是一个函数,输入 State,输出 State。
3.2 自定义节点
你需要实现 NodeAction 或 NodeActionWithConfig:
java
// 简单版:只管处理状态
public interface NodeAction {
Map<String, Object> apply(OverAllState state) throws Exception;
}
// 高级版:多拿一个配置参数
public interface NodeActionWithConfig {
Map<String, Object> apply(OverAllState state, RunnableConfig config) throws Exception;
}
区别很简单:
| 对比项 | NodeAction | NodeActionWithConfig |
|---|---|---|
| 参数 | 只要 state | state + config |
| 能拿到什么 | 只有业务数据 | 业务数据 + 运行时元数据(线程ID等) |
| 适用场景 | 纯业务逻辑 | 需要读取运行配置、做日志追踪 |
门主建议:90% 的情况用 NodeAction 就够了,除非你需要从 config 里拿东西。
3.3 节点类型
工作流里常见的节点就这几种:
| 类型 | 说明 | 示例 |
|---|---|---|
| 处理节点 | 纯业务逻辑,数据处理 | 文本清洗、数据格式化 |
| AI 节点 | 调用 LLM 做推理 | 查询扩写、内容生成 |
| 条件节点 | 根据 State 值做路由判断 | 判断文本长度决定下一步 |
| 聚合节点 | 收集多个并行节点的结果 | 合并多个搜索结果 |
| Agent 节点 | 把整个 Agent 当作一个节点 | agent.asNode() |
3.4 节点开发最佳实践
- 单一职责:一个节点只做一件事,别搞成大杂烩
- 不要直接改 State:返回新的 Map,而不是修改入参(避免副作用)
- 异常处理:内部 catch 住,返回错误信息,别让异常中断整个流程
- 加日志:工作流排错全靠日志,不加日志就是给自己挖坑
四、边(Edge)是怎么指挥交通的?
边定义了控制流,告诉工作流「下一步该去哪」。
4.1 两种边
| 边类型 | 方法 | 说明 |
|---|---|---|
| 固定边 | addEdge(from, to) |
无条件,A 执行完一定去 B |
| 条件边 | addConditionalEdges(节点, 路由函数, 路由映射) |
根据返回值动态决定下一步 |
4.2 固定边
最简单的连线:
java
graph.addEdge("节点A", "节点B"); // A 执行完,无条件去 B
4.3 条件边
这个才是工作流的灵魂。
java
graph.addConditionalEdges(
"condition_node", // 条件节点
edge_async(state -> state.value("_condition_result", "default").toString()), // 路由函数
Map.of(
"error_handling", "error_node", // 值是 error_handling → 去 error_node
"data_processing", "process_node", // 值是 data_processing → 去 process_node
"default", StateGraph.END // 默认 → 结束
)
);
流程是这样的:
五、StateGraph:整个工作流的大管家
StateGraph 是构建工作流的核心类,所有节点、边都归它管。
5.1 使用流程
代码示例:
java
// 1. 创建 StateGraph(带状态策略)
StateGraph graph = new StateGraph(keyStrategyFactory);
// 2. 添加节点
graph.addNode("processor", node_async(new TextProcessorNode()));
// 3. 添加边
graph.addEdge(StateGraph.START, "processor");
graph.addEdge("processor", StateGraph.END);
// 4. 编译
CompiledGraph compiledGraph = graph.compile();
5.2 node_async 是啥?
node_async() 是个工具方法,把 NodeAction 包装成异步执行的节点。
为什么要异步?因为工作流可能涉及多个并行节点,异步执行才能发挥性能优势。
六、START 和 END:起点和终点
StateGraph 预定义了两个特殊节点:
| 常量 | 作用 | 说明 |
|---|---|---|
StateGraph.START |
工作流的起点 | 第一个执行的节点,不需要实现 |
StateGraph.END |
工作流的终点 | 执行到这里整个流程结束 |
这两个是虚拟节点,不需要写实现类,直接用就行。
java
graph.addEdge(StateGraph.START, "first_node"); // 从起点开始
graph.addEdge("last_node", StateGraph.END); // 到终点结束
七、门主踩坑:闭环!闭环!闭环!
说三遍是因为太重要了。
7.1 什么是闭环?
从 StateGraph.START 出发,沿着边能走到 StateGraph.END,中间不能有断开的路径。
7.2 正确示例 ✅
java
graph.addEdge(StateGraph.START, "A");
graph.addEdge("A", "B");
graph.addEdge("B", "C");
graph.addEdge("C", StateGraph.END);
流程:START → A → B → C → END ✅ 完整闭环
7.3 错误示例 ❌
java
graph.addEdge(StateGraph.START, "A");
graph.addEdge("A", "B");
graph.addEdge("B", "C");
// 没有连到 END!C 执行完就断了 → 运行时报错
条件边更容易踩坑:
java
graph.addConditionalEdges("A",
edge_async(state -> state.value("type").toString()),
Map.of(
"a", "B",
"b", "C"
// 如果 type 是 "c" 呢?没有匹配的路径 → 断流!
)
);
门主提醒:条件边的映射一定要覆盖所有可能的情况 ,实在拿不准就用 default 兜底。
八、节点和边是怎么协作的?
最后串起来看整个流程:
核心关系:
| 关系 | 说明 |
|---|---|
| 节点 → State | 节点读取 State 的输入,处理完后返回更新的 State |
| 边 → 节点 | 边决定哪个节点先执行、哪个后执行 |
| 条件边 → State | 条件边读取 State 的值来决定路由方向 |
| 节点 ↔ 节点 | 不直接通信,通过 State 间接传递数据 |
记住:节点之间不直接说话,都靠 State 传话。
👇 三连支持,动力源泉
如果这篇文章帮你省下了踩坑的时间,欢迎:
🔹 点赞 ------ 让更多人看到这篇干货 🔹 在看 ------ 你的认可是我持续输出的动力 🔹 转发 ------ 分享给身边正在做AI Agent的朋友
你的每一个小动作,对我都很重要 ❤️
🙏 关于作者
你好,我是 空门技术栈,一个常年和Bug战斗、持续填坑的Java开发者。
专注分享:
- ✅ Java / Spring Boot / Spring AI Alibaba 企业级实战
- ✅ RAG知识库、AI Agent、多智能体协作落地经验
- ✅ Docker部署、微服务架构、线上问题排查
- ✅ 偶尔聊聊「如何保住头发」这类程序员终极话题 😂
不搞水文,不贩卖焦虑,只写能跑通、能落地、能帮你少加班的实战内容。
关注我,咱们一起少踩坑,多写优雅代码。
📖 更多干货推荐
🤖 MCP / Spring AI
- 告别手动复制接口文档!Apifox MCP + AI 自动测试让开发效率起飞
- MySQL MCP Server 从零安装到使用实战,AI 直接查询数据库
- 别只会配 MCP!从 JSON-RPC 到 Streamable HTTP,带你用 Spring AI 2.0 搭建 MCP Server
🧠 Spring AI Alibaba / Agent / Workflow
- Spring AI Alibaba 多智能体(Multi-agent)实战:6 大协作模式 + 完整代码
- Spring AI Alibaba 智能体作为工具实战:别再让主 Agent 当"人肉路由器"了
- 一文搞懂 Spring AI Alibaba Workflow:10 个实战案例带你彻底掌握 AI 工作流编排
- Spring AI Alibaba Graph 学习代码
🔥 LangChain / 流式 / 结构化输出
- LangChain Agent 终于讲透了:短期记忆、Redis 持久化、Middleware 企业级实战,一篇带你从入门到生产
- LangChain 流式输出终于讲透了:6 种 stream_mode 一篇全搞懂
- Spring AI 流式对话踩坑:SSE 已关闭,为什么大模型还在继续生成?
- LangChain 结构化输出终于讲透了:ProviderStrategy、ToolStrategy、动态 Schema 一篇全会
☕ Java / Spring / 高并发
🤝 项目合作 / 技术咨询
平时工作之余,也会接一些技术项目和咨询,主要方向:
⚔️ 企业级开发
- Java / Spring Boot 项目开发与重构
- 微服务架构设计与落地
- 系统性能调优、线上问题排查
🤖 AI 应用落地(这是我最近的主力方向)
- Spring AI Alibaba / RAG / Agent 应用开发
- 企业私有知识库搭建
- AI能力接入现有业务系统
- 大模型本地化部署与调优
🛠️ 技术顾问 / 疑难Bug排查
- 项目架构评审与方案设计
- 线上疑难问题定位解决
- 技术选型与团队培训
如果你正遇到以下情况,欢迎找我聊聊:
- ✅ 想做AI项目,但技术方案拿不准
- ✅ 项目卡在某个Bug上很久,团队搞不定
- ✅ 想把AI接入现有业务,不知道从哪下手
- ✅ 需要靠谱的开发外包或长期技术顾问
📮 联系渠道(按回复速度排序):
- 最快:私信空门技术栈
- 邮件 :2929119150@qq.com(请注明来意和具体需求)
一个人踩坑,是事故;一群人踩坑,就是《避坑宝典》。
------ IT 空门,与诸君共修技术大道 😎