Agent设计模式学习(基于langchain4j实现)(10) - ReACT
这次我们不再讨论前文的招聘场景,而是学习一种更为广泛使用的Agent模式:ReACT (推理+行动)。先来看示意图:

这跟人类解决问题的思考方式很像:loop(思考-行动)。当我们遇到一系列问题时,通常先思考,逐个想方案(plan),然后执行(action),解决1个后(解决过程中,可能会借助工具),再来解决下1个,直到所有问题都处理完。
定义ReAct Agent
1 public interface ReActAssistant { 2 @SystemMessage(""" 3 你是一个使用ReAct(Reasoning and Acting)模式的智能助手。 4 请按照以下步骤思考: 5 1. 理解用户的问题 6 2. 思考解决问题需要什么信息 7 3. 如果需要计算或查询,选择合适的工具 8 4. 使用工具获取结果 9 5. 基于结果给出最终答案 10 11 请用中文思考和回答。 12 当使用工具时,明确说明你要使用的工具。 13 """) 14 @UserMessage("问:{{request}}") 15 @Agent("基于用户提供的问题进行思考和回答") 16 String chat(@V("request") String request); 17 18 }
没错,就是这么简单,提示词里写清楚要求就行。
ReAct 使用示例
1 @SpringBootApplication 2 public class ReActAgentApplication { 3 4 public static void main(String[] args) throws IOException { 5 ConfigurableApplicationContext context = SpringApplication.run(AgentDesignPatternApplication.class, args); 6 ChatModel model = context.getBean("ollamaChatModel", ChatModel.class); 7 SampleTools sampleTools = context.getBean("sampleTools", SampleTools.class); 8 9 ReActAssistant agent = AgenticServices 10 .agentBuilder(ReActAssistant.class) 11 .chatModel(model) 12 .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(15)) 13 .tools(sampleTools) 14 .build(); 15 16 17 String[] testQueries = { 18 "计算 15 加上 27 等于多少?", 19 "北京现在的天气怎么样?", 20 "计算半径为5的圆的面积", 21 "现在是几点?", 22 "计算长方体的体积,长10,宽5,高3", 23 "帮我算一下 (25 × 4) ÷ 2 等于多少?", 24 "快递单123456,现在到哪了?", 25 "我的订单56789,退款到账了没?" 26 }; 27 28 for (String query : testQueries) { 29 System.out.println("问: " + query); 30 try { 31 String response = agent.chat(query); 32 System.out.println("答: " + response); 33 System.out.println("-".repeat(50)); 34 // 避免请求过快 35 Thread.sleep(1000); 36 } catch (Exception e) { 37 System.err.println("查询失败: " + e.getMessage()); 38 } 39 } 40 41 } 42 }
这里面的很多问题,需要用到工具,工具示例如下:
1 /** 2 * @author junmingyang 3 */ 4 @Component("sampleTools") 5 public class SampleTools { 6 7 @Tool("计算两个数的加法运算") 8 public String add(double a, double b) { 9 double result = a + b; 10 System.out.printf("[工具调用] 加法: %.2f + %.2f = %.2f\n", a, b, result); 11 return String.format("%.2f + %.2f = %.2f", a, b, result); 12 } 13 14 @Tool("计算两个数的减法运算") 15 public String subtract(double a, double b) { 16 double result = a - b; 17 System.out.printf("[工具调用] 减法: %.2f - %.2f = %.2f\n", a, b, result); 18 return String.format("%.2f - %.2f = %.2f", a, b, result); 19 } 20 21 @Tool("计算两个数的乘法运算") 22 public String multiply(double a, double b) { 23 double result = a * b; 24 System.out.printf("[工具调用] 乘法: %.2f × %.2f = %.2f\n", a, b, result); 25 return String.format("%.2f × %.2f = %.2f", a, b, result); 26 } 27 28 @Tool("计算两个数的除法运算") 29 public String divide(double a, double b) { 30 if (b == 0) { 31 return "错误:除数不能为零"; 32 } 33 double result = a / b; 34 System.out.printf("[工具调用] 除法: %.2f ÷ %.2f = %.2f\n", a, b, result); 35 return String.format("%.2f ÷ %.2f = %.2f", a, b, result); 36 } 37 38 @Tool("获取当前日期和时间") 39 public String getCurrentDateTime() { 40 String datetime = LocalDateTime.now().format( 41 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") 42 ); 43 System.out.println("[工具调用] 当前时间: " + datetime); 44 return datetime; 45 } 46 47 @Tool("查询指定城市的天气信息") 48 public String getWeather(String city) { 49 System.out.println("[工具调用] 查询天气: " + city); 50 // 这里可以集成真实的天气API 51 return String.format("%s的天气:晴转多云,温度22-28°C,湿度65%%,东南风2级", city); 52 } 53 54 @Tool("计算圆的面积") 55 public String calculateCircleArea(double radius) { 56 double area = Math.PI * radius * radius; 57 System.out.printf("[工具调用] 圆面积计算: 半径=%.2f, 面积=%.2f\n", radius, area); 58 return String.format("半径为 %.2f 的圆的面积是 %.2f", radius, area); 59 } 60 61 @Tool("查询快递单") 62 public String queryExpressOrder(String expressOrderNo) { 63 System.out.printf("[工具调用] 快递单: %s,已经在运输途中,预订明天送达\n", 64 expressOrderNo); 65 return String.format("[工具调用] 快递单: %s,已经在运输途中,预订明天送达\n", 66 expressOrderNo); 67 } 68 69 @Tool("查询退款进度") 70 public String queryRefundProgress(String orderNo) { 71 System.out.printf("[工具调用] 订单: %s,退款已审批通过,预计1-3个工作日按原路退回\n", 72 orderNo); 73 return String.format("[工具调用] 订单: %s,退款已审批通过,预计1-3个工作日按原路退回\n", 74 orderNo); 75 } 76 }
运行效果
1 问: 计算 15 加上 27 等于多少? 2 [工具调用] 加法: 15.00 + 27.00 = 42.00 3 答: 计算结果是:15 + 27 = 42。 4 -------------------------------------------------- 5 问: 北京现在的天气怎么样? 6 [工具调用] 查询天气: 北京 7 答: 北京当前的天气情况是: 8 - 天气状况:晴转多云 9 - 温度:22-28°C 10 - 湿度:65% 11 - 风力:东南风2级 12 13 总体来说天气比较舒适,适合户外活动。 14 -------------------------------------------------- 15 问: 计算半径为5的圆的面积 16 [工具调用] 圆面积计算: 半径=5.00, 面积=78.54 17 答: 半径为5的圆的面积是 **78.54**(平方单位)。 18 19 圆的面积计算公式为:π × 半径² = π × 5² = π × 25 ≈ 78.54 20 -------------------------------------------------- 21 问: 现在是几点? 22 [工具调用] 当前时间: 2026-01-31 19:47:39 23 答: 现在的时间是:2026年1月31日 19:47:39 24 -------------------------------------------------- 25 问: 计算长方体的体积,长10,宽5,高3 26 [工具调用] 乘法: 10.00 × 5.00 = 50.00 27 [工具调用] 乘法: 50.00 × 3.00 = 150.00 28 答: 长方体的体积是 **150**(立方单位)。 29 30 长方体体积计算公式:长 × 宽 × 高 = 10 × 5 × 3 = 150 31 -------------------------------------------------- 32 问: 帮我算一下 (25 × 4) ÷ 2 等于多少? 33 [工具调用] 乘法: 25.00 × 4.00 = 100.00 34 [工具调用] 除法: 100.00 ÷ 2.00 = 50.00 35 答: (25 × 4) ÷ 2 = **50** 36 37 计算过程: 38 - 25 × 4 = 100 39 - 100 ÷ 2 = 50 40 -------------------------------------------------- 41 问: 快递单123456,现在到哪了? 42 [工具调用] 快递单: 123456,已经在运输途中,预订明天送达 43 答: 您的快递单123456目前状态: 44 - **物流状态**:已经在运输途中 45 - **预计送达时间**:明天送达 46 47 快递正在正常运输中,请耐心等待。 48 -------------------------------------------------- 49 问: 我的订单56789,退款到账了没? 50 [工具调用] 订单: 56789,退款已审批通过,预计1-3个工作日按原路退回 51 答: 您的订单56789退款状态: 52 - **退款状态**:退款已审批通过 53 - **预计到账时间**:1-3个工作日内按原路退回 54 55 退款申请已经通过审核,资金将在1-3个工作日内原路返回到您的支付账户,请注意查收。 56 --------------------------------------------------
为了让结果看上去更简洁,把跟LLM交互的request与response去掉了。 如果对【LLM生成的计划】以及【如何选择工具】感兴趣,可以观察下request/response日志,以最后1个问题为例,请求参数为:
1 { 2 "model": "minimax-m2:cloud", 3 "messages": [ 4 { 5 "role": "system", 6 "content": "你是一个使用ReAct(Reasoning and Acting)模式的智能助手。\n请按照以下步骤思考:\n1. 理解用户的问题\n2. 思考解决问题需要什么信息\n3. 如果需要计算或查询,选择合适的工具\n4. 使用工具获取结果\n5. 基于结果给出最终答案\n\n请用中文思考和回答。\n当使用工具时,明确说明你要使用的工具。\n" 7 }, 8 { 9 "role": "assistant", 10 "tool_calls": [ 11 { 12 "function": { 13 "name": "multiply", 14 "arguments": { 15 "a": 50, 16 "b": 3 17 } 18 } 19 } 20 ] 21 }, 22 { 23 "role": "tool", 24 "content": "50.00 × 3.00 = 150.00" 25 }, 26 { 27 "role": "assistant", 28 "content": "长方体的体积是 **150** 立方单位。\n\n计算过程:\n- 长方体体积 = 长 × 宽 × 高\n- 10 × 5 × 3 = 150\n\n所以这个长方体的体积为150立方单位。", 29 "tool_calls": [] 30 }, 31 { 32 "role": "user", 33 "content": "问:帮我算一下 (25 × 4) ÷ 2 等于多少?" 34 }, 35 { 36 "role": "assistant", 37 "content": "我来为您计算 (25 × 4) ÷ 2。", 38 "tool_calls": [ 39 { 40 "function": { 41 "name": "multiply", 42 "arguments": { 43 "a": 25, 44 "b": 4 45 } 46 } 47 } 48 ] 49 }, 50 { 51 "role": "tool", 52 "content": "25.00 × 4.00 = 100.00" 53 }, 54 { 55 "role": "assistant", 56 "tool_calls": [ 57 { 58 "function": { 59 "name": "divide", 60 "arguments": { 61 "a": 100, 62 "b": 2 63 } 64 } 65 } 66 ] 67 }, 68 { 69 "role": "tool", 70 "content": "100.00 ÷ 2.00 = 50.00" 71 }, 72 { 73 "role": "assistant", 74 "content": "(25 × 4) ÷ 2 = **50**\n\n计算过程:\n- 先算 25 × 4 = 100\n- 再算 100 ÷ 2 = 50\n\n所以答案是 50。", 75 "tool_calls": [] 76 }, 77 { 78 "role": "user", 79 "content": "问:快递单123456,现在到哪了?" 80 }, 81 { 82 "role": "assistant", 83 "content": "我来帮您查询快递单123456的状态。", 84 "tool_calls": [ 85 { 86 "function": { 87 "name": "queryExpressOrder", 88 "arguments": { 89 "expressOrderNo": "123456" 90 } 91 } 92 } 93 ] 94 }, 95 { 96 "role": "tool", 97 "content": "[工具调用] 快递单: 123456,已经在运输途中,预订明天送达\n" 98 }, 99 { 100 "role": "assistant", 101 "content": "您的快递单123456目前状态:**已经在运输途中**,预计**明天送达**。\n\n快递正在正常运输中,请耐心等待。", 102 "tool_calls": [] 103 }, 104 { 105 "role": "user", 106 "content": "问:我的订单56789,退款到账了没?" 107 } 108 ], 109 "options": { 110 "stop": [] 111 }, 112 "stream": false, 113 "tools": [ 114 { 115 "type": "function", 116 "function": { 117 "name": "getWeather", 118 "description": "查询指定城市的天气信息", 119 "parameters": { 120 "type": "object", 121 "properties": { 122 "city": { 123 "type": "string" 124 } 125 }, 126 "required": [ 127 "city" 128 ] 129 } 130 } 131 }, 132 { 133 "type": "function", 134 "function": { 135 "name": "queryExpressOrder", 136 "description": "查询快递单", 137 "parameters": { 138 "type": "object", 139 "properties": { 140 "expressOrderNo": { 141 "type": "string" 142 } 143 }, 144 "required": [ 145 "expressOrderNo" 146 ] 147 } 148 } 149 }, 150 { 151 "type": "function", 152 "function": { 153 "name": "queryRefundProgress", 154 "description": "查询退款进度", 155 "parameters": { 156 "type": "object", 157 "properties": { 158 "orderNo": { 159 "type": "string" 160 } 161 }, 162 "required": [ 163 "orderNo" 164 ] 165 } 166 } 167 }, 168 { 169 "type": "function", 170 "function": { 171 "name": "calculateCircleArea", 172 "description": "计算圆的面积", 173 "parameters": { 174 "type": "object", 175 "properties": { 176 "radius": { 177 "type": "number" 178 } 179 }, 180 "required": [ 181 "radius" 182 ] 183 } 184 } 185 }, 186 { 187 "type": "function", 188 "function": { 189 "name": "getCurrentDateTime", 190 "description": "获取当前日期和时间" 191 } 192 }, 193 { 194 "type": "function", 195 "function": { 196 "name": "add", 197 "description": "计算两个数的加法运算", 198 "parameters": { 199 "type": "object", 200 "properties": { 201 "a": { 202 "type": "number" 203 }, 204 "b": { 205 "type": "number" 206 } 207 }, 208 "required": [ 209 "a", 210 "b" 211 ] 212 } 213 } 214 }, 215 { 216 "type": "function", 217 "function": { 218 "name": "multiply", 219 "description": "计算两个数的乘法运算", 220 "parameters": { 221 "type": "object", 222 "properties": { 223 "a": { 224 "type": "number" 225 }, 226 "b": { 227 "type": "number" 228 } 229 }, 230 "required": [ 231 "a", 232 "b" 233 ] 234 } 235 } 236 }, 237 { 238 "type": "function", 239 "function": { 240 "name": "subtract", 241 "description": "计算两个数的减法运算", 242 "parameters": { 243 "type": "object", 244 "properties": { 245 "a": { 246 "type": "number" 247 }, 248 "b": { 249 "type": "number" 250 } 251 }, 252 "required": [ 253 "a", 254 "b" 255 ] 256 } 257 } 258 }, 259 { 260 "type": "function", 261 "function": { 262 "name": "divide", 263 "description": "计算两个数的除法运算", 264 "parameters": { 265 "type": "object", 266 "properties": { 267 "a": { 268 "type": "number" 269 }, 270 "b": { 271 "type": "number" 272 } 273 }, 274 "required": [ 275 "a", 276 "b" 277 ] 278 } 279 } 280 } 281 ] 282 }
可以看到,走到最后1个问题时(106行),除了所有可用的工具之外(113-281行),前面所有问题及答案(8-103行),也一并发给LLM了,所以LLM看上去才有记忆!
LLM的响应:
1 { 2 "model": "minimax-m2:cloud", 3 "remote_model": "minimax-m2", 4 "remote_host": "https://ollama.com:443", 5 "created_at": "2026-01-31T11:52:21.93218197Z", 6 "message": { 7 "role": "assistant", 8 "content": "我来帮您查询订单56789的退款进度。", 9 "thinking": "用户询问订单56789的退款进度,我需要使用queryRefundProgress工具来查询这个订单号的退款情况。", 10 "tool_calls": [ 11 { 12 "id": "call_dmgzch57", 13 "function": { 14 "index": 0, 15 "name": "queryRefundProgress", 16 "arguments": { 17 "orderNo": "56789" 18 } 19 } 20 } 21 ] 22 }, 23 "done": true, 24 "done_reason": "stop", 25 "total_duration": 816309591, 26 "prompt_eval_count": 1023, 27 "eval_count": 63 28 }
从这里看出,LLM选中了工具 queryRefundProgress(第9行)
时序图

文中示例代码:
https://github.com/yjmyzz/agentic_turoial_with_langchain4j
参考:
Building Effective AI Agents \ Anthropic
作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
浙公网安备 33010602011771号