原文链接:Spring Ai Alibaba Graph 快速入门
教程说明
说明:本教程将采用2025年5月20日正式的GA版,给出如下内容
- 核心功能模块的快速上手教程
- 核心功能模块的源码级解读
- Spring ai alibaba增强的快速上手教程 + 源码级解读
版本:JDK21 + SpringBoot3.4.5 + SpringAI 1.0.0 + SpringAI Alibaba 1.0.0.2
将陆续完成如下章节教程。本章是第十章(Graph构建智能体)下的Graph快速入门
代码开源如下:github.com/GTyingzi/sp…
微信推文往届解读可参考:
第一章内容
SpringAI(GA)的chat:快速上手+自动注入源码解读
第二章内容
SpringAI(GA):Sqlite、Mysql、Redis消息存储快速上手
第三章内容
第四章内容
第五章内容
SpringAI(GA):内存、Redis、ES的向量数据库存储—快速上手
SpringAI(GA):向量数据库理论源码解读+Redis、Es接入源码
第六章内容
第七章内容
SpringAI(GA): SpringAI下的MCP源码解读
第八章内容
第九章内容
整理不易,获取更好的观赏体验,可付费获取飞书云文档Spring AI最新教程权限,目前59.9,随着内容不断完善,会逐步涨价。
注:M6版快速上手教程+源码解读飞书云文档已免费提供
为鼓励大家积极参与为Spring Ai Alibaba开源社区:github.com/alibaba/spr…
Graph 构建智能体 — 快速入门篇
[!TIP] Spring Ai Alibaba Graph 在设计理念上大幅参考 Langgraph,其中涵盖了全局 State 管理、Graph 定义,基本等同于 Langgraph(python 实现)的 Java 平替
基于Langgraph构建的相关智能方案可见:LangGraph详解及智能方案
框架代码地址:github.com/alibaba/spr…
以下是最简单的一个 graph 示例,实现对用户问题的扩展几条相似的
实战代码可见:github.com/GTyingzi/sp… 下的 graph 目录,本章代码为其 simple 模块
其他快速上手案例可见:《多节点并行案例》、《Graph 节点流式透传案例》、《人类反馈复原案例》、《Graph 节点接入 MCP 案例》
pom.xml
这里使用 1.0.0.3-SNAPSHOT。在定义 StateGraph 方面和 1.0.0.2 有些变动
<properties>
<spring-ai-alibaba.version>1.0.0.3-SNAPSHOT</spring-ai-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-graph-core</artifactId>
<version>${spring-ai-alibaba.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 8080
spring:
application:
name: simple
ai:
openai:
api-key: ${AI_DASHSCOPE_API_KEY}
base-url: https://dashscope.aliyuncs.com/compatible-mode
chat:
options:
model: qwen-max
config
OverAllState 中存储的字段
- query:用户的问题
- expander_number:扩展的数量
- expander_content:扩展的内容
定义 ExpanderNode,边的连接为:START -> expander -> END
package com.spring.ai.tutorial.graph.config;
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.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.spring.ai.tutorial.graph.node.ExpanderNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction._node_async_;
@Configuration
public class GraphConfiguration {
private static final Logger _logger _= LoggerFactory._getLogger_(GraphConfiguration.class);
@Bean
public StateGraph simpleGraph(ChatClient.Builder chatClientBuilder) throws GraphStateException {
KeyStrategyFactory keyStrategyFactory = () -> {
HashMap<String, KeyStrategy> keyStrategyHashMap = new HashMap<>();
// 用户输入
keyStrategyHashMap.put("query", new ReplaceStrategy());
keyStrategyHashMap.put("expander_number", new ReplaceStrategy());
keyStrategyHashMap.put("expander_content", new ReplaceStrategy());
return keyStrategyHashMap;
};
StateGraph stateGraph = new StateGraph(keyStrategyFactory)
.addNode("expander", _node_async_(new ExpanderNode(chatClientBuilder)))
.addEdge(StateGraph._START_, "expander")
.addEdge("expander", StateGraph._END_);
// 添加 PlantUML 打印
GraphRepresentation representation = stateGraph.getGraph(GraphRepresentation.Type._PLANTUML_,
"expander flow");
_logger_.info("\n=== expander UML Flow ===");
_logger_.info(representation.content());
_logger_.info("==================================\n");
return stateGraph;
}
}
node
ExpanderNode
- PromptTemplate DEFAULT_PROMPT_TEMPLATE:扩展文本的提示词
- ChatClient chatClient:调用 AI 模型的 client 端
- Integer NUMBER:默认扩展为 3 条相似问题
最后将 AI 模型的响应内容返回给给到字段 expander_content 中
package com.spring.ai.tutorial.graph.node;
import com.alibaba.cloud.ai.graph.NodeOutput;
import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import com.alibaba.cloud.ai.graph.streaming.StreamingChatGenerator;
import org.bsc.async.AsyncGenerator;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatResponse;
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;
public class ExpanderNode implements NodeAction {
private static final PromptTemplate _DEFAULT_PROMPT_TEMPLATE _= new PromptTemplate("You are an expert at information retrieval and search optimization.\nYour task is to generate {number} different versions of the given query.\n\nEach variant must cover different perspectives or aspects of the topic,\nwhile maintaining the core intent of the original query. The goal is to\nexpand the search space and improve the chances of finding relevant information.\n\nDo not explain your choices or add any other text.\nProvide the query variants separated by newlines.\n\nOriginal query: {query}\n\nQuery variants:\n");
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("expander_number", this.NUMBER);
Flux<String> streamResult = this.chatClient.prompt().user((user) -> user.text(_DEFAULT_PROMPT_TEMPLATE_.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("expander_content", queryVariants);
return resultMap;
}
}
controller
package com.spring.ai.tutorial.graph.controller;
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.GraphStateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
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
@RequestMapping("/graph")
public class SimpleGraphController {
private static final Logger _logger _= LoggerFactory._getLogger_(SimpleGraphController.class);
private final CompiledGraph compiledGraph;
public SimpleGraphController(@Qualifier("simpleGraph") StateGraph stateGraph) throws GraphStateException {
this.compiledGraph = stateGraph.compile();
}
@GetMapping(value = "/expand")
public Map<String, Object> expand(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?", required = false) String query,
@RequestParam(value = "expander_number", defaultValue = "3", required = false) Integer expanderNumber,
@RequestParam(value = "thread_id", defaultValue = "yingzi", required = false) String threadId){
RunnableConfig runnableConfig = RunnableConfig._builder_().threadId(threadId).build();
Map<String, Object> objectMap = new HashMap<>();
objectMap.put("query", query);
objectMap.put("expander_number", expanderNumber);
Optional<OverAllState> invoke = this.compiledGraph.invoke(objectMap, runnableConfig);
return invoke.map(OverAllState::data).orElse(new HashMap<>());
}}
效果
学习交流圈
你好,我是影子,曾先后在🐻、新能源、老铁就职,现在是一名AI研发工程师,同时作为Spring AI Alibaba开源社区的Committer。目前新建了一个交流群,一个人走得快,一群人走得远,另外,本人长期维护一套飞书云文档笔记,涵盖后端、大数据系统化的面试资料,可私信免费获取