Agent设计模式学习(基于langchain4j实现)(10) - ReACT

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

react_pattern

这跟人类解决问题的思考方式很像: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 }
ReActAssistant

没错,就是这么简单,提示词里写清楚要求就行。

 

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 }
ReActAgentApplication

这里面的很多问题,需要用到工具,工具示例如下:

 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 }
SampleTools

 

运行效果

 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² = π × 2578.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 --------------------------------------------------
View Code

为了让结果看上去更简洁,把跟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个问题-查退款进度-请求LLM

可以看到,走到最后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响应

从这里看出,LLM选中了工具 queryRefundProgress(第9行)

 

时序图

react_diagram_simple

 

文中示例代码:

https://github.com/yjmyzz/agentic_turoial_with_langchain4j

 

参考:

Building Effective AI Agents \ Anthropic

[译] AI Workflow & AI Agent:架构、模式与工程建议(Anthropic,2024)

Agents and Agentic AI | LangChain4j

posted @ 2026-01-31 20:24  菩提树下的杨过  阅读(0)  评论(0)    收藏  举报