AI大模型综合(二)LangGraph4j(完整示例)

目录

一、LangGraph4j简介

1.1 核心特性

1.2 应用场景

1.3 核心概念

状态图StateGraph

节点Node

边Edge

子图 / 子流程 / 嵌套 Graph

状态(State)

检查点

中断 / 人工干预

1.4 使用流程

1.5 官方文档

二、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>

        StateGraph
 是用于定义应用程序结构的主要类。您可以在此添加节点和边以创建图。它由 AgentState 进行参数化。

 

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();

执行结果:

Read more

AI 编程新王 Codex 全面上手指南

AI 编程新王 Codex 全面上手指南 一篇文章带你精通 Codex 四大环境 + 免费使用方法 💡 前言:AI 编程的新时代 AI 编程的竞争正进入“第二轮洗牌期”。 过去几个月,Claude Code 一度成为开发者的宠儿,但频繁的限速、封号、降智问题让不少人头疼。 如今,OpenAI 推出的 Codex 迅速崛起,凭借强大的编程能力和超高性价比,成为“AI 编程新王”。 Codex 是什么? 它是基于 GPT-5 模型打造的专用编程环境,支持命令行、VS Code 插件、SDK 集成、云端操作等多种运行模式。 不论你是写脚本、做项目、还是维护仓库,Codex 都能像“AI 结对程序员”一样协助你高效开发。

By Ne0inhk
Python+AI 实战:搭建属于你的智能问答机器人

Python+AI 实战:搭建属于你的智能问答机器人

欢迎文末添加好友交流,共同进步! “ 俺はモンキー・D・ルフィ。海贼王になる男だ!” 引言 * 在数字化转型浪潮中,智能问答机器人正成为企业客服、知识库检索乃至个人助理等场景的关键交互入口。它能让员工秒级获取技术解答、客户即时获得业务支持、学习者随时得到个性化辅导,极大提升信息获取效率与用户体验。 * 为何选择 Python 与开源 AI 模型?Python 拥有成熟的 AI 生态——Hugging Face Transformers、LangChain、FAISS 等工具大幅降低开发门槛;而本地部署的开源大模型(如 Phi-3、Mistral、Llama 系列)则保障了数据隐私、规避了 API 成本,特别适合对安全性或离线能力有要求的场景。 * 本文将手把手带你从零构建一个基于 RAG(检索增强生成)架构的本地智能问答系统:使用 Sentence-BERT 实现语义检索,FAISS 作为向量数据库,并集成轻量级开源语言模型生成答案。

By Ne0inhk
【AI开发】—— OpenCode双插件协同开发指南

【AI开发】—— OpenCode双插件协同开发指南

OpenCode双插件协同开发指南|Oh My OpenCode+Superpowers 兼顾效率与规范 很多同学在OpenCode中装完Superpowers后,都会有一个疑问:已经有了做代码规范的Superpowers,还有必要用Oh My OpenCode(OMOC)吗? 甚至装了两个插件后,不知道如何配合使用,导致要么只用到了其中一个的功能,要么让两者互相“冲突”,浪费了插件的核心价值。 其实答案很明确:两者是互补而非替代的关系,组合使用才是OpenCode的最优解。OMOC是「项目开发总指挥」,负责拆解任务、并行调度、自动化工具调用,主打一个提效率;Superpowers是「代码工程质检员」,负责约束AI遵循TDD、代码审查、重构等最佳实践,主打一个保规范。 这篇文章就把两者的核心区别讲透,再通过实操性拉满的协同开发教程,教你用OMOC+Superpowers开发项目,既让AI写代码又快又好,还能彻底摆脱“手动拆任务、反复改代码”的痛点,全程贴合开发实际,新手也能直接跟着做。 一、先搞懂:OMOC与Superpowers 核心区别(

By Ne0inhk

ubuntu24.04安装 openClaw+kimi2.5+飞书 详细教程

文章目录 * ubuntu24.04安装openClaw详细教程 * 准备工作: * 一、申请飞书小机器人 * 1.登录/注册 飞书账号 * 2.添加一个机器人 * 3.开通权限 im和用户相关的 * 4.复制需要的配置 * 注册大模型API * 1.登录/注册KIMI账号,进入开发者工作台 * 2.在API Key管理创建一个key * 安装openClaw * 开始配置,选QuickStart * 配置大模型 * 配置飞书 * 其他配置 * 配置飞书回调 * 飞书对话使用 * 问题处理 * 使用飞书发消息返回 access not configured. * openclaw-gateway 运行异常 * 有个飞书的警告,具体找不到了,好像是feishu overwrite什么鬼 ubuntu24.04安装openClaw详细教程 想看视频的话,推荐B站大佬 林粒粒呀 的windows安装openClaw

By Ne0inhk