7. 复杂任务怎么做的任务拆分?为什么要拆分?效果如何提升?
7. 复杂任务怎么做的任务拆分?为什么要拆分?效果如何提升?
💡 简要回答
我理解任务拆分的原因是 LLM 一次性处理太复杂的任务很容易出错,把大任务拆成小步骤,每步聚焦一件事,准确率会明显提升。拆分方式主要有两种:一种是静态拆分,提前把步骤写死;另一种是动态拆分,让 LLM 自己根据目标规划步骤,更灵活但也更难控制。拆完之后步骤之间可能有依赖关系,我的经验是把能并行的步骤并发跑,端到端延迟可以降很多,有时能降 40% 到 60%。
📝 详细解析
为什么任务要拆分?
先从一个具体的失败案例说起,感受一下为什么任务拆分是必要的。
你让一个 LLM 一次性完成「帮我写一份竞品分析报告」,它需要搜索多家竞品的信息、整理核心功能对比、分析各自优缺点、写结论。听起来是一件事,但其实是四件完全不同的事混在一起。LLM 收到这个任务,往往会出现这几种毛病:在搜索阶段就开始掺杂分析意见,在写对比表格时突然引入新的竞品信息,报告写到一半忘掉了前面整理的某个关键数据点,最后输出一篇结构混乱的文章,读下来感觉什么都有、什么都不深。

这不是偶发的问题,而是有系统性原因的。LLM 的工作台,也就是 context window,是有大小限制的,能同时处理的信息量是有上限的。任务越大,中间状态越多,桌面就越乱:搜索结果、分析意见、写了一半的段落全部堆在一起,LLM 很难持续追踪「我现在在做哪个子目标」。就像让一个人同时记住十件事并全部做对,比让他每次只专注做一件事出错率高得多。
任务拆分要解决的,就是这个「桌面太乱」的问题。把一个大目标切成多个小步骤,每个步骤只做一件事,LLM 的全部注意力都集中在这一件事上,桌面保持干净,质量自然高。而且还有一个额外的好处:每一个步骤都是独立的输出,可以被单独检查和验证。某一步出了问题,重试那一步就行,不需要从头跑整个任务。
任务拆分两种思路
任务拆分有两种思路,一种是你自己来拆,一种是让 LLM 来拆。
静态拆分是你提前把任务流程设计好,固定成一个确定的 Workflow,每一步是什么、按什么顺序执行,全部事先写死。比如「写一篇技术博客」,固定拆成:搜索资料 -> 整理大纲 -> 逐段撰写 -> 润色校对,四步顺序执行。好处是行为完全可预测,出了问题知道是哪一步的问题,好排查;坏处是灵活性低,遇到你没设计进流程的情况就容易卡住。
动态拆分则是把「任务拆解」这件事本身也交给 LLM 来做。你给它一个目标,让它先输出一个执行计划,再按计划一步步执行,这是 Plan-and-Execute 模式的核心思想。

用项目管理来类比。一个没有经验的程序员接到任务「开发用户登录系统」,可能会直接开始写代码,边写边想「接下来要做什么」,结果很容易漏掉某个环节,比如忘了写错误处理,或者到最后才想起来要做密码加密。但一个有经验的工程师会先写项目计划:需求分析 -> 数据库设计 -> 接口设计 -> 编码实现 -> 安全测试,把整体结构想清楚了再开始动手。
Plan-and-Execute 就是给 LLM 引入这个「先规划再执行」的习惯,把「想清楚要做什么」和「真正去做」分成两个独立的阶段。
整个 Plan-and-Execute 流程分三个阶段:

- 第一阶段是规划,把目标告诉 LLM,让它输出有序的步骤列表,只做规划,不做任何实际执行;
- 第二阶段是执行,拿着计划逐步执行每个步骤,每一步都要把前面所有步骤的结果作为 context 传进去,LLM 始终知道整件事做到哪里了,不会「失忆」;
- 第三阶段是汇总,所有步骤跑完之后,把各步骤的产出整合在一起生成最终输出。动态拆分的优势是灵活性强,LLM 可以根据具体任务的特点制定最合适的计划;劣势是规划质量不稳定,规划一旦出了问题,后续所有执行步骤都建立在错误的基础上。
步骤拆好之后,还有一件重要的事:分析步骤之间的依赖关系。有些步骤必须等前一步完成才能开始,有些步骤之间没有依赖,可以同时进行。识别出可以并行的步骤,是降低总耗时的关键。
用厨师做饭来建立直觉。你要同时处理三件事:烧水、切菜、腌肉。如果傻傻地串行,等水烧开了再切菜,切完菜再腌肉,总时间是三件事之和。

但一个有经验的厨师会这样:先烧水,烧水的同时切菜腌肉,水开了三件事都好了,直接下锅。总时间由「最长的那条路径」决定,也就是烧水的时间,因为切菜和腌肉都在等水开的过程中完成了。并行执行降低的不是「每步的时间」,而是「关键路径的总时间」。
回到 Agent 的场景,假设你有步骤 1、2、3、4,其中步骤 3 依赖步骤 1 的结果,步骤 4 依赖步骤 2 和步骤 3:
import asyncio
async def execute_parallel_steps(independent_steps: list):
# asyncio.gather 让多个步骤同时开始执行,不等某一个完成再启动下一个
# 这就像厨师烧水的同时切菜,两件事并发进行
tasks = [execute_step_async(step) for step in independent_steps]
results = await asyncio.gather(*tasks) # 等所有并发步骤都完成,一起拿结果
return results
依赖图:步骤 1 和步骤 2 相互独立,可以并行
步骤 3 需要步骤 1 的结果才能开始
步骤 4 需要步骤 2 和步骤 3 都完成才能开始
步骤1 ──────────────┐
├──> 步骤3 ──┐
步骤2 ──────────────┘ ├──> 步骤4(最终输出)
└────────────────────── ┘
如果这四步全部串行,总时间是四步之和。识别出依赖关系并行执行后,关键路径变成「步骤1/2(并行)-> 步骤3 -> 步骤4」,假设每步各需要 3 秒,串行是 12 秒,并行之后是 9 秒。步骤越多、可并行的越多,节省的时间越可观,实际项目里降低 40% 到 60% 的端到端延迟是很正常的数字。
任务不是拆得越细越好,粒度的把握很重要。
- 拆太细有两个代价:步骤越多、LLM 调用次数越多,总 token 消耗上升;而且步骤太碎,每步只做一件极小的事,LLM 看不到全局,产出的各部分容易衔接生硬。
- 拆太粗又回到了原来的问题:每步负责的事太多,出错概率上升,也无法定位问题出在哪一步。
实践中通常把「原子操作」作为划分单步的标准:这个步骤只做一件独立的事,边界清晰,做完有明确的输出,和其他步骤不互相依赖。
具体举例感受一下区别。「搜索竞品 A 的产品信息」是原子的,只做一件事(搜索),有明确的输入和输出,做完就完了。
「整理竞品分析」不是原子的,它包含了搜索信息、筛选关键点、格式化输出三件事,还没开始就已经有三个子任务了。
判断一个步骤是不是原子的,有一个简单方法:你能给它写一个清晰的函数签名吗?能的话,它大概是原子的;如果你发现函数里还要分好几个阶段、处理好几类情况,那大概需要再拆。
