之前做个几个大模型的应用,都是使用Python语言,后来有一个项目使用了Java,并使用了Spring AI框架。随着Spring AI不断地完善,最近它发布了1.0正式版,意味着它已经能很好的作为企业级生产环境的使用。对于Java开发者来说真是一个福音,其功能已经能满足基于大模型开发企业级应用。借着这次机会,给大家分享一下Spring AI框架。
注意:由于框架不同版本改造会有些使用的不同,因此本次系列中使用基本框架是 Spring AI-1.0.0,JDK版本使用的是19,Spring-AI-Alibaba-1.0.0.3-SNAPSHOT。
代码参考: https://github.com/forever1986/springai-study
上一章讲解了Spring AI Alibaba如何基于Nacos构建企业级的MCP服务,这一章将讲解Graph框架,该Graph框架参考来自Langgraph,主要是用于搭建智能体。
1 Spring AI Alibaba Graph
Spring AI Alibaba Graph 模块是其核心之一,也是整个框架在设计理念上区别于 Spring AI 只做底层原子抽象的地方,Spring AI Alibaba 期望帮助开发者更容易的构建智能体应用。基于 Graph 开发者可以构建工作流、多智能体应用。Spring AI Alibaba Graph 在设计理念上借鉴 Langgraph,因此在一定程度上可以理解为是 Java 版的 Langgraph 实现,社区在此基础上增加了大量预置 Node、简化了 State 定义过程等,让开发者更容易编写对等低代码平台的工作流、多智能体等。
1.1 核心概念
Graph 是通过构建一个有向图的方式,构造Agent的工作流,其中有几个核心概念需要了解:
- StateGraph(状态图):定义整个工作流的主类,支持添加节点和边
- Node(节点):表示工作流中的单个步骤,可封装模型调用或自定义逻辑
- Edge(边):表示节点之间的转移关系,支持条件分支
- OverAllState(全局状态):贯穿整个工作流的可序列化状态对象
- CompiledGraph(已编译图):StateGraph 的可执行版本
1.2 关键源码
StateGraph 类是构造图的关键,其几个变量非常重要,下面截取关键代码注释:
public class StateGraph {
/**
* 表示图表的结束部分
*/
public static final String END = "__END__";
/**
* 表示该图表的起始点
*/
public static final String START = "__START__";
/**
* 代表图表中的“错误”
*/
public static final String ERROR = "__ERROR__";
/**
* 图中的节点
*/
final Nodes nodes = new Nodes();
/**
* 图中的边
*/
final Edges edges = new Edges();
/**
* 用于创建整体状态实例的工厂
*/
private OverAllStateFactory overAllStateFactory;
/**
* 提供状态的策略工厂(替换或者追加)
*/
private KeyStrategyFactory keyStrategyFactory;
/**
* 图的名字
*/
private String name;
}
Node 节点,其真正执行的代码是NodeAction,NodeAction有几个已有的实现:

- LlmNode:专门处理与大语言模型的交互,支持同步和流式执行模式
- ToolNode:负责执行 LLM 响应中的工具调用
- HumanNode:实现人机交互功能,通过中断图执行等待人工反馈
- HttpNode:负责调用HTTP请求外部信息
- McpNode:负责调用MCP服务的
- DocumentExtractorNode:负责获取外部文档
通过不同Node的节点,构建一个StateGraph,这样就构建了一个工作流,而这工作流其实就是一个Agent。下面通过一个入门演示案例作为入手,初步了解Spring AI Alibaba-Graph框架
2 入门示例
代码参考lesson26子模块下的graph-get-start子模块
示例说明:通过构建一个StateGraph,它只有一个中间节点,该节点的作用就是把用户输入的问题扩展成不同角度的多个问题。注意:为了演示Graph框架是一个独立的模块,这里使用智谱聊天大模型GLM-4-Flash-250414,而非阿里的千问模型,也就是说它并没有和dashscope 模块的模型强依赖

说明:上图就是代码打印的PlantUML格式,将其新建一个PlantUML文件展示的效果
1)新建lesson26子模块
2)在lesson26子模块下,新建graph-get-start子模块,引入pom如下:
<dependencies>
<!-- 引入智谱的model插件 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 需要引入gson插件 -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
2)新建application.properties配置文件
# 聊天模型
spring.ai.zhipuai.api-key=你的智谱API KEY
spring.ai.zhipuai.chat.options.model=GLM-4-Flash-250414
spring.ai.zhipuai.chat.options.temperature=0.7
3)自定义ExpanderNode节点,用于将用户问题变成多角度的多个问题
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import reactor.core.publisher.Flux;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 自定义的ExpanderNode节点:将用户的问题,
*/
public class ExpanderNode implements NodeAction {
private static final PromptTemplate DEFAULTPROMPTTEMPLATE = new PromptTemplate("""
您是信息检索和搜索优化方面的专家。
您的任务是生成给定查询的 {number} 种不同版本。
每个变体都必须涵盖该主题的不同视角或方面,同时保持原始查询的核心意图。其目的是扩大搜索范围,并提高找到相关信息的可能性。
请勿解释您的选择或添加任何其他文字。
请将查询变体以换行的方式分隔展示。
原始查询:{query}
查询变体:
""");
private final ChatClient chatClient;
private final Integer NUMBER = 3;
public ExpanderNode(ChatClient.Builder chatClientBuilder) {
this.chatClient = chatClientBuilder.build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
String query = state.value("query", "");
Integer expanderNumber = state.value("expandernumber", this.NUMBER);
Flux<String> streamResult = this.chatClient.prompt().user((user) -> user.text(DEFAULTPROMPTTEMPLATE.getTemplate()).param("number", expanderNumber).param("query", query)).stream().content();
String result = streamResult.reduce("", (acc, item) -> acc + item).block();
List<String> queryVariants = Arrays.asList(result.split("\n"));
HashMap<String, Object> resultMap = new HashMap<>();
resultMap.put("expandercontent", queryVariants);
return resultMap;
}
}
4)新建配置类GraphConfiguration,配置StateGraph图
import com.alibaba.cloud.ai.graph.GraphRepresentation;
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.KeyStrategyFactory;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.demo.lesson26.getstart.node.ExpanderNode;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
public class GraphConfiguration {
@Bean
public StateGraph simpleGraph(ChatClient.Builder chatClientBuilder) throws GraphStateException {
// 全局变量的替换策略(ReplaceStrategy为替换,AppendStrategy为追加)
KeyStrategyFactory keyStrategyFactory = () -> {
HashMap<String, KeyStrategy> keyStrategyHashMap = new HashMap<>();
// 用户输入
keyStrategyHashMap.put("query", new ReplaceStrategy());
keyStrategyHashMap.put("expandernumber", new ReplaceStrategy());
keyStrategyHashMap.put("expandercontent", new ReplaceStrategy());
return keyStrategyHashMap;
};
// 构造图
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
// 节点ExpanderNode
.addNode("expander", AsyncNodeAction.node_async(new ExpanderNode(chatClientBuilder)))
// 边:START -> ExpanderNode
.addEdge(StateGraph.START, "expander")
// 边:ExpanderNode -> END
.addEdge("expander", StateGraph.END);
// 将图打印出来,可以使用 PlantUML 插件查看
GraphRepresentation representation = stateGraph.getGraph(GraphRepresentation.Type.PLANTUML,
"expander flow");
System.out.println("\n=== expander UML Flow ===");
System.out.println(representation.content());
System.out.println("==================================\n");
return stateGraph;
}
}
5)配置SimpleGraphController 演示类:
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.StateGraph;
import com.alibaba.cloud.ai.graph.exception.GraphRunnerException;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
@RestController
public class SimpleGraphController {
private final CompiledGraph compiledGraph;
public SimpleGraphController(@Qualifier("simpleGraph") StateGraph stateGraph) throws GraphStateException {
// 将图编译成CompiledGraph
this.compiledGraph = stateGraph.compile();
}
@GetMapping(value = "/graph/expand")
public Map<String, Object> expand(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?", required = false) String query,
@RequestParam(value = "expandernumber", defaultValue = "3", required = false) Integer expanderNumber,
@RequestParam(value = "threadid", defaultValue = "ceshi", required = false) String threadId) throws GraphRunnerException {
// 构建会话配置
RunnableConfig runnableConfig = RunnableConfig.builder().threadId(threadId).build();
// 入参配置
Map<String, Object> objectMap = new HashMap<>();
objectMap.put("query", query);
objectMap.put("expandernumber", expanderNumber);
// 调用图
Optional<OverAllState> invoke = this.compiledGraph.invoke(objectMap, runnableConfig);
return invoke.map(OverAllState::data).orElse(new HashMap<>());
}
}
6)配置Lesson26GetstartApplication启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Lesson26GetstartApplication {
public static void main(String[] args) {
SpringApplication.run(Lesson26GetstartApplication.class, args);
}
}
7)演示效果
http://localhost:8080/graph/expand

结语:本章通过讲解Graph框架,初步认识构建一个工作流或者智能体的基本流程。这就是Spring AI Alibaba相对于Spring AI 的重要扩展之一。接下来几章,将展现Graph框架不同的节点和工作流,配合更复杂一点的功能,让你认识一下Spring AI Alibaba的Graph框架强大
Spring AI系列上一章:《Spring AI 系列之三十一 - Spring AI Alibaba-基于Nacos的MCP》
Spring AI系列下一章:《Spring AI 系列之三十三 - Spring AI Alibaba-Graph框架之人类反馈》
347

被折叠的 条评论
为什么被折叠?



