AI大模型综合(二)LangGraph4j(完整示例)
目录
一、LangGraph4j简介
LangGraph4j 是一个专为 Java设计的开源库,用于构建状态ful、多智能体应用,并与语言模型(LLMs)无缝集成,支持复杂任务协作与流程管理。
LangGraph4j 基于状态图(StateGraph)模型,通过节点(Node)、边(Edge)和检查点(State)实现智能体协作
1.1 核心特性
流程管理:支持循环、条件分支和并行执行,灵活处理复杂业务逻辑。
状态管理:通过检查点机制保存执行状态,实现任务中断恢复和上下文追溯,提升系统鲁棒性。
模块化设计:节点职责单一,降低代码耦合度,便于扩展与维护。
集成能力:与 Spring Boot、LangChain4j 等框架无缝兼容,支持“人在环路”(Human-in-Loop)模式。无缝集成主流Java生态,与Spring Boot(studio/springboot/)、Quarkus(studio/quarkus/)、Jetty(studio/jetty/)等框架深度整合,提供开箱即用的部署方案,满足从微服务到Serverless的全场景需求
- 原生支持循环:Agent的“思考-行动”循环、自我纠错、多轮对话等需要循环的场景,在LangGraph4j中可以轻松实现,而无需像传统编程那样写复杂的 while 循环和状态管理代码。
- 人机协同(Human-in-the-Loop): 图可以在任何节点后暂停,等待人类的输入、审核或确认,然后再继续执行。这对于构建可控、可靠的企业级应用至关重要。
可观测性与调试:
- 检查点: 在任何点保存图的状态,以便稍后重播或检查。这对于调试和理解复杂交互非常宝贵。
- 图可视化: 使用 PlantUML 或 Mermaid 生成图的可视化表示,以理解其结构。
1.2 应用场景
舆情监测系统:智能体判断用户情绪倾向(正面/负面),通过条件边路由至处理节点,检查点保存中间结果
支付流程:商品市场智能体调用支付智能体完成交易
人机交互场景: 比如,多个角色的审批流程;
自动化修复场景: 比如,在 自动化 Bug 修复 / 代码生成 中,把生成、运行、调试、修复等阶段建为节点,用 LangGraph 来协同控制流
1.3 核心概念
- 状态图StateGraph
状态图是一种数据结构,其生命周期存在于整个Langgraph过程中,所有节点的执行过程和结构都可以被记录到状态图中;而且每个节点也都可以随时访问状态图中的数据,来获取其当时的执行过程和结果
StateGraph<S extends AgentState>是用于定义应用程序结构的主要类。您可以在此添加节点和边以创建图。它由
StateGraphAgentState进行参数化。
AgentState(或其扩展类)表示图的共享状态。它本质上是一个在节点间传递的映射(Map<String, Object>)。每个节点都可以读取此状态并返回对其的更新
在StateGraph中定义好所有节点和边后,你需要调用compile()方法将其编译为CompiledGraph<S extends AgentState>。这个编译后的图是逻辑的不可变、可运行表示。编译过程会验证图结构(例如,检查是否存在孤立节点)。
- 节点Node
节点通常是一个函数(或实现 NodeAction<S> 或 AsyncNodeAction<S> 的类);其主要作用就是用来执行具体的任务。
执行一些计算(例如,调用 LLM、执行工具、人工审查、运行自定义业务逻辑)节点可以是同步的或异步的(CompletableFuture)。
节点往往会对应以下几种行为:
- 调用 LLM(如 OpenAI、Anthropic、Claude、GPT 模型等)
- 调用外部工具(API、数据库、搜索、检索系统)(ps:类似@Tool)
- 访问 / 管理 memory(长期或短期记忆)
- 逻辑判断 / 条件控制 / 计算 / 转换
- 人工审查 / 人工输入
- 边Edge
边就相当于判断逻辑中的分支,边决定了当前节点执行完毕之后,下一个执行的节点。用来表示执行时从一个节点到下一个节点的路径。可以有条件判断、失败重试、错误分支等。
Normal Edges(普通边):从一个节点到另一个节点的无条件过渡;
Conditional Edges(条件边):下一个节点根据当前 AgentState 动态确定
Entry Points(入口点):您还可以使用 addConditionalEntryPoint(...) 为图定义条件入口点
- 子图 / 子流程 / 嵌套 Graph
复杂场景中,一个节点内部可能又含有子图/子流程,以支持层次化设计
- 状态(State)
执行 Graph 时,需要一个 状态对象(state) 来在节点间传递上下文数据。这个状态可能是一个键值映射(Map<String, Object>),节点可以读取或写入状态中字段。
- 检查点
在任何步骤保存图的状态(Checkpoint)。检查点是存储了当前节点的所有信息,包括了状态图,也有检查点版本号,回溯关系之类的信息
检查点 / 恢复:执行过程中在关键点或每节点后保存执行状态和状态快照,以支持中断后重启或时间旅行
保存图的状态(Checkpoint)适用的场景:
- 调试:在不同点检查状态,以了解发生了什么。
- 恢复:将图恢复到之前的状态并继续执行。
- 长时间运行的流程:持久化长时间运行的智能体交互状态。 你通常会使用
CheckpointSaver实现(例如,用于内存存储的MemorySaver,或者你可以实现自己的持久化存储)。 - 中断 / 人工干预
在某些节点可以插入人工审核、人工干预步骤(human-in-the-loop)
1.4 使用流程
(1)定义状态对象(AgentState):首先,定义一个类或数据结构来表示整个工作流的共享状态(例如,一个包含消息列表、中间结果的 AgentState 对象)。
(2)定义节点(NodeAction):将你的 Agent、工具或其他业务逻辑实现为一个个的函数或方法,这些方法接受当前的状态作为输入,并返回对状态的更新。
(3)构建图(StateGraph):实例化一个 StateGraph<T>, 并传入你定义的状态类。使用 addNode() 将你定义好的节点添加到图中。使用 addEdge() 或 addConditionalEdges()来连接这些节点,定义任务的流转逻辑。
(4)编译图:调用 StateGraph 的 compile()方法,LangGraph4j 会将你定义的图结构编译成一个可执行的Graph对象。
(5)执行:调用编译后图对象的 stream() 或 invoke() 方法,传入初始输入,即可启动整个多智能体系统的协作流程。图会根据你定义的边和条件,自动地在不同节点间流转,直到到达终点。
1.5 官方文档
https://github.com/langgraph4j/langgraph4j/
中文翻译版:https://gitcode.com/gh_mirrors/la/langgraph4j
注:
中文翻译版:https://gitcode.com/GitHub_Trending/la/langchain4j
二、LangGraph4j示例演示
示例1:
执行流程图:

SimpleState:
package com.ai.langgraph4j.example02; import org.bsc.langgraph4j.state.AgentState; import org.bsc.langgraph4j.state.Channel; import org.bsc.langgraph4j.state.Channels; import java.util.ArrayList; import java.util.List; import java.util.Map; //执行 Graph 时,需要一个状态对象(state)来在节点间传递上下文数据。这个状态可能是一个键值映射(Map<String, Object>) public class SimpleState extends AgentState { public static final String MESSAGES_KEY = "messages"; // 定义了 SimpleState,其中包含一个使用 AppenderChannel 来累积字符串的 MESSAGES_KEY public static final Map<String, Channel<?>> SCHEMA = Map.of( MESSAGES_KEY, Channels.appender(ArrayList::new) ); public SimpleState(Map<String, Object> initData) { super(initData); } public List<String> messages() { return this.<List<String>>value("messages") .orElse( List.of() ); } } TestTool:
package com.ai.langgraph4j.example02; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import static java.lang.String.format; @Component public class TestTool { @Tool("tool for test AI agent executor") String execTest(@P("test message") String message) { return format( "test tool ('%s') executed with result 'OK'", message); } @Tool("return current number of system thread allocated by application") int threadCount() { return Thread.getAllStackTraces().size(); } } GreeterNodeAgent:
package com.ai.langgraph4j.example02; import org.bsc.langgraph4j.action.NodeAction; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.Map; @Component public class GreeterNodeAgent implements NodeAction<SimpleState> { @Autowired private TestTool testTool; @Override public Map<String, Object> apply(SimpleState simpleState) throws Exception { System.out.println("GreeterNode 执行了. Current messages: " + simpleState.messages()); testTool.execTest(simpleState.messages().getFirst()); //GreeterNode 会添加一条 “Hello” 消息 return Map.of(SimpleState.MESSAGES_KEY, "Hello from GreeterNode!"); } } ResponderNodeAgent:
package com.ai.langgraph4j.example02; import org.bsc.langgraph4j.action.NodeAction; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; @Component public class ResponderNodeAgent implements NodeAction<SimpleState> { @Override public Map<String, Object> apply(SimpleState simpleState) throws Exception { System.out.println("ResponderNode 执行了. Current messages: " + simpleState.messages()); List<String> currentMessages = simpleState.messages(); if (currentMessages.contains("Hello from GreeterNode!")) { //ResponderNode 会检查问候语并添加一条确认消息 return Map.of(SimpleState.MESSAGES_KEY, "Acknowledged greeting!"); } return Map.of(SimpleState.MESSAGES_KEY, "No greeting found."); } } package com.ai.langgraph4j.example02; import org.bsc.langgraph4j.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import static org.bsc.langgraph4j.GraphDefinition.END; import static org.bsc.langgraph4j.GraphDefinition.START; import static org.bsc.langgraph4j.action.AsyncNodeAction.node_async; import org.bsc.langgraph4j.GraphStateException; import org.bsc.langgraph4j.StateGraph; @Configuration public class LG4jConfiguration { @Autowired private GreeterNodeAgent greeterNodeAgent; @Autowired private ResponderNodeAgent responderNodeAgent; @Bean("helloGraph") public StateGraph<SimpleState> buildhelloGraph( GreeterNodeAgent greeterNode, ResponderNodeAgent responderNode ) throws GraphStateException { return new StateGraph<>( SimpleState.SCHEMA, initData -> new SimpleState(initData)) .addNode("greeter", node_async(greeterNode)) .addNode("responder", node_async(responderNode)) // Define edges .addEdge(START, "greeter") // Start with the greeter node .addEdge("greeter", "responder") .addEdge("responder", END) ; } // 创建可运行的 CompiledGraph @Bean("hellocompiledGraphGraph") public CompiledGraph<SimpleState> buildcompiledGraphhelloGraph( ) throws GraphStateException { return new StateGraph<>( SimpleState.SCHEMA, initData -> new SimpleState(initData)) .addNode("greeter", node_async(greeterNodeAgent)) .addNode("responder", node_async(responderNodeAgent)) // Define edges .addEdge(START, "greeter") // Start with the greeter node .addEdge("greeter", "responder") .addEdge("responder", END) .compile() ; } } Langgraph4jController:
package com.ai.langgraph4j.example02; import org.bsc.langgraph4j.CompiledGraph; import org.bsc.langgraph4j.GraphStateException; import org.bsc.langgraph4j.StateGraph; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController @RequestMapping(value = "/langgraph4jController") public class Langgraph4jController { @Autowired @Qualifier("helloGraph") private StateGraph<SimpleState> stateGraph; @Autowired @Qualifier("hellocompiledGraphGraph") private CompiledGraph<SimpleState> compiledGraph; //执行 Graph 时,需要状态对象(state) 来在节点间传递上下文数据。这个状态可能是一个键值映射(Map<String, Object>) @RequestMapping("/buildstateGraph") public String buildstateGraph(String message) throws GraphStateException { // 创建+ 运行 var items = stateGraph.compile().stream(Map.of(SimpleState.MESSAGES_KEY, message)); for (var item : items) { System.out.println(item); } return items.stream().toString(); } @RequestMapping("/buildcompiledGraph") public String buildcompiledGraph(String message) throws GraphStateException { // 运行 var items = compiledGraph.stream(Map.of(SimpleState.MESSAGES_KEY, message)); for (var item : items) { System.out.println(item); } var finalState = compiledGraph.invoke(Map.of(SimpleState.MESSAGES_KEY, "开始执行图")); System.out.println("------invoke result = " + finalState); return items.stream().toString(); } } 执行:

日志内容:
GreeterNode 执行了. Current messages: [图执行开始了]
NodeOutput{node=__START__, state={
messages=[
图执行开始了
]
}}
ResponderNode 执行了. Current messages: [图执行开始了, Hello from GreeterNode!]
NodeOutput{node=greeter, state={
messages=[
图执行开始了
Hello from GreeterNode!
]
}}
NodeOutput{node=responder, state={
messages=[
图执行开始了
Hello from GreeterNode!
Acknowledged greeting!
]
}}
NodeOutput{node=__END__, state={
messages=[
图执行开始了
Hello from GreeterNode!
Acknowledged greeting!
]
}}
注意: 日志内容表示,某个node节点执行后,当时上下文的内容是存储在state结构中的
如果想让controller返回具体内容,可以修改controller返回内容:
// 运行 var items = compiledGraph.stream(Map.of("messages", userMessage)); StringBuilder sb = new StringBuilder(); for (var item : items) { System.out.println(item); sb.append(item.toString()); } return sb.toString();执行结果:
