|
这次我们不再讨论前文的招聘场景,而是学习一种更为广泛使用的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 }
复制代码
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² = π × 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 --------------------------------------------------
复制代码
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行)
时序图
文中示例代码:
https://github.com/yjmyzz/agentic_turoial_with_langchain4j
参考:
Building Effective AI Agents \ Anthropic
[译] AI Workflow & AI Agent:架构、模式与工程建议(Anthropic,2024)
Agents and Agentic AI | LangChain4j 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |