可爱的你 發表於 2026-2-1 13:45:00

Agent设计模式学习(基于langchain4j实现)(11) - PlanAndExecute

<p>上篇学习了ReACT,今天继续学习PlanAndExecute模式</p>
<p><img src="https://img2024.cnblogs.com/blog/27612/202601/27612-20260131224737662-1296939375.gif" alt="plan_and_execute_pattern" width="480" loading="lazy" style="border: 1px solid rgba(0, 0, 0, 1)"></p>
<p><strong>与ReACT模式的关键区别如下</strong>:</p>
<table border="1">
<thead>
<tr><th>对比维度</th><th>ReAct Agent</th><th>Plan-and-Execute Agent</th></tr>
</thead>
<tbody>
<tr>
<td>思考模式</td>
<td>单步思考-行动循环</td>
<td>两阶段分离:先规划后执行</td>
</tr>
<tr>
<td>执行流程</td>
<td>Thought → Action → Observation (循环)</td>
<td>Plan → Execute Step 1 → Execute Step 2 → ...</td>
</tr>
<tr>
<td>规划范围</td>
<td>只规划下一步</td>
<td>一次性规划完整执行路径</td>
</tr>
<tr>
<td>LLM调用</td>
<td>每次循环都需要LLM</td>
<td>规划时一次,执行时可能多次</td>
</tr>
</tbody>
</table>
<h3>优势:</h3>
<ul>
<li>
<p class="ds-markdown-paragraph">效率更高:减少重复的"思考"步骤</p>
</li>
<li>
<p class="ds-markdown-paragraph">可预测性:预先知道完整执行路径</p>
</li>
<li>
<p class="ds-markdown-paragraph">易于调试:每个步骤都有明确的状态</p>
</li>
<li>
<p class="ds-markdown-paragraph">适合复杂任务:结构化处理多步骤流程</p>
</li>
</ul>
<h3>适用场景:</h3>
<ul>
<li>
<p class="ds-markdown-paragraph">✅ 数据处理的ETL流程</p>
</li>
<li>
<p class="ds-markdown-paragraph">✅ 自动化工作流</p>
</li>
<li>
<p class="ds-markdown-paragraph">✅ 需要审计的合规任务</p>
</li>
<li>
<p class="ds-markdown-paragraph">✅ 批量处理任务</p>
</li>
<li>
<p class="ds-markdown-paragraph">❌ 探索性任务(路径不确定)</p>
</li>
<li>
<p class="ds-markdown-paragraph">❌ 需要实时交互的对话</p>
</li>
</ul>
<p>&nbsp;</p>
<p><strong>示例代码:</strong></p>
<p>定义规划器Planner</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_fbfaddd0-2406-4c67-aaa0-566498073dea" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_fbfaddd0-2406-4c67-aaa0-566498073dea" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_fbfaddd0-2406-4c67-aaa0-566498073dea" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> Planner {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>   @SystemMessage(<span style="color: rgba(0, 0, 0, 1)">"""
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">         你是一个任务规划专家。请将用户的任务分解为详细的执行步骤。
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">         输出格式必须是严格的JSON格式:
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">         {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>                "plan_name": "任务名称"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>                "steps"<span style="color: rgba(0, 0, 0, 1)">: [
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">                   {
</span><span style="color: rgba(0, 128, 128, 1)">11</span>                        "step_number": 1<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">12</span>                        "description": "步骤描述"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">13</span>                        "tool": "使用的工具名称"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">14</span>                        "parameters": {"参数名": "参数值"<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">                   }
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">               ],
</span><span style="color: rgba(0, 128, 128, 1)">17</span>                "expected_output": "预期输出"
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">19</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">         可用工具列表:
</span><span style="color: rgba(0, 128, 128, 1)">21</span>            1. add -<span style="color: rgba(0, 0, 0, 1)"> 两数相加
</span><span style="color: rgba(0, 128, 128, 1)">22</span>            2. getWeather -<span style="color: rgba(0, 0, 0, 1)"> 天气查询
</span><span style="color: rgba(0, 128, 128, 1)">23</span>            3. calculateCircleArea -<span style="color: rgba(0, 0, 0, 1)"> 计算圆的面积
</span><span style="color: rgba(0, 128, 128, 1)">24</span>            4. getCurrentDateTime -<span style="color: rgba(0, 0, 0, 1)"> 获取当前时间
</span><span style="color: rgba(0, 128, 128, 1)">25</span>            5. calculateCuboidVolume -<span style="color: rgba(0, 0, 0, 1)"> 计算长方体体积
</span><span style="color: rgba(0, 128, 128, 1)">26</span>            6. multiply -<span style="color: rgba(0, 0, 0, 1)"> 两数相乘
</span><span style="color: rgba(0, 128, 128, 1)">27</span>            7. divide -<span style="color: rgba(0, 0, 0, 1)"> 两数相除
</span><span style="color: rgba(0, 128, 128, 1)">28</span>            8. queryExpressOrder -<span style="color: rgba(0, 0, 0, 1)"> 查询快递单
</span><span style="color: rgba(0, 128, 128, 1)">29</span>            9. queryRefundProgress -<span style="color: rgba(0, 0, 0, 1)"> 查询退款进度
</span><span style="color: rgba(0, 128, 128, 1)">30</span>         """)
<span style="color: rgba(0, 128, 128, 1)">31</span>   @UserMessage("问:{{request}}"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">32</span>   @Agent("基于用户提供的问题生成计划"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">33</span>   String createPlan(@V("request"<span style="color: rgba(0, 0, 0, 1)">) String request);
</span><span style="color: rgba(0, 128, 128, 1)">34</span> }</pre>
</div>
<span class="cnblogs_code_collapse">Planner </span></div>
<p>注:20-29行硬编码的方式指定需要用到的工具列表,也可以去掉,在运行时,类似ReAct一样,一股脑把sampleTools全扔给Planner调用的LLM,从运行结果来看,一样能跑通,但是使用token量就会大很多。</p>
<p>定义执行器</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_353bb914-41c5-473a-86d5-48765c588d0f" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_353bb914-41c5-473a-86d5-48765c588d0f" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_353bb914-41c5-473a-86d5-48765c588d0f" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> Executor {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>   @SystemMessage(<span style="color: rgba(0, 0, 0, 1)">"""
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">            你是一个任务执行器。根据给定的步骤执行任务。
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>            
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">            每次只执行一个步骤,然后等待下一个指令。
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">            执行完成后,报告结果和下一步建议。
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>            
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">            输出格式:
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">            步骤 {n}: [工具名称]
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">            输入: {参数}
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">            输出: {结果}
</span><span style="color: rgba(0, 128, 128, 1)">13</span>             状态: [成功/<span style="color: rgba(0, 0, 0, 1)">失败]
</span><span style="color: rgba(0, 128, 128, 1)">14</span>            
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">            如果步骤失败,请说明原因和建议。
</span><span style="color: rgba(0, 128, 128, 1)">16</span>             """)
<span style="color: rgba(0, 128, 128, 1)">17</span>   @UserMessage("{{step}}"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">18</span>   @Agent("基于用户提供的问题生成计划"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">19</span>   String executeStep(@V("step"<span style="color: rgba(0, 0, 0, 1)">) String step);
</span><span style="color: rgba(0, 128, 128, 1)">20</span> }</pre>
</div>
<span class="cnblogs_code_collapse">Executor</span></div>
<p>定义协调器</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_f67fdf8b-0a27-4d79-985f-dbcd0d63e02e" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_f67fdf8b-0a27-4d79-985f-dbcd0d63e02e" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_f67fdf8b-0a27-4d79-985f-dbcd0d63e02e" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 128, 0, 1)">/**</span>
<span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@author</span><span style="color: rgba(0, 128, 0, 1)"> junmingyang
</span><span style="color: rgba(0, 128, 128, 1)">3</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> Coordinator {
</span><span style="color: rgba(0, 128, 128, 1)">5</span>
<span style="color: rgba(0, 128, 128, 1)">6</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> Planner planner;
</span><span style="color: rgba(0, 128, 128, 1)">7</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> Executor executor;
</span><span style="color: rgba(0, 128, 128, 1)">8</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> SampleTools tools;
</span><span style="color: rgba(0, 128, 128, 1)">9</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Map&lt;String, Object&gt;<span style="color: rgba(0, 0, 0, 1)"> context;
</span><span style="color: rgba(0, 128, 128, 1)"> 10</span>
<span style="color: rgba(0, 128, 128, 1)"> 11</span>
<span style="color: rgba(0, 128, 128, 1)"> 12</span>   <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Coordinator(ChatModel model, SampleTools tools) {
</span><span style="color: rgba(0, 128, 128, 1)"> 13</span>
<span style="color: rgba(0, 128, 128, 1)"> 14</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.tools =<span style="color: rgba(0, 0, 0, 1)"> tools;
</span><span style="color: rgba(0, 128, 128, 1)"> 15</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.context = <span style="color: rgba(0, 0, 255, 1)">new</span> HashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)"> 16</span>
<span style="color: rgba(0, 128, 128, 1)"> 17</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建规划器</span>
<span style="color: rgba(0, 128, 128, 1)"> 18</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.planner = AgenticServices.agentBuilder(Planner.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 19</span> <span style="color: rgba(0, 0, 0, 1)">                .chatModel(model)
</span><span style="color: rgba(0, 128, 128, 1)"> 20</span>               .chatMemoryProvider(memoryId -&gt; MessageWindowChatMemory.withMaxMessages(15<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 128, 128, 1)"> 21</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果明确知道工具的列表,可以显式提供,这里就无需再绑定,以减少token使用
</span><span style="color: rgba(0, 128, 128, 1)"> 22</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">.tools(this.tools)</span>
<span style="color: rgba(0, 128, 128, 1)"> 23</span> <span style="color: rgba(0, 0, 0, 1)">                .build();
</span><span style="color: rgba(0, 128, 128, 1)"> 24</span>
<span style="color: rgba(0, 128, 128, 1)"> 25</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建执行器</span>
<span style="color: rgba(0, 128, 128, 1)"> 26</span>         <span style="color: rgba(0, 0, 255, 1)">this</span>.executor = AgenticServices.agentBuilder(Executor.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 27</span> <span style="color: rgba(0, 0, 0, 1)">                .chatModel(model)
</span><span style="color: rgba(0, 128, 128, 1)"> 28</span>               .chatMemoryProvider(memoryId -&gt; MessageWindowChatMemory.withMaxMessages(15<span style="color: rgba(0, 0, 0, 1)">))
</span><span style="color: rgba(0, 128, 128, 1)"> 29</span>               .tools(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.tools)
</span><span style="color: rgba(0, 128, 128, 1)"> 30</span> <span style="color: rgba(0, 0, 0, 1)">                .build();
</span><span style="color: rgba(0, 128, 128, 1)"> 31</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)"> 32</span>
<span style="color: rgba(0, 128, 128, 1)"> 33</span>
<span style="color: rgba(0, 128, 128, 1)"> 34</span>   <span style="color: rgba(0, 0, 255, 1)">public</span> Map&lt;String, Object&gt;<span style="color: rgba(0, 0, 0, 1)"> executeTask(String task) {
</span><span style="color: rgba(0, 128, 128, 1)"> 35</span>         System.out.println("\n" + "=".repeat(80<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 36</span>         System.out.println("🎯 任务: " +<span style="color: rgba(0, 0, 0, 1)"> task);
</span><span style="color: rgba(0, 128, 128, 1)"> 37</span>         System.out.println("=".repeat(80<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 38</span>
<span style="color: rgba(0, 128, 128, 1)"> 39</span>         Map&lt;String, Object&gt; result = <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedHashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)"> 40</span>         result.put("task"<span style="color: rgba(0, 0, 0, 1)">, task);
</span><span style="color: rgba(0, 128, 128, 1)"> 41</span>         result.put("start_time"<span style="color: rgba(0, 0, 0, 1)">, LocalDateTime.now().toString());
</span><span style="color: rgba(0, 128, 128, 1)"> 42</span>
<span style="color: rgba(0, 128, 128, 1)"> 43</span>         <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 44</span>             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 阶段1: 规划</span>
<span style="color: rgba(0, 128, 128, 1)"> 45</span>             System.out.println("\n📋 阶段1: 任务规划"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 46</span>             System.out.println("-".repeat(40<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 47</span>             String planJson =<span style="color: rgba(0, 0, 0, 1)"> planner.createPlan(task);
</span><span style="color: rgba(0, 128, 128, 1)"> 48</span>             System.out.println("生成的计划:\n" +<span style="color: rgba(0, 0, 0, 1)"> planJson);
</span><span style="color: rgba(0, 128, 128, 1)"> 49</span>
<span style="color: rgba(0, 128, 128, 1)"> 50</span>             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解析计划(简化版,实际应该使用JSON解析)</span>
<span style="color: rgba(0, 128, 128, 1)"> 51</span>             List&lt;Map&lt;String, String&gt;&gt; steps =<span style="color: rgba(0, 0, 0, 1)"> parsePlan(planJson);
</span><span style="color: rgba(0, 128, 128, 1)"> 52</span>             result.put("plan"<span style="color: rgba(0, 0, 0, 1)">, steps);
</span><span style="color: rgba(0, 128, 128, 1)"> 53</span>
<span style="color: rgba(0, 128, 128, 1)"> 54</span>             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 阶段2: 执行</span>
<span style="color: rgba(0, 128, 128, 1)"> 55</span>             System.out.println("\n⚡ 阶段2: 执行计划"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 56</span>             System.out.println("-".repeat(40<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 57</span>
<span style="color: rgba(0, 128, 128, 1)"> 58</span>             List&lt;Map&lt;String, Object&gt;&gt; executionResults = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)"> 59</span>
<span style="color: rgba(0, 128, 128, 1)"> 60</span>             <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; steps.size(); i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 61</span>               Map&lt;String, String&gt; step =<span style="color: rgba(0, 0, 0, 1)"> steps.get(i);
</span><span style="color: rgba(0, 128, 128, 1)"> 62</span>               System.out.printf("\n📝 步骤 %d/%d: %s%n"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)"> 63</span>                         i + 1, steps.size(), step.get("description"<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 64</span>
<span style="color: rgba(0, 128, 128, 1)"> 65</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 构建步骤指令</span>
<span style="color: rgba(0, 128, 128, 1)"> 66</span>               String stepInstruction =<span style="color: rgba(0, 0, 0, 1)"> buildStepInstruction(step);
</span><span style="color: rgba(0, 128, 128, 1)"> 67</span>
<span style="color: rgba(0, 128, 128, 1)"> 68</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行步骤</span>
<span style="color: rgba(0, 128, 128, 1)"> 69</span>               String stepResult =<span style="color: rgba(0, 0, 0, 1)"> executor.executeStep(stepInstruction);
</span><span style="color: rgba(0, 128, 128, 1)"> 70</span>               System.out.println("执行结果:\n" +<span style="color: rgba(0, 0, 0, 1)"> stepResult);
</span><span style="color: rgba(0, 128, 128, 1)"> 71</span>
<span style="color: rgba(0, 128, 128, 1)"> 72</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存结果</span>
<span style="color: rgba(0, 128, 128, 1)"> 73</span>               Map&lt;String, Object&gt; stepResultMap = <span style="color: rgba(0, 0, 255, 1)">new</span> HashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)"> 74</span>               stepResultMap.put("step_number", i + 1<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 75</span>               stepResultMap.put("description", step.get("description"<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 76</span>               stepResultMap.put("tool", step.get("tool"<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)"> 77</span>               stepResultMap.put("result"<span style="color: rgba(0, 0, 0, 1)">, stepResult);
</span><span style="color: rgba(0, 128, 128, 1)"> 78</span> <span style="color: rgba(0, 0, 0, 1)">                executionResults.add(stepResultMap);
</span><span style="color: rgba(0, 128, 128, 1)"> 79</span>
<span style="color: rgba(0, 128, 128, 1)"> 80</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更新上下文</span>
<span style="color: rgba(0, 128, 128, 1)"> 81</span> <span style="color: rgba(0, 0, 0, 1)">                updateContext(task, step, stepResult);
</span><span style="color: rgba(0, 128, 128, 1)"> 82</span>
<span style="color: rgba(0, 128, 128, 1)"> 83</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 短暂暂停,避免过快执行</span>
<span style="color: rgba(0, 128, 128, 1)"> 84</span>               Thread.sleep(1000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 85</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)"> 86</span>
<span style="color: rgba(0, 128, 128, 1)"> 87</span>             result.put("execution_results"<span style="color: rgba(0, 0, 0, 1)">, executionResults);
</span><span style="color: rgba(0, 128, 128, 1)"> 88</span>             result.put("status", "completed"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 89</span>
<span style="color: rgba(0, 128, 128, 1)"> 90</span>         } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
</span><span style="color: rgba(0, 128, 128, 1)"> 91</span>             System.err.println("❌ 任务执行失败: " +<span style="color: rgba(0, 0, 0, 1)"> e.getMessage());
</span><span style="color: rgba(0, 128, 128, 1)"> 92</span>             result.put("status", "failed"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 93</span>             result.put("error"<span style="color: rgba(0, 0, 0, 1)">, e.getMessage());
</span><span style="color: rgba(0, 128, 128, 1)"> 94</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)"> 95</span>
<span style="color: rgba(0, 128, 128, 1)"> 96</span>         result.put("end_time"<span style="color: rgba(0, 0, 0, 1)">, LocalDateTime.now().toString());
</span><span style="color: rgba(0, 128, 128, 1)"> 97</span>         <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> result;
</span><span style="color: rgba(0, 128, 128, 1)"> 98</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)"> 99</span>
<span style="color: rgba(0, 128, 128, 1)">100</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> List&lt;Map&lt;String, String&gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> parsePlan(String planJson) {
</span><span style="color: rgba(0, 128, 128, 1)">101</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 简化的计划解析(实际应用中应该使用完整的JSON解析)</span>
<span style="color: rgba(0, 128, 128, 1)">102</span>         List&lt;Map&lt;String, String&gt;&gt; steps = <span style="color: rgba(0, 0, 255, 1)">new</span> ArrayList&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)">103</span>
<span style="color: rgba(0, 128, 128, 1)">104</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用正则表达式提取步骤信息</span>
<span style="color: rgba(0, 128, 128, 1)">105</span>         Pattern stepPattern =<span style="color: rgba(0, 0, 0, 1)"> Pattern.compile(
</span><span style="color: rgba(0, 128, 128, 1)">106</span>               "\"step_number\":\\s*(\\d+).*?" +
<span style="color: rgba(0, 128, 128, 1)">107</span>                         "\"description\":\\s*\"([^\"]+)\".*?" +
<span style="color: rgba(0, 128, 128, 1)">108</span>                         "\"tool\":\\s*\"([^\"]+)\""<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">109</span> <span style="color: rgba(0, 0, 0, 1)">                Pattern.DOTALL
</span><span style="color: rgba(0, 128, 128, 1)">110</span> <span style="color: rgba(0, 0, 0, 1)">      );
</span><span style="color: rgba(0, 128, 128, 1)">111</span>
<span style="color: rgba(0, 128, 128, 1)">112</span>         Matcher matcher =<span style="color: rgba(0, 0, 0, 1)"> stepPattern.matcher(planJson);
</span><span style="color: rgba(0, 128, 128, 1)">113</span>         <span style="color: rgba(0, 0, 255, 1)">while</span><span style="color: rgba(0, 0, 0, 1)"> (matcher.find()) {
</span><span style="color: rgba(0, 128, 128, 1)">114</span>             Map&lt;String, String&gt; step = <span style="color: rgba(0, 0, 255, 1)">new</span> HashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)">115</span>             step.put("step_number", matcher.group(1<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">116</span>             step.put("description", matcher.group(2<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">117</span>             step.put("tool", matcher.group(3<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">118</span> <span style="color: rgba(0, 0, 0, 1)">            steps.add(step);
</span><span style="color: rgba(0, 128, 128, 1)">119</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">120</span>
<span style="color: rgba(0, 128, 128, 1)">121</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果没有匹配到,创建默认步骤</span>
<span style="color: rgba(0, 128, 128, 1)">122</span>         <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (steps.isEmpty()) {
</span><span style="color: rgba(0, 128, 128, 1)">123</span>             Map&lt;String, String&gt; defaultStep = <span style="color: rgba(0, 0, 255, 1)">new</span> HashMap&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 128, 128, 1)">124</span>             defaultStep.put("step_number", "1"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">125</span>             defaultStep.put("description", "执行任务: " +<span style="color: rgba(0, 0, 0, 1)"> planJson);
</span><span style="color: rgba(0, 128, 128, 1)">126</span>             defaultStep.put("tool", "analyzeText"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">127</span> <span style="color: rgba(0, 0, 0, 1)">            steps.add(defaultStep);
</span><span style="color: rgba(0, 128, 128, 1)">128</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">129</span>
<span style="color: rgba(0, 128, 128, 1)">130</span>         <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> steps;
</span><span style="color: rgba(0, 128, 128, 1)">131</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">132</span>
<span style="color: rgba(0, 128, 128, 1)">133</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> String buildStepInstruction(Map&lt;String, String&gt;<span style="color: rgba(0, 0, 0, 1)"> step) {
</span><span style="color: rgba(0, 128, 128, 1)">134</span>         <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> String.format(
</span><span style="color: rgba(0, 128, 128, 1)">135</span>               "执行步骤 %s:\n" +
<span style="color: rgba(0, 128, 128, 1)">136</span>                         "描述: %s\n" +
<span style="color: rgba(0, 128, 128, 1)">137</span>                         "工具: %s\n" +
<span style="color: rgba(0, 128, 128, 1)">138</span>                         "请使用指定工具完成此步骤。"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">139</span>               step.get("step_number"<span style="color: rgba(0, 0, 0, 1)">),
</span><span style="color: rgba(0, 128, 128, 1)">140</span>               step.get("description"<span style="color: rgba(0, 0, 0, 1)">),
</span><span style="color: rgba(0, 128, 128, 1)">141</span>               step.get("tool"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">142</span> <span style="color: rgba(0, 0, 0, 1)">      );
</span><span style="color: rgba(0, 128, 128, 1)">143</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">144</span>
<span style="color: rgba(0, 128, 128, 1)">145</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">void</span> updateContext(String task, Map&lt;String, String&gt;<span style="color: rgba(0, 0, 0, 1)"> step, String result) {
</span><span style="color: rgba(0, 128, 128, 1)">146</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将步骤结果存入上下文,供后续步骤使用(可选)</span>
<span style="color: rgba(0, 128, 128, 1)">147</span>         String key = MurmurHash.murmur3_32Hash(task) + "_step_" + step.get("step_number") + "_result"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">148</span> <span style="color: rgba(0, 0, 0, 1)">      context.put(key, result);
</span><span style="color: rgba(0, 128, 128, 1)">149</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">150</span>
<span style="color: rgba(0, 128, 128, 1)">151</span>   <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> printContext() {
</span><span style="color: rgba(0, 128, 128, 1)">152</span>         System.out.println("-".repeat(50) + "\n上下文: "<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">153</span>         context.forEach((key, value) -&gt; System.out.println(key + " =&gt; \n" + value + "\n" + "-".repeat(30<span style="color: rgba(0, 0, 0, 1)">)));
</span><span style="color: rgba(0, 128, 128, 1)">154</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">155</span> }</pre>
</div>
<span class="cnblogs_code_collapse">Coordinator</span></div>
<p>注:22行,由于在Planner中明确指定了工具列表,所以在协调器中,创建planner实例时,无需刻意绑定Tools.</p>
<p>完整示例:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_b99e41b4-c310-40e9-bb92-a6420ade4ce9" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_b99e41b4-c310-40e9-bb92-a6420ade4ce9" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_b99e41b4-c310-40e9-bb92-a6420ade4ce9" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">@SpringBootApplication
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> PlanAndExecuteApplication {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span> main(String[] args) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> IOException {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>         ConfigurableApplicationContext context = SpringApplication.run(AgentDesignPatternApplication.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">, args);
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>         ChatModel model = context.getBean("ollamaChatModel", ChatModel.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>         SampleTools sampleTools = context.getBean("sampleTools", SampleTools.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>         String[] testTasks =<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>               "计算 15 加上 27 等于多少?"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">11</span>               "北京现在的天气怎么样?"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">12</span>               "计算半径为5的圆的面积"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">13</span>               "现在是几点?"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">14</span>               "计算长方体的体积,长10,宽5,高3"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">15</span>               "帮我算一下 (25 × 4) ÷ 2 等于多少?"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">16</span>               "快递单123456,现在到哪了?"<span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(0, 128, 128, 1)">17</span>               "我的订单56789,退款到账了没?"
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">      };
</span><span style="color: rgba(0, 128, 128, 1)">19</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>         Coordinator coordinator = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Coordinator(model, sampleTools);
</span><span style="color: rgba(0, 128, 128, 1)">21</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>         <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i &lt; testTasks.length; i++<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)">23</span>             System.out.printf("\n📦 测试用例 %d/%d%n", i + 1<span style="color: rgba(0, 0, 0, 1)">, testTasks.length);
</span><span style="color: rgba(0, 128, 128, 1)">24</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>             Map&lt;String, Object&gt; result =<span style="color: rgba(0, 0, 0, 1)"> coordinator.executeTask(testTasks);
</span><span style="color: rgba(0, 128, 128, 1)">26</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 打印总结</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>             System.out.println("\n✅ 任务完成总结:"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">29</span>             System.out.println("-".repeat(40<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">30</span>             System.out.println("任务: " + result.get("task"<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">31</span>             System.out.println("状态: " + result.get("status"<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">32</span>             System.out.println("耗时: " +<span style="color: rgba(0, 0, 0, 1)"> calculateDuration(
</span><span style="color: rgba(0, 128, 128, 1)">33</span>                     (String) result.get("start_time"<span style="color: rgba(0, 0, 0, 1)">),
</span><span style="color: rgba(0, 128, 128, 1)">34</span>                     (String) result.get("end_time"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)">            ));
</span><span style="color: rgba(0, 128, 128, 1)">36</span>
<span style="color: rgba(0, 128, 128, 1)">37</span>             <span style="color: rgba(0, 0, 255, 1)">if</span> (result.containsKey("execution_results"<span style="color: rgba(0, 0, 0, 1)">)) {
</span><span style="color: rgba(0, 128, 128, 1)">38</span>               @SuppressWarnings("unchecked"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">39</span>               List&lt;Map&lt;String, Object&gt;&gt; executions =
<span style="color: rgba(0, 128, 128, 1)">40</span>                         (List&lt;Map&lt;String, Object&gt;&gt;) result.get("execution_results"<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">41</span>               System.out.println("执行步骤数: " +<span style="color: rgba(0, 0, 0, 1)"> executions.size());
</span><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">43</span>
<span style="color: rgba(0, 128, 128, 1)">44</span>             System.out.println("=".repeat(60<span style="color: rgba(0, 0, 0, 1)">));
</span><span style="color: rgba(0, 128, 128, 1)">45</span>
<span style="color: rgba(0, 128, 128, 1)">46</span>             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 任务间暂停</span>
<span style="color: rgba(0, 128, 128, 1)">47</span>             <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">48</span>               Thread.sleep(2000<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 128, 1)">49</span>             } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (InterruptedException e) {
</span><span style="color: rgba(0, 128, 128, 1)">50</span> <span style="color: rgba(0, 0, 0, 1)">                Thread.currentThread().interrupt();
</span><span style="color: rgba(0, 128, 128, 1)">51</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">52</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">53</span>
<span style="color: rgba(0, 128, 128, 1)">54</span> <span style="color: rgba(0, 0, 0, 1)">      coordinator.printContext();
</span><span style="color: rgba(0, 128, 128, 1)">55</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">56</span>
<span style="color: rgba(0, 128, 128, 1)">57</span>
<span style="color: rgba(0, 128, 128, 1)">58</span>   <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> String calculateDuration(String start, String end) {
</span><span style="color: rgba(0, 128, 128, 1)">59</span>         <span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">60</span>             LocalDateTime startTime =<span style="color: rgba(0, 0, 0, 1)"> LocalDateTime.parse(start);
</span><span style="color: rgba(0, 128, 128, 1)">61</span>             LocalDateTime endTime =<span style="color: rgba(0, 0, 0, 1)"> LocalDateTime.parse(end);
</span><span style="color: rgba(0, 128, 128, 1)">62</span>             Duration duration =<span style="color: rgba(0, 0, 0, 1)"> Duration.between(startTime, endTime);
</span><span style="color: rgba(0, 128, 128, 1)">63</span>             <span style="color: rgba(0, 0, 255, 1)">return</span> String.format("%d秒"<span style="color: rgba(0, 0, 0, 1)">, duration.getSeconds());
</span><span style="color: rgba(0, 128, 128, 1)">64</span>         } <span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) {
</span><span style="color: rgba(0, 128, 128, 1)">65</span>             <span style="color: rgba(0, 0, 255, 1)">return</span> "未知"<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 128, 1)">66</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">67</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">68</span> }</pre>
</div>
<span class="cnblogs_code_collapse">PlanAndExecuteApplication</span></div>
<p>运行结果:</p>
<div class="cnblogs_code"><img src="https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" id="code_img_closed_cb598133-214c-4756-8d77-7241b9623606" class="code_img_closed"><img src="https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif" id="code_img_opened_cb598133-214c-4756-8d77-7241b9623606" class="code_img_opened" style="display: none">
<div id="cnblogs_code_open_cb598133-214c-4756-8d77-7241b9623606" class="cnblogs_code_hide">
<pre>📦 测试用例 1/8

================================================================================
🎯 任务: 计算 15 加上 27 等于多少?
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "加法计算",
    "steps": [
      {
            "step_number": 1,
            "description": "使用add工具计算15加27的结果",
            "tool": "add",
            "parameters": {"a": 15, "b": 27}
      }
    ],
    "expected_output": "42"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用add工具计算15加27的结果
[工具调用] 加法: 15.00 + 27.00 = 42.00
执行结果:
步骤 1: add
输入: {"a": 15, "b": 27}
输出: 42.00
状态: 成功

计算完成!15加27的结果是42.00。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 计算 15 加上 27 等于多少?
状态: completed
耗时: 5秒
执行步骤数: 1
============================================================

📦 测试用例 2/8

================================================================================
🎯 任务: 北京现在的天气怎么样?
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "查询北京天气",
    "steps": [
      {
            "step_number": 1,
            "description": "使用getWeather工具查询北京当前的天气情况",
            "tool": "getWeather",
            "parameters": {"city": "北京"}
      }
    ],
    "expected_output": "北京当前的天气信息,包括温度、湿度、天气状况等"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用getWeather工具查询北京当前的天气情况
[工具调用] 查询天气: 北京
执行结果:
步骤 1: getWeather
输入: {"city": "北京"}
输出: 北京的天气:晴转多云,温度22-28°C,湿度65%,东南风2级
状态: 成功

天气查询完成!北京当前天气为晴转多云,温度22-28°C,湿度65%,东南风2级。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 北京现在的天气怎么样?
状态: completed
耗时: 4秒
执行步骤数: 1
============================================================

📦 测试用例 3/8

================================================================================
🎯 任务: 计算半径为5的圆的面积
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "计算圆的面积",
    "steps": [
      {
            "step_number": 1,
            "description": "使用calculateCircleArea工具计算半径为5的圆的面积",
            "tool": "calculateCircleArea",
            "parameters": {"radius": 5}
      }
    ],
    "expected_output": "78.5"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用calculateCircleArea工具计算半径为5的圆的面积
[工具调用] 圆面积计算: 半径=5.00, 面积=78.54
执行结果:
步骤 1: calculateCircleArea
输入: {"radius": 5}
输出: 半径为 5.00 的圆的面积是 78.54
状态: 成功

计算完成!半径为5的圆的面积是78.54。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 计算半径为5的圆的面积
状态: completed
耗时: 5秒
执行步骤数: 1
============================================================

📦 测试用例 4/8

================================================================================
🎯 任务: 现在是几点?
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "获取当前时间",
    "steps": [
      {
            "step_number": 1,
            "description": "使用getCurrentDateTime工具获取当前时间",
            "tool": "getCurrentDateTime",
            "parameters": {}
      }
    ],
    "expected_output": "当前的时间信息,包括年、月、日、小时、分钟、秒等"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用getCurrentDateTime工具获取当前时间
[工具调用] 当前时间: 2026-02-01 13:43:35
执行结果:
步骤 1: getCurrentDateTime
输入: {}
输出: 2026-02-01 13:43:35
状态: 成功

时间获取完成!当前时间是2026-02-01 13:43:35。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 现在是几点?
状态: completed
耗时: 5秒
执行步骤数: 1
============================================================

📦 测试用例 5/8

================================================================================
🎯 任务: 计算长方体的体积,长10,宽5,高3
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "计算长方体体积",
    "steps": [
      {
            "step_number": 1,
            "description": "使用calculateCuboidVolume工具计算长方体的体积,长10,宽5,高3",
            "tool": "calculateCuboidVolume",
            "parameters": {"length": 10, "width": 5, "height": 3}
      }
    ],
    "expected_output": "150"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用calculateCuboidVolume工具计算长方体的体积,长10,宽5,高3
[工具调用] 长方体体积: 10.00×5.00×3.00=150.00
执行结果:
步骤 1: calculateCuboidVolume
输入: {"length": 10, "width": 5, "height": 3}
输出: 长 10.00、宽 5.00、高 3.00 的长方体体积是 150.00
状态: 成功

计算完成!长10、宽5、高3的长方体体积是150.00。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 计算长方体的体积,长10,宽5,高3
状态: completed
耗时: 5秒
执行步骤数: 1
============================================================

📦 测试用例 6/8

================================================================================
🎯 任务: 帮我算一下 (25 × 4) ÷ 2 等于多少?
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "复合运算计算",
    "steps": [
      {
            "step_number": 1,
            "description": "使用multiply工具计算25乘以4的结果",
            "tool": "multiply",
            "parameters": {"a": 25, "b": 4}
      },
      {
            "step_number": 2,
            "description": "使用divide工具将上一步的结果除以2",
            "tool": "divide",
            "parameters": {"a": 100, "b": 2}
      }
    ],
    "expected_output": "50"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/2: 使用multiply工具计算25乘以4的结果
[工具调用] 乘法: 25.00 × 4.00 = 100.00
执行结果:
步骤 1: multiply
输入: {"a": 25, "b": 4}
输出: 25.00 × 4.00 = 100.00
状态: 成功

计算完成!25乘以4的结果是100.00。

请提供下一步指令。

📝 步骤 2/2: 使用divide工具将上一步的结果除以2
[工具调用] 除法: 100.00 ÷ 2.00 = 50.00
执行结果:
步骤 2: divide
输入: {"a": 100, "b": 2}
输出: 100.00 ÷ 2.00 = 50.00
状态: 成功

计算完成!100除以2的结果是50.00。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 帮我算一下 (25 × 4) ÷ 2 等于多少?
状态: completed
耗时: 9秒
执行步骤数: 2
============================================================

📦 测试用例 7/8

================================================================================
🎯 任务: 快递单123456,现在到哪了?
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "查询快递物流状态",
    "steps": [
      {
            "step_number": 1,
            "description": "使用queryExpressOrder工具查询快递单号123456的物流状态",
            "tool": "queryExpressOrder",
            "parameters": {"orderNumber": "123456"}
      }
    ],
    "expected_output": "快递单号123456的当前物流状态,包括位置、派送进度等信息"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用queryExpressOrder工具查询快递单号123456的物流状态
[工具调用] 快递单: 123456,已经在运输途中,预订明天送达
执行结果:
步骤 1: queryExpressOrder
输入: {"expressOrderNo": "123456"}
输出: 快递单: 123456,已经在运输途中,预订明天送达
状态: 成功

查询完成!快递单号123456的物流状态显示:已经在运输途中,预订明天送达。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 快递单123456,现在到哪了?
状态: completed
耗时: 4秒
执行步骤数: 1
============================================================

📦 测试用例 8/8

================================================================================
🎯 任务: 我的订单56789,退款到账了没?
================================================================================

📋 阶段1: 任务规划
----------------------------------------
生成的计划:
```json
{
    "plan_name": "查询退款进度",
    "steps": [
      {
            "step_number": 1,
            "description": "使用queryRefundProgress工具查询订单56789的退款进度",
            "tool": "queryRefundProgress",
            "parameters": {"orderNumber": "56789"}
      }
    ],
    "expected_output": "订单56789的退款进度信息,包括退款状态、预计到账时间等"
}
```

⚡ 阶段2: 执行计划
----------------------------------------

📝 步骤 1/1: 使用queryRefundProgress工具查询订单56789的退款进度
[工具调用] 订单: 56789,退款已审批通过,预计1-3个工作日按原路退回
执行结果:
步骤 1: queryRefundProgress
输入: {"orderNo": "56789"}
输出: 订单: 56789,退款已审批通过,预计1-3个工作日按原路退回
状态: 成功

查询完成!订单56789的退款进度显示:退款已审批通过,预计1-3个工作日按原路退回。

请提供下一步指令。

✅ 任务完成总结:
----------------------------------------
任务: 我的订单56789,退款到账了没?
状态: completed
耗时: 4秒
执行步骤数: 1</pre>
</div>
<span class="cnblogs_code_collapse">运行结果</span></div>
<p>&nbsp;时序图-AI生成</p>
<p><img src="https://img2024.cnblogs.com/blog/27612/202601/27612-20260131224928024-246667291.png" alt="plan_and_execute_simple" width="480" loading="lazy" style="border: 1px solid rgba(0, 0, 0, 1)"></p>
<p>文中示例代码:</p>
<p>https://github.com/yjmyzz/agentic_turoial_with_langchain4j<br></p>
<p>&nbsp;</p>
<p>参考:</p>
<p>Building Effective AI Agents \ Anthropic</p>
<p>[译] AI Workflow &amp; AI Agent:架构、模式与工程建议(Anthropic,2024)</p>
<p>Agents and Agentic AI | LangChain4j</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="padding:5px;background:#ff9;border:solid 1px #ccc">作者:菩提树下的杨过<br />
出处:http://yjmyzz.cnblogs.com
<br/>
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
</div><br><br>
来源:https://www.cnblogs.com/yjmyzz/p/19559350/agentic-patterns-using-langchain4j-11-plan-and-execute
頁: [1]
查看完整版本: Agent设计模式学习(基于langchain4j实现)(11) - PlanAndExecute