Agent强化知识
复杂任务怎么做的任务拆分?
为什么任务要拆分?
先从一个具体的失败案例说起,感受一下为什么任务拆分是必要的。
你让一个 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
asyncdef 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 的产品信息」是原子的,只做一件事(搜索),有明确的输入和输出,做完就完了。
「整理竞品分析」不是原子的,它包含了搜索信息、筛选关键点、格式化输出三件事,还没开始就已经有三个子任务了。
判断一个步骤是不是原子的,有一个简单方法:你能给它写一个清晰的函数签名吗?能的话,它大概是原子的;如果你发现函数里还要分好几个阶段、处理好几类情况,那大概需要再拆。
A2A
为什么单个 Agent 不够用?
要理解 A2A 是干什么的,得先把「单 Agent 的天花板」搞清楚。
一个 Agent 的本质是:一个 LLM + 一组工具 + 一段上下文窗口。这三个维度都有自己的天花板。工具是有限的,你不可能给一个 Agent 装 100 个工具,模型处理起来效率极低,容易混乱;上下文窗口是有限的,128K tokens 听起来很多,但复杂任务积累的中间产物,搜索结果、草稿、反思记录,会很快把窗口塞满,后面的生成根本顾不上前面写了什么;专业能力也是有限的,同一个 Agent 既做代码审查又做市场分析,不如专门为各自任务配置/微调的 Agent 效果好。
举一个具体任务:「帮我做一份 AI 编程工具的竞品分析报告,要有行业趋势、技术对比、商业模式分析和 SWOT」。让单个 Agent 做这件事,问题是:搜索结果和草稿会把上下文撑满,等到写 SWOT 时,前面的行业趋势分析早就被挤出了有效注意力范围;而且市场调研和技术分析需要不同的知识侧重,一个 Agent 很难全面兼顾。
解决方案很自然:把任务拆开,交给不同的专业 Agent 并行处理,最后汇总。一个「调度 Agent」负责任务拆分,「市场分析 Agent」专门做趋势调研,「技术研究 Agent」专门做工具对比,每个只需聚焦自己擅长的部分,整体效果远好于一个 Agent 包揽所有。
多 Agent 的基础问题,Agent 之间怎么互相认识?
多 Agent 系统有一个绕不开的基础问题:Agent A 要把任务委托给 Agent B,它得先知道 B 能做什么。但怎么知道呢?
最笨的方案是写死配置:A 的代码里硬编码「B 可以做竞品分析」。这样太脆了,B 的能力一变,A 的代码就得改,根本没法维护。
更好的方案是让 B 主动「发名片」,声明自己能做什么,A 来查。这就是 A2A 里 Agent Card 的设计思路。每个 A2A Agent 都在 /.well-known/agent.json 路径下发布一张名片,里面写清楚自己叫什么、能做哪类任务(Skill 列表)、支不支持流式返回、支不支持异步回调(push notification,任务完成后主动通知调用方)。任何想和它协作的 Agent,先去拿这张名片,再决定要不要把任务委托给它。
Agent Card 里最关键的是 Skill 列表,每个 Skill 描述一类能力,比如「竞品分析」「行业趋势分析」,并带有示例输入。调度 Agent 用这些 Skill 描述来做任务路由决策,「这个任务和哪个 Agent 的哪个 Skill 最匹配?」。
这套机制让整个多 Agent 系统变得可插拔:新加一个 Agent,发布它的 Agent Card,调度 Agent 就能自动发现和利用它,完全不需要改调度 Agent 的代码。
Task,A2A 里的一等公民
A2A 里任务协作的基本单位是 Task。调度 Agent 把一段任务委托给另一个 Agent,就是创建一个 Task;接收方执行这个 Task;完成后把结果作为 Task 的产出(artifacts,可以是文本、文件等)返回。
Task 有完整的生命周期状态:submitted(已提交等待处理)-> working(正在执行)-> completed/failed(完成或失败)。
为什么需要这么完整的状态机?因为 A2A 专门为长时间任务设计。一个「竞品分析」任务可能要跑几分钟,先搜索、再整理、再写报告,不可能让调度 Agent 同步等着。所以调度 Agent 提交任务后可以去处理其他事情,通过轮询状态或者 push notification(任务完成时接收方主动回调通知)来得知任务完成了。这套状态管理机制,正是为了支持这种异步长任务协作的。
调度 Agent 的视角很干净:给 Agent B 提交一个 Task,定期查一下状态,等到 completed 了去取 artifacts。整个过程不需要知道 B 内部用了什么工具、调了几次 LLM,完全黑盒。每个专业 Agent 自己的实现对外不可见,这正是解耦的意义所在。
Agent 的微服务化
如果你有后端开发经验,A2A 其实不陌生:它就是 Agent 世界里的微服务架构。
在微服务里,每个服务是独立部署的 HTTP 服务,有自己的 API 文档,服务之间通过 HTTP 互相调用,支持异步消息队列处理耗时任务。A2A 的设计几乎照搬了这套思路,只不过把「服务」换成了「Agent」:Agent Card 对应 API 文档,Task 状态机对应异步队列,/.well-known/agent.json 对应服务注册中心的条目。
每个 A2A Agent 对外就是一个 HTTP 服务,任何支持 A2A 的系统都可以发现它、向它发任务、接收结果。不绑定特定的 AI 框架,不依赖特定的编程语言。这和 MCP Server 的设计理念一脉相承:MCP 让工具成为独立标准化服务,A2A 让 Agent 成为独立标准化服务。
A2A 和 MCP 的关系
理清两者关系最简单的方式是看方向:MCP 是 Agent 向下连工具,A2A 是 Agent 向外连其他 Agent。
一个专业 Agent 内部:用 MCP 连各种工具(数据库、浏览器、代码执行器),用 Function Calling 让 LLM 触发这些工具调用。多个 Agent 之间:用 A2A 互相通信、分工协作。两个协议解决的是完全不同维度的问题,不存在谁替代谁。
在一个复杂的多 Agent 系统里,这两者通常同时在用:MCP 负责每个 Agent 和工具之间的纵向连接,A2A 负责 Agent 之间的横向协作通信。两层协议各管一个维度,合在一起才能支撑起真正复杂的 Agent 系统。

