跳到主要内容Spring AI Alibaba Graph Workflow 深度解析 | 极客日志JavaAIjava
Spring AI Alibaba Graph Workflow 深度解析
综述由AI生成介绍 Spring AI Alibaba 的 Graph Workflow 图工作流机制,通过 State、Node、Edge 三大组件实现复杂 AI 任务的多步骤编排。内容涵盖状态管理策略 KeyStrategy 的作用与内置类型,对比了普通边、条件边与并行边的路由逻辑,并提供了智能客服路由实战案例。此外,还分析了 Graph Workflow 与 ReactAgent 的关系及适用场景,支持通过代码导出可视化图表,适用于需要自定义流程、多 Agent 协作及状态持久化的工程化落地场景。
技术博主30 浏览 一、Graph Workflow 是什么?
1.1 核心概念
Graph Workflow(图工作流) 是一种基于有向图(DAG)的流程编排机制。


1.2 Graph vs 可视化拖拽平台
说到工作流,很多人第一反应会是:都叫'图'了,那不就是可视化拖拽拼装吗?这里先明确一点:Spring AI Alibaba 的 Graph Workflow 并不是可视化拖拽平台。它和 n8n、Zapier、飞书多维表格自动化这类低代码工具,定位完全不同。
| 特性 | 可视化拖拽平台 | Spring AI Graph Workflow |
|---|
| 定义方式 | 🖱️ UI 拖拽 | 💻 Java 代码 |
| 适用人群 | 业务人员、零代码用户 | 开发者 |
| 灵活性 | 受平台能力限制 | 完全可编程,自由度拉满 |
| 调试 | 平台日志查看 | 代码级调试 |
| 版本控制 | 平台内管理 | Git 友好 |
| 可视化 | ✅ 原生支持 | ✅ 可导出 Mermaid/PlantUML 图 |
二者在底层逻辑上其实是相通的,本质都是有向图(DAG),都由节点(Node)和边(Edge)构成,支持条件分支、循环逻辑,也都会管理任务的状态流转。
但它们的设计思路截然不同:Spring AI Graph 没有提供拖拽式 UI,也不主打零代码,而是以代码为核心,更适合复杂 AI 应用的工程化落地。同时它又保留了可视化能力,能通过代码直接生成流程图,在可编程性与可读性之间做到了很好的平衡。
二、Graph Workflow 三大核心组件

2.1 State(状态)
State 是什么?
- 在整个 Graph 执行过程中流转的数据
- 本质是
Map<String, Object>
- 每个节点都可以读取和修改 State
Map<String, Object> state = Map.of(
, ,
, ,
, ,
, List.of(, )
);
"user_query"
"北京今天天气怎么样?"
"intent"
"weather_query"
"result"
"北京今天晴,15°C"
"messages"
"msg1"
"msg2"
2.2 Node(节点)
- 执行具体业务逻辑的单元
- 接收 State,处理后返回新的 State
- 可以是同步或异步
public class WeatherNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
String city = (String) state.value("city").orElse("北京");
String weather = getWeather(city);
return Map.of("weather", weather);
}
}
2.3 Edge(边)
workflow.addEdge("nodeA", "nodeB");
workflow.addConditionalEdges("classifier", edge_async(state -> {
String intent = (String) state.value("intent").orElse("default");
return intent;
}), Map.of(
"weather", "weather_node",
"news", "news_node",
"default", "default_node"
));
三、构建你的第一个 Graph Workflow
3.1 完整流程
KeyStrategyFactory keyStrategyFactory = () -> {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("messages", new AppendStrategy());
strategies.put("result", new ReplaceStrategy());
return strategies;
};
StateGraph workflow = new StateGraph(keyStrategyFactory);
workflow.addNode("nodeA", node_async(new NodeA()));
workflow.addNode("nodeB", node_async(new NodeB()));
workflow.addEdge(START, "nodeA");
workflow.addEdge("nodeA", "nodeB");
workflow.addEdge("nodeB", END);
CompiledGraph compiled = workflow.compile();
Map<String, Object> initialState = Map.of("input", "Hello");
OverAllState result = compiled.invoke(initialState, config).orElseThrow();
3.2 小 Demo 案例:智能客服路由
public class CustomerServiceGraph {
public static class ClassifyNode implements NodeAction {
private final ChatClient chatClient;
@Override
public Map<String, Object> apply(OverAllState state) {
String query = (String) state.value("user_query").orElse("");
String prompt = String.format("""
分类用户意图(只返回类别):
- technical: 技术问题
- billing: 账单问题
- general: 一般咨询
用户问题:%s
""", query);
String intent = chatClient.prompt()
.user(prompt)
.call()
.content()
.trim()
.toLowerCase();
return Map.of("intent", intent);
}
}
public static class TechnicalSupportNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) {
String query = (String) state.value("user_query").orElse("");
String answer = searchTechDocs(query);
return Map.of("answer", answer);
}
}
public static class BillingNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) {
String query = (String) state.value("user_query").orElse("");
String answer = queryBillingSystem(query);
return Map.of("answer", answer);
}
}
public static CompiledGraph createGraph(ChatModel chatModel) {
ChatClient.Builder builder = ChatClient.builder(chatModel);
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("user_query", new ReplaceStrategy());
strategies.put("intent", new ReplaceStrategy());
strategies.put("answer", new ReplaceStrategy());
return strategies;
};
StateGraph workflow = new StateGraph(factory)
.addNode("classify", node_async(new ClassifyNode(builder.build())))
.addNode("technical", node_async(new TechnicalSupportNode()))
.addNode("billing", node_async(new BillingNode()))
.addNode("general", node_async(new GeneralNode()));
workflow.addEdge(START, "classify");
workflow.addConditionalEdges("classify", edge_async(state -> (String) state.value("intent").orElse("general")), Map.of(
"technical", "technical",
"billing", "billing",
"general", "general"
));
workflow.addEdge("technical", END);
workflow.addEdge("billing", END);
workflow.addEdge("general", END);
return workflow.compile();
}
public static void main(String[] args) {
CompiledGraph graph = createGraph(chatModel);
Map<String, Object> initialState = Map.of(
"user_query", "我的软件无法启动"
);
var config = RunnableConfig.builder()
.threadId("customer_001")
.build();
graph.stream(initialState, config)
.doOnNext(output -> {
System.out.println("节点:" + output.node());
System.out.println("状态:" + output.state().data());
})
.blockLast();
}
}
四、状态管理:KeyStrategy
4.1 什么是 KeyStrategy?
KeyStrategy(键策略) 是 Graph Workflow 中用于控制状态更新行为的核心机制。
public interface KeyStrategy {
Object apply(Object oldValue, Object newValue);
}
作用:当节点返回新的状态数据时,KeyStrategy 决定如何将新值与旧值合并。
4.2 什么是 KeyStrategyFactory?
KeyStrategyFactory 是一个工厂接口,用于为 State 中的每个键指定更新策略。
public interface KeyStrategyFactory {
Map<String, KeyStrategy> getStrategies();
}
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("messages", new AppendStrategy());
strategies.put("result", new ReplaceStrategy());
strategies.put("counter", new CustomStrategy());
return strategies;
};
4.3 为什么需要 KeyStrategy?
问题场景:当多个节点修改同一个 State 键时,如何合并?
4.4 工作原理
4.5 内置策略详解
1️⃣ ReplaceStrategy(替换策略)
strategies.put("result", new ReplaceStrategy());
2️⃣ AppendStrategy(追加策略)
strategies.put("messages", new AppendStrategy());
3️⃣ 自定义策略
strategies.put("counter", (oldValue, newValue) -> {
int old = oldValue != null ? (int) oldValue : 0;
int add = (int) newValue;
return old + add;
});
strategies.put("metadata", (oldValue, newValue) -> {
Map<String, Object> old = (Map<String, Object>) oldValue;
Map<String, Object> newMap = (Map<String, Object>) newValue;
Map<String, Object> merged = new HashMap<>(old);
merged.putAll(newMap);
return merged;
});
strategies.put("tags", (oldValue, newValue) -> {
Set<String> tags = new HashSet<>((List<String>) oldValue);
tags.add((String) newValue);
return new ArrayList<>(tags);
});
4.6 完整的实现 KeyStrategyFactory 示例
public class MyKeyStrategyFactory implements KeyStrategyFactory {
@Override
public Map<String, KeyStrategy> getStrategies() {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("user_query", new ReplaceStrategy());
strategies.put("intent", new ReplaceStrategy());
strategies.put("result", new ReplaceStrategy());
strategies.put("status", new ReplaceStrategy());
strategies.put("messages", new AppendStrategy());
strategies.put("history", new AppendStrategy());
strategies.put("logs", new AppendStrategy());
strategies.put("token_count", (oldValue, newValue) -> {
int old = oldValue != null ? (int) oldValue : 0;
return old + (int) newValue;
});
strategies.put("metadata", (oldValue, newValue) -> {
if (oldValue == null) return newValue;
Map<String, Object> merged = new HashMap<>((Map) oldValue);
merged.putAll((Map) newValue);
return merged;
});
return strategies;
}
}
StateGraph workflow = new StateGraph(new MyKeyStrategyFactory());
4.7 KeyStrategy 最佳实践
✅ 好的实践
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("user_input", new ReplaceStrategy());
strategies.put("conversation", new AppendStrategy());
strategies.put("final_answer", new ReplaceStrategy());
strategies.put("error", new ReplaceStrategy());
strategies.put("metadata", new ReplaceStrategy());
return strategies;
};
❌ 不好的实践
KeyStrategyFactory factory = () -> {
Map<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("messages", new AppendStrategy());
strategies.put("result", new AppendStrategy());
return strategies;
};
4.8 默认行为
如果某个键没有定义 KeyStrategy,会怎样?
建议:为所有可能出现的键都显式定义策略,避免依赖默认行为。
五、边的类型详解
5.1 普通边(固定路由)
workflow.addEdge("nodeA", "nodeB");
workflow.addEdge(START, "nodeA");
workflow.addEdge("nodeZ", END);
5.2 条件边(相当于动态路由)
workflow.addConditionalEdges(
"classifier",
edge_async(state -> {
String type = (String) state.value("type").orElse("default");
if ("urgent".equals(type)) {
return "urgent_handler";
} else if ("normal".equals(type)) {
return "normal_handler";
} else {
return "default_handler";
}
}),
Map.of(
"urgent_handler", "urgent_handler",
"normal_handler", "normal_handler",
"default_handler", "default_handler"
)
);
5.3 并行边
workflow.addEdge("A", List.of("B1", "B2", "B3"));
workflow.addEdge(List.of("B1", "B2", "B3"), "C");
六、可视化导出
虽然 Graph Workflow 是代码定义的,但可以导出为可视化图表!
GraphRepresentation mermaid = workflow.getGraph(
GraphRepresentation.Type.MERMAID,
"Customer Service Workflow"
);
System.out.println(mermaid.getContent());
将输出的 Mermaid 代码粘贴到支持 Mermaid 的平台(如 GitHub、Notion),即可看到可视化图表!
七、Graph vs ReactAgent
7.1 ReactAgent 的本质
ReactAgent 本质上就是一个预定义的 StateGraph!
StateGraph reactGraph = new StateGraph()
.addNode("llm_node", llmNode)
.addNode("tool_node", toolNode)
.addEdge(START, "llm_node")
.addConditionalEdges("llm_node", edge_async(state -> {
return needsTools(state) ? "tool_node" : END;
}), Map.of("tool_node", "tool_node", END, END))
.addEdge("tool_node", "llm_node");
7.2 何时使用 Graph?何时使用 ReactAgent?
| 场景 | 使用 | 原因 |
|---|
| 标准的 ReAct 循环 | ReactAgent | 开箱即用 |
| 自定义复杂流程 | StateGraph | 完全控制 |
| 多个 Agent 协作 | StateGraph | 灵活编排 |
| 需要人工介入 | StateGraph | 支持中断点 |
| 并行执行任务 | StateGraph | 支持并行边 |
八、总结
8.1 核心要点回顾
- Graph Workflow 是底层机制
- ReactAgent 基于 Graph 实现
- 提供完全的控制能力
- 三大核心组件
- State:数据流转
- Node:业务逻辑
- Edge:执行顺序
- 与可视化平台的对比
- 本质相同(都是 DAG)
- 形式不同(代码 vs UI)
- 各有优势
- 灵活的编排能力
8.2 Graph Workflow vs ReactAgent
| 特性 | Graph Workflow | ReactAgent |
|---|
| 核心能力 | 自定义流程编排 | 标准 ReAct 循环 |
| 适用场景 | 复杂流程、多 Agent 协作 | 标准工具调用 |
| 可组合性 | ✅ ReactAgent 基于它实现 | ✅ 可以作为 Graph 的节点 |
参考资源
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online