当前位置: 首页 > ai >正文

(5)LangGraph4j框架ReActAgent实现

LangGraph4j框架ReActAgent实现

ReAct-Agent概念

ReAct-Agent 是一种大模型应用中的智能体架构。ReAct 是 Re (Reasoning,推理)和 Act(Action,行动)两个单词的简写,用通俗的话来说,它可以让大模型像人一样“思考”和“行动”,实现更强的任务处理能力。这里把它拆解为两个关键部分来说明:

Re(Reasoning,推理)

大模型通过“推理”步骤来理解任务或者问题。就像你面对一个复杂的问题时,先分析现状、列出条件,然后在脑海中一步步推导出答案一样,ReAct 中的“推理”部分会利用模型的语言理解能力,生成逻辑清晰的分析路径。

Act(Action,行动)

仅仅推理还不够,智能体还需要“行动”,也就是执行特定的操作。比如:

  • 通过调用某个工具(例如计算器、数据库查询、外部api接口)来获取信息或解决问题。
  • 根据现有信息做出决定并采取下一步行动。

在 ReAct 框架中,“行动”这一步和“推理”紧密结合。模型不会一次性给出答案,而是以“边想边做”的方式,循环地进行推理和行动,直到完成任务。

实际运作流程

  1. 模型先“想”一想:分析问题,给出可能需要的步骤。
  2. 模型再“做”一做:如果需要,它可以去查外部信息、调用工具,或者生成一个具体操作。
  3. 根据结果再“想” :拿到行动的反馈后,重新推理,调整步骤,直到问题解决。

ReAct 的特点

  • 动态灵活:不像传统模型“一问一答”,ReAct 模型会动态地调整策略。
  • 能调用外部工具:通过执行操作(例如数据库查询或API调用),可以解决大模型本身无法直接处理的问题。
  • 更接近人类思维:这种“想一步,做一步”的方式更像人类解决复杂问题的过程。

通俗来说,ReAct 就是让模型“像人一样,先动脑后动手,再动脑接着手”,在不断循环中完成任务。

LangGraph4j 中的 ReAct-Agent

ReactAgent 接口提供了构建和运行状态图(StateGraph)的功能。它包含两个主要内部类:CallAgent 和 ExecuteTools,分别用于调用 AI 模型生成响应和执行工具请求。

整个流程体现了典型的 ReAct 模式:

  • Reasoning (推理): CallAgent 生成思考并决定是否调用工具。
  • Action (动作): ExecuteTools 执行具体工具操作。
  • 循环直到获得最终答案(final_response)为止。

状态图设计

  • 使用 StateGraph 定义执行流程:

    • 起始节点为 START → reasoning

    • reasoning根据是否需要调用工具 (shouldContinue) 决定下一步:

      • “continue” → action
      • “end” → END
    • action 节点执行完工具后再次回到 reasoning

new StateGraph<>(State.SCHEMA, stateSerializer).addNode("reasoning", node_async(callAgent)).addNode("action", node_async(executeTools))	.addEdge(START, "reasoning").addConditionalEdges("reasoning",edge_async(shouldContinue),Map.of("continue", "action", "end", END)).addEdge("action", "reasoning");

在这里插入图片描述

CallAgent

  • 负责调用 LangChain4J 的聊天模型或流式聊天模型来生成 AI 响应。

  • 根据是否启用流式输出选择不同的执行策略:

    • 非流式模式直接调用 ChatModel.execute() 获取完整响应。
    • 流式模式使用 StreamingChatModel.chat() 并结合 StreamingChatGenerator 来处理逐步生成的结果。
  • 将响应结果映射为结构化格式,支持两种情况:

    • 如果需要执行工具,则返回 messages 字段表示待执行的工具调用。
    • 如果任务完成,则返回 final_response 表示最终答案。
/*** CallAgent 是一个 NodeAction 实现类,用于调用 Agent 模型生成响应。* 可以根据是否开启流式输出选择不同的执行策略。*/
class CallAgent implements NodeAction<State> {private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CallAgent.class);final Agent agent;/*** 使用指定的代理构造 CallAgent。** @param agent 关联到此 CallAgent 的代理*/public CallAgent(Agent agent) {this.agent = agent;}/*** 将来自AI消息的响应结果映射为结构化格式。** @param response 包含AI消息的响应* @return 包含代理结果的Map* @throws IllegalStateException 如果响应的结束原因是不支持的*/private Map<String,Object> mapResult(ChatResponse response )  {var content = response.aiMessage();if (response.finishReason() == FinishReason.TOOL_EXECUTION || content.hasToolExecutionRequests() ) {return Map.of("messages", content);}if( response.finishReason() == FinishReason.STOP || response.finishReason() == null  ) {return Map.of(FINAL_RESPONSE, content.text());}throw new IllegalStateException("不支持的结束原因: " + response.finishReason() );}/*** 应用给定状态的动作并返回结果。** @param state 动作应用到的状态* @return 包含代理结果的Map* @throws IllegalArgumentException 如果状态中没有提供输入*/@Overridepublic Map<String,Object> apply(State state )  {log.trace("[ReactAgent] callAgent...");List<ChatMessage> messages = state.messages();if( messages.isEmpty() ) {log.error("[ReactAgent] callAgent result: {}", "没有提供输入!");throw new IllegalArgumentException("没有提供输入!");}if( agent.isStreaming()) {StreamingChatGenerator<State> generator = StreamingChatGenerator.<State>builder().mapResult(this::mapResult).startingNode("agent").startingState(state).build();agent.execute(messages, generator.handler());log.info("[ReactAgent] callAgent result: {}", generator);return Map.of("_generator", generator);}else {var response = agent.execute(messages);log.info("[ReactAgent] callAgent result: {}", response);return mapResult(response);}}
}

ExecuteTools

  • 处理由 AI 生成的工具调用请求。
  • 使用 LangChain4jToolService 执行这些工具,并将结果整合后返回。
  • 如果没有找到工具请求,则返回提示信息。
/*** ExecuteTools 是一个 NodeAction 实现类,负责执行 AI 生成的工具调用请求。* 通常用于处理用户输入后由 Agent 决定调用哪些外部工具的情况。*/
class ExecuteTools implements NodeAction<State> {private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ExecuteTools.class);/*** 将要执行的工具服务。*/final LangChain4jToolService toolService;/*** 使用指定的工具映射构造 ExecuteTools 实例。** @param toolMap 要执行的工具映射,不能为空*/public ExecuteTools(Map<ToolSpecification, ToolExecutor> toolMap) {this.toolService = new LangChain4jToolService(toolMap);}/*** 基于提供的代理状态应用工具执行逻辑。** @param state 当前代理执行器的状态* @return 包含执行中间步骤的地图* @throws IllegalArgumentException 如果没有提供代理结果* @throws IllegalStateException 如果找不到要执行的操作或工具*/@Overridepublic Map<String,Object> apply(State state )  {log.trace("[ReactAgent] executeTools..");var toolExecutionRequests = state.lastMessage().filter(m -> ChatMessageType.AI==m.type()).map(m -> (AiMessage)m).filter(AiMessage::hasToolExecutionRequests).map(AiMessage::toolExecutionRequests);if( toolExecutionRequests.isEmpty() ) {log.info("[ReactAgent] executeTools result: {}" , "没有找到工具执行请求!");return Map.of(FINAL_RESPONSE, "没有找到工具执行请求!");}var result = toolExecutionRequests.get().stream().map(toolService::execute).filter(Optional::isPresent).map(Optional::get).toList();log.info("[ReactAgent] executeTools result: {}" , result);return Map.of("messages", result );}
}

ReactAgent构建

public interface ReactAgent {/*** 状态序列化的支持类型。* 支持标准格式(STD)和 JSON 格式(JSON)。*/enum Serializers {STD(new LangChain4jStateSerializer<>(State::new) ),JSON(new LangChain4jJacksonStateSerializer<>(State::new));private final StateSerializer<State> serializer;/*** 使用指定的序列化器构造新的Serializers枚举实例。** @param serializer 状态序列化器*/Serializers(StateSerializer<State> serializer) {this.serializer = serializer;}/*** 获取状态序列化器。** @return 状态序列化器*/public StateSerializer<State> object() {return serializer;}}/*** Builder 类用于构建 Agent 的状态图(StateGraph)。* 包含构建节点、边及条件判断逻辑。*/class Builder extends Agent.Builder<Builder> {private StateSerializer<State> stateSerializer;/*** 设置状态序列化** @param stateSerializer 状态序列化* @return 更新的实例*/public Builder stateSerializer(StateSerializer<State> stateSerializer) {this.stateSerializer = stateSerializer;return this;}/*** 构建状态图(StateGraph)。** @return 构建的 StateGraph* @throws GraphStateException 如果图形状态中存在错误*/@Overridepublic StateGraph<State> build() throws GraphStateException {if (streamingChatModel != null && chatModel != null) {throw new IllegalArgumentException("chatLanguageModel 和 streamingChatLanguageModel 是互斥的!");}if (streamingChatModel == null && chatModel == null) {throw new IllegalArgumentException("需要 chatLanguageModel 或 streamingChatLanguageModel!");}var agent = new Agent(this);if (stateSerializer == null) {stateSerializer = Serializers.STD.object();}final var callAgent = new CallAgent(agent);final var executeTools = new ExecuteTools(toolMap());final EdgeAction<State> shouldContinue = (state) ->state.finalResponse().map(res -> "end").orElse("continue");return new StateGraph<>(State.SCHEMA, stateSerializer).addNode("agent", node_async(callAgent)).addNode("action", node_async(executeTools)).addEdge(START, "agent").addConditionalEdges("agent",edge_async(shouldContinue),Map.of("continue", "action", "end", END)).addEdge("action", "agent");}}/*** 创建一个新的 GraphBuilder 实例。** @return 新的 Builder 实例*/static Builder builder() {return new Builder();}
}

ReactAgent测试验证

1、创建StateGraph和CompiledGraph

@Bean
public StateGraph<State> reactAgent(@Qualifier("chatModel") ChatModel chatModel) throws GraphStateException {return ReactAgent.builder().name("reactAgent智能体").chatModel(chatModel).toolsFromObject(new TestTools()).toolsFromObject(new TavilySearchTool(searchApiKey)).build();
}
@Bean(value = "reactAgentGraph")
public CompiledGraph<State> reactAgentGraph(@Qualifier("reactAgent") StateGraph<State> reactAgent)throws GraphStateException {return reactAgent.compile();
}

2、创建工具类

public class TestTools {@Tool("返回给定城市的天气预报")public String getWeather(@P("应返回天气预报的城市") String city) {return "The weather in " + city + " is sunny with a high of 25 degrees.";}@Tool("求两个数的和")double add(@P(value = "数字",required = true) int a,@P(value = "数字",required = true) int b) {return a + b;}@Tool("求平方根")double squareRoot(@P(value = "数字",required = true) double x) {return Math.sqrt(x);}
}

3、创建接口

@Slf4j
@RestController
@RequestMapping("/react")
public class ReactController {@Resource(name = "reactAgentGraph")private CompiledGraph<State> reactAgentGraph;@GetMapping("/chat")public String simpleChat(@RequestParam("query") String query) {GraphRepresentation graph = reactAgentGraph.getGraph(GraphRepresentation.Type.PLANTUML, "React智能体");log.info("React-Agent PlantUML Graph:\n{}", graph.content());Optional<State> queryResult = reactAgentGraph.invoke(Map.of("messages", UserMessage.from(query)));return queryResult.get().finalResponse().get();}
}

4、执行结果日志:

在这里插入图片描述

[ReactAgent] callAgent result: ChatResponse { aiMessage = AiMessage { text = null toolExecutionRequests = [ToolExecutionRequest { id = "call_e9837fbced09464b8f9490", name = "getWeather", arguments = "{"arg0": "北京"}" }] }, metadata = QwenChatResponseMetadata{id="0b109d6b-5234-9db1-8305-ef5f666f7bbb", modelName="qwen-plus-latest", tokenUsage=TokenUsage { inputTokenCount = 432, outputTokenCount = 20, totalTokenCount = 452 }, finishReason=TOOL_EXECUTION, searchInfo=SearchInfo[searchResults=[]], reasoningContent=null} }
[ReactAgent] executeTools result: [ToolExecutionResultMessage { id = "call_e9837fbced09464b8f9490" toolName = "getWeather" text = "The weather in 北京 is sunny with a high of 25 degrees." }]
[ReactAgent] callAgent result: ChatResponse { aiMessage = AiMessage { text = "北京的天气晴朗,最高气温为25度。" toolExecutionRequests = [] }, metadata = QwenChatResponseMetadata{id="212e9aef-5f02-98d4-8fae-3201c44d02b4", modelName="qwen-plus-latest", tokenUsage=TokenUsage { inputTokenCount = 482, outputTokenCount = 13, totalTokenCount = 495 }, finishReason=STOP, searchInfo=SearchInfo[searchResults=[]], reasoningContent=null} }
http://www.xdnf.cn/news/15343.html

相关文章:

  • 核电概念盘中异动,中核科技涨停引领板块热度
  • SQL性能调优经验总结
  • HashMap的长度为什么要是2的n次幂以及HashMap的继承关系(元码解析)
  • 持续优化小程序排名,稳定获取搜索流量
  • Bash常见条件语句和循环语句
  • gRPC和http长轮询
  • Python:打造你的HTTP应用帝国
  • 019_工具集成与外部API调用
  • 缺乏实际里程碑管控项目进度,如何设定关键节点
  • 李沐动手学深度学习Pytorch-v2笔记【08线性回归+基础优化算法】2
  • PHP password_hash() 函数
  • Excel常用快捷键与功能整理
  • 当 `conda list` 里出现两个 pip:一步步拆解并卸载冲突包
  • 【时时三省】(C语言基础)用数组名作函数参数
  • MFC中BOOL类型,在某些操作系统中,-1不能被识别,一般是哪些原因?
  • C++-linux 7.文件IO(二)文件描述符、阻塞与非阻塞
  • 招投标——环保大数据平台售后服务计划
  • 现代数据平台能力地图:如何构建未来数据平台的核心能力体系
  • Spark 之 like 表达式
  • uni-app开发的页面跳转全局加载中
  • QT——信号与槽
  • git 访问 github
  • 《恋与深空》中黑白羽毛是谁的代表物?
  • python+Request提取cookie
  • ubuntu22.04下配置qt5.15.17开发环境
  • Elasticsearch9.x核心架构概述
  • 机器学习、深度学习、神经网络之间的关系
  • 多租户云环境下的隔离性保障:虚拟化、容器、安全组如何协同防护?
  • 高德开放平台携手阿里云,面向开发者推出地图服务产品MCP Server
  • Redis技术笔记-主从复制、哨兵与持久化实战指南