這次我們不再討論前文的招聘場景,而是學習一種更為廣泛使用的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
[譯] AI Workflow & AI Agent:架構、模式與工程建議(Anthropic,2024)
Agents and Agentic AI | LangChain4j