Spring AI Graph工作流案例


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 提供了灵活而强大的工作流编排能力。通过条件分支和循环执行,我们可以构建复杂的业务逻辑流程。

关键要点:

  1. 状态管理:合理使用ReplaceStrategy和AppendStrategy
  2. 条件分支:使用addConditionalEdges实现灵活的路由
  3. 循环控制:确保循环有明确的终止条件
  4. 错误处理:为异常情况提供处理路径
  5. 性能优化:合理设置循环上限,优化状态管理
  6. 监控调试:添加详细的日志和性能指标

这两种工作流模式可以单独使用,也可以组合使用,为各种业务场景提供解决方案。在实际应用中,建议根据具体需求选择合适的工作流模式,并注意性能优化和错误处理。

相关推荐
小毛驴8504 小时前
maven 常用指令
java·数据库·maven
Sagittarius_A*4 小时前
SpringBoot Web 入门指南:从零搭建第一个SpringBoot程序
java·前端·spring boot·后端
LB21124 小时前
MyBatis xml配置文件
xml·java·mybatis
代码AI弗森4 小时前
AR-LSAT 推理任务全解析:从逻辑推理到类比推理的挑战
人工智能·restful
徐礼昭|商派软件市场负责人4 小时前
最新!阿里财报电话会蒋凡与吴泳铭透露重要信息:淘宝闪购成绩斐然;零售与AI双轮驱动;阿里云推出“Agent Bay”新产品···
人工智能·阿里云·零售
LaiYoung_4 小时前
前端国际化适配提速 90%!这款 JS 脚本 CLI 工具,自动提中文、分模块、做替换,比 AI 更稳定
前端·javascript·人工智能
微信bysj7984 小时前
基于深度学习的车牌识别系统(源码+文档
java·人工智能·spring boot·后端·深度学习·微信小程序·小程序
一叶飘零_sweeeet4 小时前
从 0 到 1 吃透 Nacos:服务发现与配置中心的终极实践指南
java·分布式·服务发现