Agent 复杂任务怎么做的任务拆分?为什么要拆分?效果如何提升?
一则或许对你有用的小广告
欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书
《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于
Spring AI + Spring Boot 3.x + JDK 21...,查看介绍《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,查看介绍;演示链接:http://116.62.199.48:7070/《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/
新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍
截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观
面试考察点
- 拆分动机:面试官想知道的不只是 "拆了能提精度",更想看你能不能讲清楚为什么拆了会提。是 context window 卡住了?还是 LLM 追踪长程多目标任务的能力本身就不行?能答到这层才算懂原理。
- 拆分策略:能不能分清静态拆分、动态拆分(Plan-and-Execute)、自适应拆分(ADaPT),并说清楚它们各自的适用边界。
- 优化手段:拆完之后呢?并行优化能降多少延迟?有数据吗?答不上来基本就是 "用过" 而非 "精通"。
核心答案
任务拆分说白了就是:一个 LLM 一次做不对的大任务,切成它能稳定做对的小步骤,每步只干一件事。
为什么要拆?三个原因:
- Context Window 有限:任务越大,中间状态(搜索结果、半成品、分析意见)堆得越多,模型越容易 "失忆" 和跑偏
- 子目标追踪能力弱:LLM 维持长程多目标任务状态的能力本来就不强,子目标一多就互相串味
- 可验证、可重试:拆分后每步独立产出,错了只重试那一步,不用整条链路回滚
主流拆分方式有两类:静态拆分(流程写死,提前切好)和动态拆分(让 LLM 自己规划,也就是 Plan-and-Execute)。再往下卷还有自适应拆分(ADaPT:做不好就继续拆)。
拆完之后 "效果提升" 主要体现在三个方面:
| 维度 | 提升来源 | 典型收益 |
|---|---|---|
| 准确率 | 单步聚焦,桌面干净 | 复杂任务成功率从 30% 提到 70%+ |
| 端到端延迟 | 依赖稀疏时并行执行 | 降低 40%-60% |
| Token 成本 | 失败重试只重试子任务;Planner 用小模型 | 总 Token 消耗下降 30%+ |
深度解析
一、为什么必须拆分?从一个翻车案例说起
假设你直接让 Agent 完成 "帮我写一份竞品分析报告"。这一句话,其实是四件事混在一起:搜索多家竞品信息、整理功能对比、分析优劣、写结论。
LLM 一口气干这事,毛病马上就冒出来。搜索阶段就开始掺杂分析意见,写表格到一半突然又引入新的竞品,写到末尾把前面整理的关键数据点给忘干净。最后产出一篇 "什么都有、什么都不深" 的乱炖。
这不是偶发,是系统性问题。根源是 LLM 的注意力是有上限的。任务越大,context window 里堆的中间状态越多,"桌面越乱",模型追踪 "我现在到底在干哪个子目标" 的能力就越差。让一个人同时盯十件事全部做对,和让他一次只做一件事,出错率根本不在一个量级,一个道理。
任务拆分解决的就是这个 "桌面太乱" 的问题。把大目标切成小步骤,每步只盯一件事,桌面保持干净,准确率自然就上来了。还有个额外好处:每步独立产出,可以单独验证、单独重试,错了不用从头来。
二、两种拆分思路:静态 vs 动态
任务拆分有两条路:要么你自己拆,要么让 LLM 拆。
顺便提一句,这里说的 "拆分" 是 Agent 层面把任务切成多个独立执行步骤,跟 CoT(思维链)、ToT(思维树)那种 LLM 内部推理 是两码事,别在面试里混为一谈。CoT 是一次生成里的内部展开,不涉及多步工具调用。
静态拆分:你提前把流程设计好,固定成 Workflow,每步是什么、什么顺序,全部写死。比如 "写技术博客" 固定拆成:搜资料 → 整理大纲 → 逐段撰写 → 润色校对。
- 优点:行为完全可预测,出问题好定位是哪一步的锅
- 缺点:灵活性差,遇到没预想过的情况就卡住
动态拆分:把 "拆解" 这件事也交给 LLM。给它一个目标,让它先吐一份计划出来,再按计划一步步执行。这就是 Plan-and-Execute 模式。流程如下:
上图是 Plan-and-Execute 的三个阶段,"先想清楚要做什么、再动手、最后收口",是个闭环:
- 规划阶段:Planner 像项目经理开启动会,把目标变成一份有序的步骤列表,这步只规划不干活,注意力全在 "想清楚" 上
- 执行阶段:Executor 按列表逐步执行,每一步把前面所有步骤的结果作为 context 传进去,让 LLM 始终知道整件事做到哪了,不会像 ReAct 那样走着走着被岔路带跑
- 汇总阶段:所有步骤跑完后,Solver 把各步产出整合成连贯的最终输出,不只是简单拼接,还要解决各步之间的衔接问题
Plan-and-Execute vs ReAct 对比:
| 对比维度 | ReAct | Plan-and-Execute |
|---|---|---|
| 思考模式 | 单步思考-行动循环 | 两阶段分离:先规划后执行 |
| 规划范围 | 只规划下一步 | 一次性规划完整路径 |
| LLM 调用 | 每轮循环都调 LLM | 规划时一次,执行时多次 |
| 适合场景 | 工具调用少、需探索 | 长程任务、子任务有依赖 |
| 成本 | 高(频繁思考) | 低(减少重复思考) |
经验之谈:工具调用次数 ≤ 3 用 ReAct,存在子任务依赖、步骤超过 4 步用 Plan-and-Execute。
三、Plan-and-Execute 的 Java 实现(LangChain4j)
下面是用 LangChain4j 实现 Plan-and-Execute 的核心骨架。关键是把 Planner、Executor、Coordinator 三件套拆开,让规划器和执行器各管各的:
import dev.langchain4j.agent.agent.Agent;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.service.AgenticServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import java.util.*;
/**
* 任务规划器:只负责把大任务拆成有序步骤
* 关键点:Planner 不绑定 Tools,避免它越权去执行,只输出 JSON 计划
*/
public interface Planner {
@SystemMessage("""
你是一个任务规划专家。请将用户任务分解为有序的执行步骤。
输出严格 JSON:
{
"plan_name": "任务名",
"steps": [
{"step_number": 1, "description": "步骤描述",
"tool": "工具名", "parameters": {"k": "v"}}
]
}
可用工具:1.add 2.getWeather 3.queryExpressOrder 4.queryRefundProgress
""")
@UserMessage("任务:{{request}}")
@Agent("基于用户问题生成执行计划")
String createPlan(@V("request") String request);
}
/**
* 任务执行器:拿到单个步骤就执行一步
* 关键点:绑定 Tools,让 Executor 能真正调工具拿结果
*/
public interface Executor {
@SystemMessage("""
你是任务执行器。每次只执行一个步骤,然后报告结果。
输出格式:
步骤{n}: [工具名]
输入: {参数}
输出: {结果}
状态: [成功/失败]
""")
@UserMessage("{{step}}")
@Agent("执行单个步骤")
String executeStep(@V("step") String step);
}
/**
* 协调器:串联规划-执行-汇总三阶段
* 这里是任务拆分真正落地的地方
*/
public class Coordinator {
private final Planner planner;
private final Executor executor;
public Coordinator(ChatModel model, Object tools) {
// Planner 不绑定 tools,省 Token
this.planner = AgenticServices.agentBuilder(Planner.class)
.chatModel(model)
.chatMemoryProvider(id -> MessageWindowChatMemory.withMaxMessages(15))
.build();
// Executor 绑定 tools,负责真正执行
this.executor = AgenticServices.agentBuilder(Executor.class)
.chatModel(model)
.chatMemoryProvider(id -> MessageWindowChatMemory.withMaxMessages(15))
.tools(tools)
.build();
}
public Map<String, Object> executeTask(String task) {
Map<String, Object> result = new LinkedHashMap<>();
try {
// 阶段 1:规划——这一步把大任务拆开
String planJson = planner.createPlan(task);
List<Map<String, String>> steps = parsePlan(planJson);
result.put("plan", steps);
// 阶段 2:逐步执行
List<String> executionResults = new ArrayList<>();
for (Map<String, String> step : steps) {
String stepResult = executor.executeStep(formatStep(step));
executionResults.add(stepResult);
// 关键点:把上一步结果塞进 context,下一步能用
// 这是避免 LLM "失忆" 的核心
}
result.put("executionResults", executionResults);
result.put("status", "completed");
} catch (Exception e) {
result.put("status", "failed");
result.put("error", e.getMessage());
}
return result;
}
private List<Map<String, String>> parsePlan(String planJson) {
// 实际项目里用 Jackson/Gson 解析 JSON,这里省略
return List.of();
}
private String formatStep(Map<String, String> step) {
return "执行步骤 " + step.get("step_number")
+ ",描述:" + step.get("description")
+ ",工具:" + step.get("tool");
}
}
几个工程细节要注意:
- Planner 不绑定 Tools:避免它越权去执行,还能省一大笔 Token(不必把所有工具描述塞进 Planner 的上下文)
- Executor 绑定 Tools:执行阶段才需要真正的工具调用能力
- 步骤间 context 传递:每一步的产出要显式塞进下一步的上下文,这是避免 "失忆" 的关键
四、并行优化:拆完之后的隐藏加速器
很多人拆完就完事了。其实还有个大招没使,分析步骤间的依赖关系,把能并行的步骤并发跑。
先看一个依赖图:

上图是一个典型的任务依赖 DAG(有向无环图):
- 步骤 1 和 步骤 2 互相独立,可以并行
- 步骤 3 依赖步骤 1 的结果,必须等它完成
- 步骤 4 依赖步骤 2 和步骤 3,要等它们都完成
假设每步耗时 3 秒:
- 串行执行:3+3+3+3 = 12 秒
- 并行执行:关键路径变成 "步骤1/2(并行 3 秒)→ 步骤3(3 秒)→ 步骤4(3 秒)" = 9 秒
省下来的不是 "每步的时间",是 关键路径的总时间。步骤越多、依赖越稀疏、I/O 占比越高,并行空间越大。实际项目里降低 40%-60% 的端到端延迟是常见数字。当然如果所有步骤都强依赖上一步,并行空间基本为零,这点别被面试官套出来。
用 Java 的 CompletableFuture 落地并行执行:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ParallelExecutor {
private final ExecutorService pool = Executors.newFixedThreadPool(8);
private final Executor executor; // LangChain4j 的 Executor
/**
* 按依赖图并行执行步骤
* independentSteps 是没有依赖关系的步骤列表
*/
public List<String> runParallel(List<String> independentSteps) {
// 用 CompletableFuture 让多个步骤同时跑,而不是串行等
List<CompletableFuture<String>> futures = independentSteps.stream()
.map(step -> CompletableFuture.supplyAsync(
() -> executor.executeStep(step), pool))
.toList();
// 等所有并行步骤都完成,一起拿结果
return futures.stream()
.map(CompletableFuture::join)
.toList();
}
}
五、自适应拆分(ADaPT):做不好就继续拆
静态拆分和动态拆分都有个隐含假设:拆分在任务开始时一次性完成,执行中不再调整。但现实经常翻车,你提前拆了三步,执行到第二步发现它比想象中复杂得多,LLM 一次根本做不好。
这时候就该上 ADaPT(As-Needed Decomposition) 了。核心思路一句话讲完:
先让 Executor 试,做得好就继续走;做不好就交给 Planner 把它进一步拆成更小的子任务,对每个子任务重复同样的流程。
整体效果就像一棵递归展开的任务树:只有真正做不好的节点才会被继续拆,简单的节点一步到位。任务越复杂,递归层数越深;任务越简单,可能一层都不拆。计算开销跟任务实际难度成正比,不会对所有任务一刀切。
六、Replan 机制:计划得跟着现实走
还有个常被忽略的环节,Replan。计划定好不是神圣不可侵犯,执行中经常发现原计划不靠谱。比如你规划 "先查竞品 A 定价、再查 B 定价、最后做对比",结果发现竞品 A 早就停运了,那后面那一步对比就没意义了,整个计划要重新调整。
Replan 的做法:每个步骤执行完后,把当前结果和剩余计划一起交给 Planner,让它判断 "基于新信息,后面的计划还合理吗",合理就继续,不合理就重新规划剩余步骤。
代价是每步多一次 "评估" 的 LLM 调用。折中做法是设置触发条件,只有当某步输出和预期差异很大、或者步骤执行失败时,才触发 Replan,别无谓地每步都跑一遍。
七、拆分粒度:以 "原子操作" 为标准
任务不是越细越好。拆太细有两个代价:
- 步骤越多,LLM 调用次数越多,Token 消耗往上涨
- 步骤太碎,每步只做极小的事,LLM 看不到全局,各部分衔接生硬
判断一个步骤是不是原子的,有个简单办法:能不能给它写一个清晰的函数签名? 能,基本就是原子的;如果函数里还要分好几个阶段、处理好几类情况,那大概率得再拆。
举例,"搜索竞品 A 的产品信息" 是原子的;"整理竞品分析" 不是,里面藏了搜索、筛选、格式化三件事。
效果如何提升?量化指标
拆分带来的收益,面试官最爱听数字。从三个维度量化:
上图把三类收益画在一起:
- 准确率:复杂任务成功率从 30% 提到 70%+(来自单步聚焦、桌面干净、可独立验证重试)
- 延迟:端到端延迟降低 40%-60%(来自依赖稀疏时的 DAG 并行,前提是 I/O 占大头)
- 成本:Token 总消耗下降 30%+(失败只重试子任务 + Planner 用小模型 + 减少重复思考)
再补一个验证标准,拆得好不好用三条卡:
- 完备性:所有步骤加在一起,能不能覆盖原始任务的全部要求
- 独立性:步骤间职责边界清晰,没有两个步骤做同一件事
- 可验证性:每步执行完,能不能用简单标准自动判断对错(建议拆分时就写好 "验收标准",像写单元测试的断言一样)
面试高频追问
-
追问一:Plan-and-Execute 和 ReWOO 有什么区别?
ReWOO(Reasoning Without Observation)走得更极端:它在规划阶段就用占位符(如
#E1、#E2)把所有证据引用写好,执行阶段把工具结果填回占位符。全程只在规划、汇总时调 LLM,中间执行步骤不需要 LLM 介入。Token 消耗比 Plan-and-Execute 更低,但灵活性差一些,适合任务结构相对固定的场景(如 ETL)。 -
追问二:拆分粒度怎么把握?
还是以 "原子操作" 为标准。能用一个清晰函数签名描述的就是原子步骤;如果函数里还要分多阶段、处理多类情况,就得再拆。实践中常用 200-500 tokens 的单步产出作为粒度参考。
-
追问三:如何评估一个 Agent 任务拆分系统的好坏?
看三个指标:任务成功率(端到端跑通率)、平均步骤数(反映规划质量,过多说明拆太碎)、平均 Token 消耗(反映成本)。还可以用 ADaPT 的思路看 "需要二次拆分的步骤占比",占比越高,说明 Planner 的初版规划越差。
常见面试变体
- "Plan-and-Execute 和 ReAct 哪个更好?分别适用什么场景?"
- "你在项目中是怎么做 Agent 任务规划的?踩过哪些坑?"
- "如何处理执行过程中发现原计划不合理的情况?"(这就是问 Replan)
- "为什么 ReWOO 比 Plan-and-Execute 更省 Token?原理是什么?"
记忆口诀
任务拆分三步走:先想 "桌面为什么乱"(context window 上限),再想 "怎么切才合理"(静态/动态/自适应),最后想 "切完怎么加速"(DAG 并行 + Replan)。
一句话记忆:先规划再执行(Plan-and-Execute),失败就重规划(Replan),做不好就继续拆(ADaPT),有依赖画 DAG(并行)。
总结
任务拆分这道题要答出三个层次才算完整:为什么拆(context window 有限 + 子目标追踪弱 + 可独立验证重试)、怎么拆(静态写死 / 动态 Plan-and-Execute / 自适应 ADaPT)、拆完还要做什么(依赖分析做并行、Replan 应对意外、原子粒度把控)。最后补一句量化收益,准确率、延迟、成本三维提升,这道题就答得相当漂亮了。
