博客 / 詳情

返回

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

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.