找回密码
 立即注册
首页 业界区 科技 Agent设计模式学习(基于langchain4j实现)(10) - ReACT ...

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

秦晓曼 昨天 01:05

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

1.gif

这跟人类解决问题的思考方式很像:loop(思考-行动)。当我们遇到一系列问题时,通常先思考,逐个想方案(plan),然后执行(action),解决1个后(解决过程中,可能会借助工具),再来解决下1个,直到所有问题都处理完。

 

定义ReAct Agent

2.gif
3.gif
  1. 1 public interface ReActAssistant {
  2. 2 @SystemMessage("""
  3. 3 你是一个使用ReAct(Reasoning and Acting)模式的智能助手。
  4. 4 请按照以下步骤思考:
  5. 5 1. 理解用户的问题
  6. 6 2. 思考解决问题需要什么信息
  7. 7 3. 如果需要计算或查询,选择合适的工具
  8. 8 4. 使用工具获取结果
  9. 9 5. 基于结果给出最终答案
  10. 10
  11. 11 请用中文思考和回答。
  12. 12 当使用工具时,明确说明你要使用的工具。
  13. 13 """)
  14. 14 @UserMessage("问:{{request}}")
  15. 15 @Agent("基于用户提供的问题进行思考和回答")
  16. 16 String chat(@V("request") String request);
  17. 17
  18. 18 }
复制代码
ReActAssistant

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

 

ReAct 使用示例

4.gif
5.gif
  1. 1 @SpringBootApplication
  2. 2 public class ReActAgentApplication {
  3. 3
  4. 4 public static void main(String[] args) throws IOException {
  5. 5 ConfigurableApplicationContext context = SpringApplication.run(AgentDesignPatternApplication.class, args);
  6. 6 ChatModel model = context.getBean("ollamaChatModel", ChatModel.class);
  7. 7 SampleTools sampleTools = context.getBean("sampleTools", SampleTools.class);
  8. 8
  9. 9 ReActAssistant agent = AgenticServices
  10. 10 .agentBuilder(ReActAssistant.class)
  11. 11 .chatModel(model)
  12. 12 .chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(15))
  13. 13 .tools(sampleTools)
  14. 14 .build();
  15. 15
  16. 16
  17. 17 String[] testQueries = {
  18. 18 "计算 15 加上 27 等于多少?",
  19. 19 "北京现在的天气怎么样?",
  20. 20 "计算半径为5的圆的面积",
  21. 21 "现在是几点?",
  22. 22 "计算长方体的体积,长10,宽5,高3",
  23. 23 "帮我算一下 (25 × 4) ÷ 2 等于多少?",
  24. 24 "快递单123456,现在到哪了?",
  25. 25 "我的订单56789,退款到账了没?"
  26. 26 };
  27. 27
  28. 28 for (String query : testQueries) {
  29. 29 System.out.println("问: " + query);
  30. 30 try {
  31. 31 String response = agent.chat(query);
  32. 32 System.out.println("答: " + response);
  33. 33 System.out.println("-".repeat(50));
  34. 34 // 避免请求过快
  35. 35 Thread.sleep(1000);
  36. 36 } catch (Exception e) {
  37. 37 System.err.println("查询失败: " + e.getMessage());
  38. 38 }
  39. 39 }
  40. 40
  41. 41 }
  42. 42 }
复制代码
ReActAgentApplication

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

6.gif
7.gif
  1. 1 /**
  2. 2 * @author junmingyang
  3. 3 */
  4. 4 @Component("sampleTools")
  5. 5 public class SampleTools {
  6. 6
  7. 7 @Tool("计算两个数的加法运算")
  8. 8 public String add(double a, double b) {
  9. 9 double result = a + b;
  10. 10 System.out.printf("[工具调用] 加法: %.2f + %.2f = %.2f\n", a, b, result);
  11. 11 return String.format("%.2f + %.2f = %.2f", a, b, result);
  12. 12 }
  13. 13
  14. 14 @Tool("计算两个数的减法运算")
  15. 15 public String subtract(double a, double b) {
  16. 16 double result = a - b;
  17. 17 System.out.printf("[工具调用] 减法: %.2f - %.2f = %.2f\n", a, b, result);
  18. 18 return String.format("%.2f - %.2f = %.2f", a, b, result);
  19. 19 }
  20. 20
  21. 21 @Tool("计算两个数的乘法运算")
  22. 22 public String multiply(double a, double b) {
  23. 23 double result = a * b;
  24. 24 System.out.printf("[工具调用] 乘法: %.2f × %.2f = %.2f\n", a, b, result);
  25. 25 return String.format("%.2f × %.2f = %.2f", a, b, result);
  26. 26 }
  27. 27
  28. 28 @Tool("计算两个数的除法运算")
  29. 29 public String divide(double a, double b) {
  30. 30 if (b == 0) {
  31. 31 return "错误:除数不能为零";
  32. 32 }
  33. 33 double result = a / b;
  34. 34 System.out.printf("[工具调用] 除法: %.2f ÷ %.2f = %.2f\n", a, b, result);
  35. 35 return String.format("%.2f ÷ %.2f = %.2f", a, b, result);
  36. 36 }
  37. 37
  38. 38 @Tool("获取当前日期和时间")
  39. 39 public String getCurrentDateTime() {
  40. 40 String datetime = LocalDateTime.now().format(
  41. 41 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
  42. 42 );
  43. 43 System.out.println("[工具调用] 当前时间: " + datetime);
  44. 44 return datetime;
  45. 45 }
  46. 46
  47. 47 @Tool("查询指定城市的天气信息")
  48. 48 public String getWeather(String city) {
  49. 49 System.out.println("[工具调用] 查询天气: " + city);
  50. 50 // 这里可以集成真实的天气API
  51. 51 return String.format("%s的天气:晴转多云,温度22-28°C,湿度65%%,东南风2级", city);
  52. 52 }
  53. 53
  54. 54 @Tool("计算圆的面积")
  55. 55 public String calculateCircleArea(double radius) {
  56. 56 double area = Math.PI * radius * radius;
  57. 57 System.out.printf("[工具调用] 圆面积计算: 半径=%.2f, 面积=%.2f\n", radius, area);
  58. 58 return String.format("半径为 %.2f 的圆的面积是 %.2f", radius, area);
  59. 59 }
  60. 60
  61. 61 @Tool("查询快递单")
  62. 62 public String queryExpressOrder(String expressOrderNo) {
  63. 63 System.out.printf("[工具调用] 快递单: %s,已经在运输途中,预订明天送达\n",
  64. 64 expressOrderNo);
  65. 65 return String.format("[工具调用] 快递单: %s,已经在运输途中,预订明天送达\n",
  66. 66 expressOrderNo);
  67. 67 }
  68. 68
  69. 69 @Tool("查询退款进度")
  70. 70 public String queryRefundProgress(String orderNo) {
  71. 71 System.out.printf("[工具调用] 订单: %s,退款已审批通过,预计1-3个工作日按原路退回\n",
  72. 72 orderNo);
  73. 73 return String.format("[工具调用] 订单: %s,退款已审批通过,预计1-3个工作日按原路退回\n",
  74. 74 orderNo);
  75. 75 }
  76. 76 }
复制代码
SampleTools

 

运行效果

8.gif
9.gif
  1. 1 问: 计算 15 加上 27 等于多少?
  2. 2 [工具调用] 加法: 15.00 + 27.00 = 42.00
  3. 3 答: 计算结果是:15 + 27 = 42。
  4. 4 --------------------------------------------------
  5. 5 问: 北京现在的天气怎么样?
  6. 6 [工具调用] 查询天气: 北京
  7. 7 答: 北京当前的天气情况是:
  8. 8 - 天气状况:晴转多云
  9. 9 - 温度:22-28°C
  10. 10 - 湿度:65%
  11. 11 - 风力:东南风2级
  12. 12
  13. 13 总体来说天气比较舒适,适合户外活动。
  14. 14 --------------------------------------------------
  15. 15 问: 计算半径为5的圆的面积
  16. 16 [工具调用] 圆面积计算: 半径=5.00, 面积=78.54
  17. 17 答: 半径为5的圆的面积是 **78.54**(平方单位)。
  18. 18
  19. 19 圆的面积计算公式为:π × 半径² = π × 5² = π × 25 ≈ 78.54
  20. 20 --------------------------------------------------
  21. 21 问: 现在是几点?
  22. 22 [工具调用] 当前时间: 2026-01-31 19:47:39
  23. 23 答: 现在的时间是:2026年1月31日 19:47:39
  24. 24 --------------------------------------------------
  25. 25 问: 计算长方体的体积,长10,宽5,高3
  26. 26 [工具调用] 乘法: 10.00 × 5.00 = 50.00
  27. 27 [工具调用] 乘法: 50.00 × 3.00 = 150.00
  28. 28 答: 长方体的体积是 **150**(立方单位)。
  29. 29
  30. 30 长方体体积计算公式:长 × 宽 × 高 = 10 × 5 × 3 = 150
  31. 31 --------------------------------------------------
  32. 32 问: 帮我算一下 (25 × 4) ÷ 2 等于多少?
  33. 33 [工具调用] 乘法: 25.00 × 4.00 = 100.00
  34. 34 [工具调用] 除法: 100.00 ÷ 2.00 = 50.00
  35. 35 答: (25 × 4) ÷ 2 = **50**
  36. 36
  37. 37 计算过程:
  38. 38 - 25 × 4 = 100
  39. 39 - 100 ÷ 2 = 50
  40. 40 --------------------------------------------------
  41. 41 问: 快递单123456,现在到哪了?
  42. 42 [工具调用] 快递单: 123456,已经在运输途中,预订明天送达
  43. 43 答: 您的快递单123456目前状态:
  44. 44 - **物流状态**:已经在运输途中
  45. 45 - **预计送达时间**:明天送达
  46. 46
  47. 47 快递正在正常运输中,请耐心等待。
  48. 48 --------------------------------------------------
  49. 49 问: 我的订单56789,退款到账了没?
  50. 50 [工具调用] 订单: 56789,退款已审批通过,预计1-3个工作日按原路退回
  51. 51 答: 您的订单56789退款状态:
  52. 52 - **退款状态**:退款已审批通过
  53. 53 - **预计到账时间**:1-3个工作日内按原路退回
  54. 54
  55. 55 退款申请已经通过审核,资金将在1-3个工作日内原路返回到您的支付账户,请注意查收。
  56. 56 --------------------------------------------------
复制代码
View Code

为了让结果看上去更简洁,把跟LLM交互的request与response去掉了。 如果对【LLM生成的计划】以及【如何选择工具】感兴趣,可以观察下request/response日志,以最后1个问题为例,请求参数为:

10.gif
11.gif
  1. 1 {
  2. 2 "model": "minimax-m2:cloud",
  3. 3 "messages": [
  4. 4 {
  5. 5 "role": "system",
  6. 6 "content": "你是一个使用ReAct(Reasoning and Acting)模式的智能助手。\n请按照以下步骤思考:\n1. 理解用户的问题\n2. 思考解决问题需要什么信息\n3. 如果需要计算或查询,选择合适的工具\n4. 使用工具获取结果\n5. 基于结果给出最终答案\n\n请用中文思考和回答。\n当使用工具时,明确说明你要使用的工具。\n"
  7. 7 },
  8. 8 {
  9. 9 "role": "assistant",
  10. 10 "tool_calls": [
  11. 11 {
  12. 12 "function": {
  13. 13 "name": "multiply",
  14. 14 "arguments": {
  15. 15 "a": 50,
  16. 16 "b": 3
  17. 17 }
  18. 18 }
  19. 19 }
  20. 20 ]
  21. 21 },
  22. 22 {
  23. 23 "role": "tool",
  24. 24 "content": "50.00 × 3.00 = 150.00"
  25. 25 },
  26. 26 {
  27. 27 "role": "assistant",
  28. 28 "content": "长方体的体积是 **150** 立方单位。\n\n计算过程:\n- 长方体体积 = 长 × 宽 × 高\n- 10 × 5 × 3 = 150\n\n所以这个长方体的体积为150立方单位。",
  29. 29 "tool_calls": []
  30. 30 },
  31. 31 {
  32. 32 "role": "user",
  33. 33 "content": "问:帮我算一下 (25 × 4) ÷ 2 等于多少?"
  34. 34 },
  35. 35 {
  36. 36 "role": "assistant",
  37. 37 "content": "我来为您计算 (25 × 4) ÷ 2。",
  38. 38 "tool_calls": [
  39. 39 {
  40. 40 "function": {
  41. 41 "name": "multiply",
  42. 42 "arguments": {
  43. 43 "a": 25,
  44. 44 "b": 4
  45. 45 }
  46. 46 }
  47. 47 }
  48. 48 ]
  49. 49 },
  50. 50 {
  51. 51 "role": "tool",
  52. 52 "content": "25.00 × 4.00 = 100.00"
  53. 53 },
  54. 54 {
  55. 55 "role": "assistant",
  56. 56 "tool_calls": [
  57. 57 {
  58. 58 "function": {
  59. 59 "name": "divide",
  60. 60 "arguments": {
  61. 61 "a": 100,
  62. 62 "b": 2
  63. 63 }
  64. 64 }
  65. 65 }
  66. 66 ]
  67. 67 },
  68. 68 {
  69. 69 "role": "tool",
  70. 70 "content": "100.00 ÷ 2.00 = 50.00"
  71. 71 },
  72. 72 {
  73. 73 "role": "assistant",
  74. 74 "content": "(25 × 4) ÷ 2 = **50**\n\n计算过程:\n- 先算 25 × 4 = 100\n- 再算 100 ÷ 2 = 50\n\n所以答案是 50。",
  75. 75 "tool_calls": []
  76. 76 },
  77. 77 {
  78. 78 "role": "user",
  79. 79 "content": "问:快递单123456,现在到哪了?"
  80. 80 },
  81. 81 {
  82. 82 "role": "assistant",
  83. 83 "content": "我来帮您查询快递单123456的状态。",
  84. 84 "tool_calls": [
  85. 85 {
  86. 86 "function": {
  87. 87 "name": "queryExpressOrder",
  88. 88 "arguments": {
  89. 89 "expressOrderNo": "123456"
  90. 90 }
  91. 91 }
  92. 92 }
  93. 93 ]
  94. 94 },
  95. 95 {
  96. 96 "role": "tool",
  97. 97 "content": "[工具调用] 快递单: 123456,已经在运输途中,预订明天送达\n"
  98. 98 },
  99. 99 {
  100. 100 "role": "assistant",
  101. 101 "content": "您的快递单123456目前状态:**已经在运输途中**,预计**明天送达**。\n\n快递正在正常运输中,请耐心等待。",
  102. 102 "tool_calls": []
  103. 103 },
  104. 104 {
  105. 105 "role": "user",
  106. 106 "content": "问:我的订单56789,退款到账了没?"
  107. 107 }
  108. 108 ],
  109. 109 "options": {
  110. 110 "stop": []
  111. 111 },
  112. 112 "stream": false,
  113. 113 "tools": [
  114. 114 {
  115. 115 "type": "function",
  116. 116 "function": {
  117. 117 "name": "getWeather",
  118. 118 "description": "查询指定城市的天气信息",
  119. 119 "parameters": {
  120. 120 "type": "object",
  121. 121 "properties": {
  122. 122 "city": {
  123. 123 "type": "string"
  124. 124 }
  125. 125 },
  126. 126 "required": [
  127. 127 "city"
  128. 128 ]
  129. 129 }
  130. 130 }
  131. 131 },
  132. 132 {
  133. 133 "type": "function",
  134. 134 "function": {
  135. 135 "name": "queryExpressOrder",
  136. 136 "description": "查询快递单",
  137. 137 "parameters": {
  138. 138 "type": "object",
  139. 139 "properties": {
  140. 140 "expressOrderNo": {
  141. 141 "type": "string"
  142. 142 }
  143. 143 },
  144. 144 "required": [
  145. 145 "expressOrderNo"
  146. 146 ]
  147. 147 }
  148. 148 }
  149. 149 },
  150. 150 {
  151. 151 "type": "function",
  152. 152 "function": {
  153. 153 "name": "queryRefundProgress",
  154. 154 "description": "查询退款进度",
  155. 155 "parameters": {
  156. 156 "type": "object",
  157. 157 "properties": {
  158. 158 "orderNo": {
  159. 159 "type": "string"
  160. 160 }
  161. 161 },
  162. 162 "required": [
  163. 163 "orderNo"
  164. 164 ]
  165. 165 }
  166. 166 }
  167. 167 },
  168. 168 {
  169. 169 "type": "function",
  170. 170 "function": {
  171. 171 "name": "calculateCircleArea",
  172. 172 "description": "计算圆的面积",
  173. 173 "parameters": {
  174. 174 "type": "object",
  175. 175 "properties": {
  176. 176 "radius": {
  177. 177 "type": "number"
  178. 178 }
  179. 179 },
  180. 180 "required": [
  181. 181 "radius"
  182. 182 ]
  183. 183 }
  184. 184 }
  185. 185 },
  186. 186 {
  187. 187 "type": "function",
  188. 188 "function": {
  189. 189 "name": "getCurrentDateTime",
  190. 190 "description": "获取当前日期和时间"
  191. 191 }
  192. 192 },
  193. 193 {
  194. 194 "type": "function",
  195. 195 "function": {
  196. 196 "name": "add",
  197. 197 "description": "计算两个数的加法运算",
  198. 198 "parameters": {
  199. 199 "type": "object",
  200. 200 "properties": {
  201. 201 "a": {
  202. 202 "type": "number"
  203. 203 },
  204. 204 "b": {
  205. 205 "type": "number"
  206. 206 }
  207. 207 },
  208. 208 "required": [
  209. 209 "a",
  210. 210 "b"
  211. 211 ]
  212. 212 }
  213. 213 }
  214. 214 },
  215. 215 {
  216. 216 "type": "function",
  217. 217 "function": {
  218. 218 "name": "multiply",
  219. 219 "description": "计算两个数的乘法运算",
  220. 220 "parameters": {
  221. 221 "type": "object",
  222. 222 "properties": {
  223. 223 "a": {
  224. 224 "type": "number"
  225. 225 },
  226. 226 "b": {
  227. 227 "type": "number"
  228. 228 }
  229. 229 },
  230. 230 "required": [
  231. 231 "a",
  232. 232 "b"
  233. 233 ]
  234. 234 }
  235. 235 }
  236. 236 },
  237. 237 {
  238. 238 "type": "function",
  239. 239 "function": {
  240. 240 "name": "subtract",
  241. 241 "description": "计算两个数的减法运算",
  242. 242 "parameters": {
  243. 243 "type": "object",
  244. 244 "properties": {
  245. 245 "a": {
  246. 246 "type": "number"
  247. 247 },
  248. 248 "b": {
  249. 249 "type": "number"
  250. 250 }
  251. 251 },
  252. 252 "required": [
  253. 253 "a",
  254. 254 "b"
  255. 255 ]
  256. 256 }
  257. 257 }
  258. 258 },
  259. 259 {
  260. 260 "type": "function",
  261. 261 "function": {
  262. 262 "name": "divide",
  263. 263 "description": "计算两个数的除法运算",
  264. 264 "parameters": {
  265. 265 "type": "object",
  266. 266 "properties": {
  267. 267 "a": {
  268. 268 "type": "number"
  269. 269 },
  270. 270 "b": {
  271. 271 "type": "number"
  272. 272 }
  273. 273 },
  274. 274 "required": [
  275. 275 "a",
  276. 276 "b"
  277. 277 ]
  278. 278 }
  279. 279 }
  280. 280 }
  281. 281 ]
  282. 282 }
复制代码
最后1个问题-查退款进度-请求LLM

可以看到,走到最后1个问题时(106行),除了所有可用的工具之外(113-281行),前面所有问题及答案(8-103行),也一并发给LLM了,所以LLM看上去才有记忆!

LLM的响应

12.gif
13.gif
  1. 1 {
  2. 2 "model": "minimax-m2:cloud",
  3. 3 "remote_model": "minimax-m2",
  4. 4 "remote_host": "https://ollama.com:443",
  5. 5 "created_at": "2026-01-31T11:52:21.93218197Z",
  6. 6 "message": {
  7. 7 "role": "assistant",
  8. 8 "content": "我来帮您查询订单56789的退款进度。",
  9. 9 "thinking": "用户询问订单56789的退款进度,我需要使用queryRefundProgress工具来查询这个订单号的退款情况。",
  10. 10 "tool_calls": [
  11. 11 {
  12. 12 "id": "call_dmgzch57",
  13. 13 "function": {
  14. 14 "index": 0,
  15. 15 "name": "queryRefundProgress",
  16. 16 "arguments": {
  17. 17 "orderNo": "56789"
  18. 18 }
  19. 19 }
  20. 20 }
  21. 21 ]
  22. 22 },
  23. 23 "done": true,
  24. 24 "done_reason": "stop",
  25. 25 "total_duration": 816309591,
  26. 26 "prompt_eval_count": 1023,
  27. 27 "eval_count": 63
  28. 28 }
复制代码
查退款进度-LLM响应

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

 

时序图

14.png

 

文中示例代码:

https://github.com/yjmyzz/agentic_turoial_with_langchain4j

 

参考:

Building Effective AI Agents \ Anthropic

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

Agents and Agentic AI | LangChain4j


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册