Spring AI Graph 工作流实战指南
概述
Spring AI Graph 是一个强大的工作流编排框架,支持复杂的业务逻辑流程。本项目展示了两种常见的工作流模式:循环工作流和条件分支工作流。
项目结构
src/main/java/com/xinggui/springaigraph/
├── demo6/ # 条件分支工作流
│ ├── Demo6Node.java # 条件分支节点实现
│ └── Demo6Graph.java # 条件分支图配置
├── demo7/ # 循环工作流
│ ├── Demo7Node.java # 循环执行节点实现
│ └── Demo7Graph.java # 循环执行图配置
└── DemoController.java # 控制器入口
1. 条件分支工作流 (Demo6)
条件分支工作流根据不同的输入条件,选择不同的执行路径。适用于需要根据业务规则进行决策的场景。
1.1 节点实现 (Demo6Node.java)
java
package com.xinggui.springaigraph.demo6;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import java.util.Map;
/**
* 条件分支工作流节点实现
* 包含输入处理、通过条件处理、失败条件处理三个节点
*/
@Slf4j
public class Demo6Node {
/**
* 输入处理节点:处理用户输入并返回处理结果
*/
static class InputNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从状态中获取输入文本
String inputText = (String) state.value("inputText").orElse("");
log.info("处理输入: {}", inputText);
// 返回处理后的输入,键名与Graph中定义的节点名一致
return Map.of("inputText", inputText);
}
}
/**
* 通过条件处理节点:处理满足通过条件的业务逻辑
*/
public static class PassNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) {
String message = (String) state.value("inputText").orElse("");
log.info("这是 PassNode: {}", message);
return Map.of("result", message + " ✅");
}
}
/**
* 失败条件处理节点:处理不满足通过条件的业务逻辑
*/
public static class FailNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) {
String message = (String) state.value("inputText").orElse("");
log.info("这是 FailNode: {}", message);
return Map.of("resultTwo", message + " ❌");
}
}
}
1.2 图配置 (Demo6Graph.java)
java
package com.xinggui.springaigraph.demo6;
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.OverAllStateFactory;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.action.AsyncEdgeAction;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* 条件分支工作流图配置
* 实现基于输入值的条件路由逻辑
*/
@Configuration
@Slf4j
public class Demo6Graph {
@Bean("demoSixsGraph")
public StateGraph demo6Graph() throws Exception {
// 初始化状态工厂,注册所有需要的键和策略
OverAllStateFactory factory = () -> {
OverAllState s = new OverAllState();
s.registerKeyAndStrategy("inputText", new ReplaceStrategy()); // 输入文本,值覆盖策略
s.registerKeyAndStrategy("result", new ReplaceStrategy()); // 处理结果,值覆盖策略
s.registerKeyAndStrategy("resultTwo", new ReplaceStrategy()); // 失败结果,值覆盖策略
return s;
};
// 构建状态图
StateGraph stateGraph = new StateGraph("ChainGraph", factory);
// 添加节点:每个节点对应一个具体的业务逻辑
stateGraph.addNode("inputText", AsyncNodeAction.node_async(new Demo6Node.InputNode(chatClient)))
.addNode("result", AsyncNodeAction.node_async(new Demo6Node.PassNode(chatClient)))
.addNode("resultTwo", AsyncNodeAction.node_async(new Demo6Node.FailNode(chatClient)))
// 添加边:定义节点之间的执行顺序
.addEdge(StateGraph.START, "inputText") // 从开始到输入处理
// 添加条件边:根据输入值决定下一步执行路径
.addConditionalEdges("inputText", AsyncEdgeAction.edge_async(t -> {
String checked = (String) t.value("inputText").orElse("fail");
// 条件判断逻辑:如果输入是"pass"则走通过分支,否则走失败分支
return checked.equalsIgnoreCase("pass") ? "pass" : "fail";
}), Map.of(
"pass", "result", // 通过条件 → 执行通过逻辑
"fail", "resultTwo" // 失败条件 → 执行失败逻辑
))
// 为每个分支添加结束边
.addEdge("result", StateGraph.END) // 通过分支结束
.addEdge("resultTwo", StateGraph.END); // 失败分支结束
// 生成PlantUML流程图,便于理解和调试
GraphRepresentation representation = stateGraph.getGraph(GraphRepresentation.Type.PLANTUML,
"chain flow");
System.out.println("\n=== Chain UML Flow ===");
System.out.println(representation.content());
System.out.println("==================================\n");
return stateGraph;
}
}
1.3 工作流逻辑
START → inputText → [条件判断]
├─ "pass" → result → END
└─ 其他值 → resultTwo → END
关键特性:
- 使用
addConditionalEdges
实现条件分支 - 条件判断基于输入值进行路由
- 每个分支都有明确的结束点
2. 循环工作流 (Demo7)
循环工作流适用于需要重复执行某个任务直到满足特定条件的场景,比如数据验证、内容优化等。
2.1 节点实现 (Demo7Node.java)
java
package com.xinggui.springaigraph.demo7;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
* 循环工作流节点实现
* 包含输入初始化、循环执行、失败处理三个节点
*/
@Slf4j
public class Demo7Node {
/**
* 输入节点:初始化输入值,为循环提供起始点
*/
static class InputDemoSevenNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从状态中获取输入值,默认为1
Integer inputText = (Integer) state.value("inputText").orElse(1);
log.info("初始化输入值: {}", inputText);
// 返回current键,用于后续循环中的状态传递
return Map.of("current", inputText);
}
}
/**
* 循环执行节点:每次执行后判断是否继续循环
* 这是循环工作流的核心节点
*/
static class AddDemoSevenNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 获取当前循环的值
Integer current = (Integer) state.value("current").orElse(1);
log.info("当前值: {}, 执行 +1 操作", current);
// 循环终止条件:当值大于等于5时结束
if (current >= 5) {
log.info("当前值 {} >= 5,循环结束", current);
return Map.of("result", "PASS"); // 达到条件,返回PASS状态
}
// 继续循环:值+1,并返回继续循环的状态
Integer newValue = current + 1;
log.info("值从 {} 增加到 {}", current, newValue);
return Map.of(
"result", "NEEDS_IMPROVEMENT", // 需要继续改进
"current", newValue // 更新后的值
);
}
}
/**
* 失败处理节点:处理循环过程中的异常情况
*/
static class FailDemoSevenNode implements NodeAction {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
log.info("执行失败处理逻辑");
// 这里可以添加失败处理的具体业务逻辑
// 比如记录错误日志、发送告警等
return Map.of("result", "FAIL");
}
}
}
2.2 图配置 (Demo7Graph.java)
java
package com.xinggui.springaigraph.demo7;
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.OverAllStateFactory;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.action.AsyncEdgeAction;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
/**
* 循环工作流图配置
* 实现基于条件的循环执行逻辑
*/
@Configuration
@Slf4j
public class Demo7Graph {
@Bean("demoSevenGraph")
public StateGraph stateGraph() throws GraphStateException {
// 初始化状态工厂,注册循环工作流需要的所有键
OverAllStateFactory factory = () -> {
OverAllState s = new OverAllState();
s.registerKeyAndStrategy("inputText", new ReplaceStrategy()); // 初始输入值
s.registerKeyAndStrategy("current", new ReplaceStrategy()); // 当前循环值
s.registerKeyAndStrategy("result", new ReplaceStrategy()); // 循环结果状态
return s;
};
// 构建状态图
StateGraph stateGraph = new StateGraph("demoSeven", factory)
// 添加节点定义
.addNode("inputText", AsyncNodeAction.node_async(new Demo7Node.InputDemoSevenNode()))
.addNode("current", AsyncNodeAction.node_async(new Demo7Node.AddDemoSevenNode()))
.addNode("fail", AsyncNodeAction.node_async(new Demo7Node.FailDemoSevenNode()))
// 添加边:定义执行流程
.addEdge(StateGraph.START, "inputText") // 开始 → 输入初始化
.addEdge("inputText", "current") // 输入初始化 → 循环执行
// 添加条件边:实现循环逻辑的核心
.addConditionalEdges("current", AsyncEdgeAction.edge_async(state -> {
// 从状态中获取循环结果
String result = (String) state.value("result").orElse("NEEDS_IMPROVEMENT");
log.info("条件边判断结果: {}", result);
return result;
}), Map.of(
"PASS", StateGraph.END, // 达到条件,结束循环
"NEEDS_IMPROVEMENT", "current", // 需要改进,继续循环
"FAIL", "fail" // 执行失败,进入失败处理
))
// 失败处理后的结束边
.addEdge("fail", StateGraph.END);
// 生成PlantUML流程图,便于理解循环逻辑
GraphRepresentation representation = stateGraph.getGraph(GraphRepresentation.Type.PLANTUML,
"demo-seven flow");
System.out.println("\n=== Demo Seven UML Flow ===");
System.out.println(representation.content());
System.out.println("==================================\n");
return stateGraph;
}
}
2.3 工作流逻辑
START → inputText → current → [条件判断]
├─ PASS (>=5) → END
├─ NEEDS_IMPROVEMENT (<5) → current (循环)
└─ FAIL → fail → END
关键特性:
- 使用条件边实现循环逻辑
- 状态在每次循环中更新
- 明确的循环终止条件
3. 控制器实现 (DemoController.java)
java
package com.xinggui.springaigraph;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Optional;
/**
* 工作流控制器
* 提供REST API接口来测试不同的工作流
*/
@RestController
@Slf4j
public class DemoController {
/**
* 注入循环工作流图
*/
@Resource(name = "demoSevenGraph")
private StateGraph stateGraph;
/**
* 测试条件分支工作流
* 根据query参数的值,执行不同的分支逻辑
*
* @param query 查询参数,支持"pass"、"fail"等值
* @return 工作流执行结果
*/
@GetMapping("/code4")
public Object code4Graph(String query) throws GraphStateException, GraphRunnerException {
log.info("测试条件分支工作流,输入参数: {}", query);
// 构建输入参数
HashMap<String, Object> map = new HashMap<>();
map.put("inputText", query);
// 编译并执行工作流
var executableGraph = stateGraph.compile();
Optional<OverAllState> result = executableGraph.invoke(map);
// 返回执行结果
return result.map(OverAllState::data).orElse(new HashMap<>());
}
/**
* 测试循环工作流
* 从初始值1开始,每次+1,直到达到5时结束
*
* @return 循环执行结果
*/
@GetMapping("/code5")
public Object code5Graph() throws GraphStateException, GraphRunnerException {
log.info("测试循环工作流,从1开始循环到5");
// 构建输入参数:初始值为1
HashMap<String, Object> map = new HashMap<>();
map.put("inputText", 1);
// 编译并执行工作流
var executableGraph = stateGraph.compile();
Optional<OverAllState> result = executableGraph.invoke(map);
// 返回执行结果
return result.map(OverAllState::data).orElse(new HashMap<>());
}
}
5. 使用示例
5.1 条件分支测试
bash
# 测试通过分支
GET /code4?query=pass
# 预期:执行通过逻辑,返回通过结果
# 测试失败分支
GET /code4?query=fail
# 预期:执行失败逻辑,返回失败结果
# 测试其他值
GET /code4?query=other
# 预期:执行默认分支逻辑
5.2 循环执行测试
bash
# 测试循环工作流
GET /code5
# 预期执行流程:
# 1. 初始值: 1
# 2. 第一次循环: 1 + 1 = 2
# 3. 第二次循环: 2 + 1 = 3
# 4. 第三次循环: 3 + 1 = 4
# 5. 第四次循环: 4 + 1 = 5
# 6. 达到条件,循环结束
10. 总结
Spring AI Graph 提供了灵活而强大的工作流编排能力。通过条件分支和循环执行,我们可以构建复杂的业务逻辑流程。
关键要点:
- 状态管理:合理使用ReplaceStrategy和AppendStrategy
- 条件分支:使用addConditionalEdges实现灵活的路由
- 循环控制:确保循环有明确的终止条件
- 错误处理:为异常情况提供处理路径
- 性能优化:合理设置循环上限,优化状态管理
- 监控调试:添加详细的日志和性能指标
这两种工作流模式可以单独使用,也可以组合使用,为各种业务场景提供解决方案。在实际应用中,建议根据具体需求选择合适的工作流模式,并注意性能优化和错误处理。