调用 addConditionalEdges 就是告诉 Graph:从 sourceId 节点跑完之后,执行 condition 函数拿到分支标记,再根据 mappings 路由跳转到对应的下游节点,实现工作流的 if-else 分支跳转
预期结果:


START → 生成笑话 → 评估笑话
条件分支:评估笑话 →优秀 → END
→不够优秀 → 优化笑话 → END

(节点)都已经准备就绪:
-
GenerateJokeNode生成笑话
-
EvaluateJokesNode评估笑话
- EnhanceJokeQualityNode优化笑话
1、pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.sb</groupId>
<artifactId>zhipuai</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>zhipuai</name>
<description>zhipuai</description>
<properties>
<java.version>17</java.version>
<!-- <spring-ai.version>2.0.0-M5</spring-ai.version>-->
<spring-ai.version>1.1.2</spring-ai.version>
<spring-ai-alibaba.version>1.0.0.4</spring-ai-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>${spring-ai-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2、application.yml
server:
port: 18091 # 应用监听端口,默认 18091
spring:
application:
name: sangeng-graph
ai:
zhipuai:
#api-key: ${ZHIPU_KEY} # 从环境变量 ZHIPU_KEY 读,不硬编码
api-key: "XXXXXXXXXXXXXXX" # 从环境变量 ZHIPU_KEY 读,不硬编码
chat:
options:
model: glm-4.5-air # GLM-4.5-air
3、controller
package com.sb.zhipuai.controller;
import com.alibaba.cloud.ai.graph.CompiledGraph;
import com.alibaba.cloud.ai.graph.OverAllState;
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.Map;
import java.util.Optional;
@RestController
@RequestMapping("/graph")
public class GraphController {
private static final Logger log = LoggerFactory.getLogger(GraphController.class);
// 构造器注入 Spring 容器里那个 CompiledGraph
private final CompiledGraph compiledGraph;
private final CompiledGraph simpleGraph; // 对应 simpleGraph
private final CompiledGraph conditionalGraph;
public GraphController(
@Qualifier("quickStartGraph") CompiledGraph compiledGraph,
@Qualifier("simpleGraph") CompiledGraph simpleGraph,
@Qualifier("conditionalGraph") CompiledGraph conditionalGraph) {
this.compiledGraph = compiledGraph;
this.simpleGraph = simpleGraph;
this.conditionalGraph = conditionalGraph;
}
@GetMapping("/conditionalGraph")
public Map<String, Object> conditionalGraph(@RequestParam("topic") String topic){
Optional<OverAllState> overAllStateOptional = conditionalGraph.call(Map.of("topic", topic));
Map<String, Object> data = overAllStateOptional.map(OverAllState::data).orElse(Map.of());
return data;
}
@GetMapping("/quickStartGraph")
public String quickStartGraph() {
Optional<OverAllState> overAllStateOptional = compiledGraph.call(Map.of()); // ← call → invoke
log.info("overAllStateOptional: {}", overAllStateOptional);
return "ok";
}
/**
* 造句翻译
* @param word
* @return
*/
@GetMapping("/simpleGraph")
public Map<String, Object> simpleGraph(@RequestParam("word") String word){
Optional<OverAllState> overAllStateOptional = simpleGraph.call(Map.of("word", word));
Map<String, Object> data = overAllStateOptional.map(OverAllState::data).orElse(Map.of());
return data;
}
}
4、GraphConfiguration
package com.sb.zhipuai.config;
import com.alibaba.cloud.ai.graph.*;
import com.alibaba.cloud.ai.graph.action.AsyncEdgeAction;
import com.alibaba.cloud.ai.graph.action.AsyncNodeAction;
import com.alibaba.cloud.ai.graph.action.NodeAction;
import com.alibaba.cloud.ai.graph.exception.GraphStateException;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.sb.zhipuai.node.*;
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.Map;
import static com.alibaba.cloud.ai.graph.action.AsyncNodeAction.node_async;
//定义状态图 (状态,节点,边),编译生成 CompiledGraph 后注入 Spring 容器
@Configuration
public class GraphConfiguration {
private static final Logger log = LoggerFactory.getLogger(GraphConfiguration.class);
@Bean("conditionalGraph")
public CompiledGraph conditionalGraph(ChatClient.Builder clientBuilder) throws GraphStateException{
// 定义状态键的更新策略
KeyStrategyFactory keyStrategyFactory = () -> Map.of("topic",new ReplaceStrategy());
// 定义创建状态图stateGraph
StateGraph stateGraph = new StateGraph("conditionalGraph", keyStrategyFactory);
// 添加节点
stateGraph.addNode("生成笑话", AsyncNodeAction.node_async(new GenerateJokeNode(clientBuilder)));
stateGraph.addNode("评估笑话", AsyncNodeAction.node_async(new EvaluateJokesNode(clientBuilder)));
stateGraph.addNode("优化笑话", AsyncNodeAction.node_async(new EnhanceJokeQualityNode(clientBuilder)));
// 定义边:START → 生成笑话 → 评估笑话
stateGraph.addEdge(StateGraph.START, "生成笑话");
stateGraph.addEdge("生成笑话", "评估笑话");
// 条件分支:评估笑话 →优秀 → END
// →不够优秀 → 优化笑话 → END
stateGraph.addConditionalEdges(
"评估笑话",
AsyncEdgeAction.edge_async((OverAllState state) -> state.value("result", "优秀")),
// 返回值映射目标节点
Map.of(
"优秀", StateGraph.END,
"不够优秀", "优化笑话"
)
);
stateGraph.addEdge("优化笑话", StateGraph.END);
return stateGraph.compile();
}
@Bean("quickStartGraph")
public CompiledGraph quickStartGraph() throws GraphStateException {
// 状态值:input1、input2 都用 ReplaceStrategy(新值覆盖旧值)
KeyStrategyFactory keyStrategyFactory = new KeyStrategyFactory() {
@Override
public Map<String, KeyStrategy> apply() {
return Map.of("input1", new ReplaceStrategy(),
"input2", new ReplaceStrategy());
}
};
// 定义创建状态图stateGraph
StateGraph stateGraph = new StateGraph("quickStartGraph", keyStrategyFactory);
// 定义节点 node1:打印当前状态,返回 input1=1, input2=1
stateGraph.addNode("node1", node_async(new NodeAction() {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
log.info("node1 state: {}", state);
return Map.of("input1", 1, "input2", 1);
}
}));
// 定义node2:打印当前状态,返回 input1=2, input2=2
stateGraph.addNode("node2", node_async(new NodeAction() {
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
log.info("node2 state: {}", state);
return Map.of("input1", 2, "input2", 2);
}
}));
// 定义边:START → node1 → node2 → END
stateGraph.addEdge(StateGraph.START, "node1");
stateGraph.addEdge("node1", "node2");
stateGraph.addEdge("node2", StateGraph.END);
// 编译状态图,交回 Spring 容器
CompiledGraph compiledGraph=stateGraph.compile();
return compiledGraph;
}
@Bean("simpleGraph")
public CompiledGraph simpleGraph(ChatClient.Builder clientBuilder) throws GraphStateException {
// 定义状态键的更新策略
KeyStrategyFactory keyStrategyFactory = () -> Map.of("word", new ReplaceStrategy());
// 创建状态图
StateGraph stateGraph = new StateGraph("simpleGraph", keyStrategyFactory);
// 添加节点
stateGraph.addNode("SentenceConstructionNode",
AsyncNodeAction.node_async(new SentenceConstructionNode(clientBuilder)));
stateGraph.addNode("TranslationNode",
AsyncNodeAction.node_async(new TranslationNode(clientBuilder)));
// 定义边 (执行顺序)
stateGraph.addEdge(StateGraph.START, "SentenceConstructionNode");
stateGraph.addEdge("SentenceConstructionNode", "TranslationNode");
stateGraph.addEdge("TranslationNode", StateGraph.END);
// 编译状态图,放入容器
return stateGraph.compile();
}
}
5、Node
package com.sb.zhipuai.node;
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 java.util.Map;
/**
* @author Administrator 造句节点
*/
public class SentenceConstructionNode implements NodeAction {
private final ChatClient chatClient;
public SentenceConstructionNode(ChatClient.Builder builder) {
// 构建 ChatClient 实例
this.chatClient = builder.build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从 state 中获取要造句的单词,如果没有则使用空字符串
String word = state.value("word", "");
PromptTemplate promptTemplate = new PromptTemplate("你是一个英语造句专家,能够基于给定的单词进行造句。" +
"要求只返回最终造好的句子,不要返回其他信息。给定的单词:{word}"
);
promptTemplate.add("word", word);
String prompt = promptTemplate.render();
String content = chatClient.prompt()
.user(prompt)
.call()
.content();
// 把句子存入 state,键名定义为 "sentence"
return Map.of("sentence", content);
}
}
package com.sb.zhipuai.node;
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 java.util.Map;
// 其他 import 保持和你造句节点一样即可
// import org.springframework.ai.chat.client.ChatClient;
// import org.springframework.ai.chat.prompt.PromptTemplate;
// import com.sangeng.workflow.NodeAction; (假设的包名)
// import com.sangeng.workflow.OverallState; (假设的包名)
/**
* @author Administrator 翻译节点
*/
public class TranslationNode implements NodeAction {
private final ChatClient chatClient;
public TranslationNode(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从 state 中获取要造句的单词,如果没有则使用空字符串
String sentence = state.value("sentence", "");
// 创建提示模板,定义AI翻译任务
PromptTemplate promptTemplate = new PromptTemplate("你是一个英语翻译专家,能够对句子进行翻译。" +
"要求只返回翻译的结果不要返回其他信息。要翻译的句子:{sentence}");
// 模板添加参数
promptTemplate.add("sentence", sentence);
String prompt = promptTemplate.render();
// 模型调用
String content = chatClient.prompt()
.user(prompt)
.call()
.content();
// 返回翻译结果,存入state
return Map.of("translation", content);
}
}
package com.sb.zhipuai.node;
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 java.util.Map;
/**
* @author Administrator 生成笑话节点
*/
public class GenerateJokeNode implements NodeAction {
private final ChatClient chatClient;
public GenerateJokeNode(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从 state 中获取topic
String topic = state.value("topic", "");
// PromptTemplate 渲染提示词
PromptTemplate promptTemplate = new PromptTemplate("你需要写一个关于指定主题的短笑话。要求返回的结果中只能包含笑话的内容" +
"主题:{topic}");
promptTemplate.add("topic", topic);
String prompt = promptTemplate.render();
//调用大模型
String content = chatClient.prompt()
.user(prompt)
.call()
.content();
// 存入state,键名定义为 "joke"
return Map.of("joke", content);
}
}
package com.sb.zhipuai.node;
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 java.util.Map;
/**
* @author Administrator评分节点
*/
public class EvaluateJokesNode implements NodeAction {
private final ChatClient chatClient;
public EvaluateJokesNode(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从 state 中获取joke
String joke = state.value("joke", "");
// 评分Prompt模板
PromptTemplate promptTemplate = new PromptTemplate("你是一个笑话评分专家,能够对笑话进行评分,基于效果的搞笑程度给出0到10分的打分。然后基于评分结果进行评价。如果大于等于3分评价:优秀 否则评价:不够优秀\n" +
"要求结果只返回最后的评价,不要其他内容。" +
"要评分的笑话::{joke}");
promptTemplate.add("joke", joke);
String prompt = promptTemplate.render();
//
String content = chatClient.prompt()
.user(prompt)
.call()
.content();
// 存入state,键名定义为 "result"
return Map.of("result", content.trim());
}
package com.sb.zhipuai.node;
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 java.util.Map;
/**
* @author Administrator 优化笑话节点
*/
public class EnhanceJokeQualityNode implements NodeAction {
private final ChatClient chatClient;
public EnhanceJokeQualityNode(ChatClient.Builder builder) {
this.chatClient = builder.build();
}
@Override
public Map<String, Object> apply(OverAllState state) throws Exception {
// 从 state 中获取joke
String joke = state.value("joke", "");
PromptTemplate promptTemplate = new PromptTemplate("你是一个笑话优化专家,你能够优化笑话,让它更加搞笑。" +
"只返回优化后的笑话,不要返回其他信息。要优化的笑话:{joke}");
promptTemplate.add("joke", joke);
String prompt = promptTemplate.render();
//调用大模型
String content = chatClient.prompt()
.user(prompt)
.call()
.content();
// 存入state,键名定义为 "newJoke"
return Map.of("newJoke", content.trim());
}
}
}
6、http://localhost:18091/graph/conditionalGraph?topic=爱情
6.1优秀

6.2不够优秀
注:content值修改一下, 跳转到EnhanceJokeQualityNode节点



浙公网安备 33010602011771号