TECH NOTE
2566 字 13 分钟 系列 16 / 2216 主 Agent 的任务拆分与串并行决策
收录于合集 多 Agent 精讲 / 附录与延伸
ON THIS PAGE / 目录13 节
一句话回答:拆分分两级——主 Agent 把用户请求拆成子任务清单(谁做、做什么、依赖谁),子任务内部的工具编排交给下游 Agent;串行还是并行只看子任务之间的数据依赖,而依赖是由动作契约算出来的,不是让模型临场决定。
一、任务拆分是怎么做的
1)拆两级,主 Agent 只做上面那一级
| 层级 | 谁拆 | 拆成什么 | 拆完就不管什么 |
|---|---|---|---|
| 宏观(跨 Agent) | 主 Agent | 请求 → 子任务清单(每个子任务指定执行方 + 依赖关系) | 不管工具怎么调、参数怎么拼、失败怎么重试 |
| 微观(Agent 内) | 领域 Agent | 子任务 → 工具调用序列(Function Calling 自己决定) | 不管还有没有别的子任务,也不管全局顺序 |
| 编排 | 编排层 | 不拆,只按清单调度 | 它不是决策者,是执行状态的持有者 |
一句话:主 Agent 决定”谁做什么、什么顺序”;领域 Agent 决定”这一步具体怎么干”。
2)拆出来的东西是一份契约,不是一段自然语言计划
SubTask( id, # 唯一标识:依赖引用、断点续跑、日志回溯都用它 agent, # 谁执行(哪个领域 Agent) action, # 做什么(必须来自动作注册表,不能自由发挥) inputs, # 输入切片:引用 state 字段或上游产物,不塞整包上下文 depends_on, # 依赖哪些子任务 ← 串并行的唯一依据 timeout, # 超时 retry, # 重试策略(次数 / 退避 / 幂等键) success_criteria, # 成功判据:没有它就无法自动重试,也判断不了该降级还是转人工)为什么必须落成结构化契约:后面的依赖计算、波次编排、失败降级、并行合并,全都依赖这些字段。如果拆出来是”先查规则再看看召回然后写文案”这种自然语言,代码无法校验、无法重试、也无法统计。
3)拆的依据:按”业务动作”切,不按”工具调用”切
- 一个子任务 = 一个能独立验收的业务动作,例如:查活动规则 / 召回商品 / 生成话术 / 合规审核。
- 不能按工具切:工具是执行细节,一个业务动作在下游可能对应好几次工具调用;按工具切会让子任务数量爆炸,调度开销吃掉并行收益。
- 粒度经验值:平均子任务数 < 1.2 → 拆得太细(开销大于收益);永远只有 1 个 → 拆得太粗(拿不到并行和独立重试的好处)。
- 分配领域 Agent 时,权限是硬门槛(先按业务线过滤,再排序),其次才是能力匹配、健康负载、成本与历史效果。
4)模型负责什么、代码负责什么
| 环节 | 谁做 | 具体做法 |
|---|---|---|
| 识别”要做哪几个动作” | 模型 | 结构化输出(JSON Schema / Pydantic)+ 枚举约束:只能从动作注册表里选 |
| 定动作之间的先后(偏序) | 代码 | 用动作契约的 requires / produces 求交集,做拓扑排序 |
| 校验与兜底 | 代码 | 模型多给的依赖只当”提示”;缺依赖 → 保守串行;动作不在注册表 → 拒绝并降级 |
| 选领域 Agent | 主 Agent(规则 + 模型) | 意图标签匹配能力描述;权限先过滤 |
为什么不让模型直接决定顺序(这是这件事的核心判断):
- 不稳定:同样输入两次调用,模型可能给出不同的依赖顺序 → 同一个请求两次行为不一致,没法回归测试。
- 不可回滚:改一句提示词,整条流程的偏序可能全变,出了事故定位不到是哪一步的锅。
- 依赖是业务事实,不是模型知识:哪个动作需要哪些输入,是数据字典/接口契约里定义好的,不该让模型”猜”。
二、串行还是并行怎么决定
1)主判据:数据依赖(producer → consumer)
A 的产出是 B 的输入 → B 依赖 A → 必须串行(A → B)A、B 互不需要对方的产出 → 无依赖 → 可以并行以”推荐一批活动商品 + 文案”为例:
flowchart LR A["① 查活动规则"] --> D["④ 合规审核"] B["② 召回商品"] --> C["③ 生成话术"] --> D A -. "无依赖,可并行" .- B这张图在说什么:① 与 ② 互不依赖 → 同一波次并行;③ 需要 ② 的召回结果 → 排在 ② 之后;④ 是硬顺序(审核必须最后)。最终得到三个波次:wave1=[①,②]、wave2=[③]、wave3=[④]。
2)完整判定流程(代码里就是这五步,按顺序套)
| 步 | 判什么 | 怎么判 | 结果 |
|---|---|---|---|
| 1 | 数据依赖 | B.requires ∩ A.produces ≠ ∅ | B 排到 A 之后 |
| 2 | 硬顺序(与数据无关也必须先后) | 契约里的顺序规则:审核通过才允许发布、鉴权通过才允许调下游、扣款成功才允许发货 | 强制串行 |
| 3 | 副作用与幂等 | side_effect = true 的动作不并行;同一资源的写操作遵单写者 | 写操作串行,读操作可并行 |
| 4 | 合成波次 | 把”依赖已全部满足”的节点合成一波,受并发上限约束 | waves = [[①,②],[③],[④]] |
| 5 | 人工节点单独成波 | 需要用户确认 / 审批的节点 | 单独一波,等人,不占并发额度 |
3)六条判据汇总(每条都要能说出”违反会怎样”)
| 判据 | 典型场景 | 违反的后果 |
|---|---|---|
| 数据依赖 | 召回 → 生成话术 | 下游拿不到输入,只能编或报错 |
| 硬顺序(合规 / 资金) | 先审核后发布、先扣款后发货 | 合规事故、不可逆的资金错误 |
| 副作用与幂等 | 改同一张订单 / 同一个购物车 | 竞态、重复下单、数据错乱 |
| 用户可感知的顺序 | 客服:先登记保修 → 再分类 → 再给方案 | 用户被并行结果淹没,对话不连贯 |
| 资源与成本 | 十路并行 | 触发下游限流、P99 抖动、token 放大 |
| 可恢复性 | 长批处理任务 | 一个分支挂掉,整批结果交付不了 |
4)并行的收益是延迟,不是成本
- 并行把 P95 压下来,但通常让 token 更贵——每个并行分支都要带自己的上下文。官方数据里多领域场景,Skills 只调 3 次却最贵(约 15K token),Handoffs 串行 7+ 次约 14K+。
- 所以策略是:先按依赖排出偏序,再把”无依赖 + 只读 + 可幂等”的节点合成一波并行;写操作、合规节点、需要用户确认的节点一律单独成波。
三、走一遍完整例子
请求:“给这个用户推一批活动商品,文案要合规。“
主 Agent 产出的子任务清单: ① query_campaign_rules → 规则 Agent depends_on=[] 只读 ② recall_products → 推荐 Agent depends_on=[] 只读 ③ generate_copy → 推荐 Agent depends_on=[②] 只读(输入 = 召回结果) ④ compliance_review → 审核 Agent depends_on=[③] 只读 + 硬顺序(审核后才能发)
代码算出的波次: wave1 = [①, ②] 无依赖 → 并行 wave2 = [③] 等 ② 的召回结果 wave3 = [④] 硬顺序:审核必须最后
执行与合并: 波内并行(图扇出 / asyncio.gather),结果按 reducer 归并; ④ 判定"不合规" → 一票否决,回到 ③ 重写(最多 N 轮);仍不过 → 转人工,带上下文工单。
失败处理(按代价从低到高): 波内某个子任务失败 → 重试(幂等键,重试前先查状态而不是重放动作) → 换实例 → 缩小能力集降级 → 跳过并标注 → 整体转人工四、工程落地要点
| 关注点 | 做法 |
|---|---|
| 单路串行 | Command(goto=...) 路由到单个节点 |
| 并行扇出 | Send(...) 列表——分支数量运行时才确定 |
| 并行结果合并 | reducer(Annotated[list, operator.add])自动归并 + 一个汇总节点;冲突按优先级仲裁(合规一票否决) |
| 顺序与恢复 | checkpointer + 同一 thread_id;状态外置,实例重启可断点续跑 |
| 依赖数据怎么传 | 结构化字段走 state,不把上游结论用自然语言塞进 prompt(避免转述失真与 token 浪费) |
| 防失控 | 并发上限 + 每层超时 + 总超时 + 最大轮次(防止 Agent 之间互相踢皮球) |
| 可观测 | 每个子任务一个 span(request_id / thread_id / run_id),能回答”这次为什么是串行” |
该盯的四个数:
| 指标 | 说明 | 异常信号 |
|---|---|---|
| 并行度分布 | 每次请求实际并行执行的波次宽度 | 长期恒为 1 → 规则太保守,白吃串行的延迟 |
| 依赖违例率 | 下游先于上游执行 / 拿不到输入的比例 | 目标为 0;>0 说明契约与模型输出没对齐 |
| 子任务数分布 | 每个请求拆出几个子任务 | 均值 < 1.2 → 拆得太细,开销大于收益 |
| 失败归因分布 | 失败集中在哪个环节 | 集中在”路由后的第一个子任务” → 分错 Agent 了 |
五、小结
- 拆分两级:主 Agent 拆子任务清单(谁做、依赖谁),领域 Agent 拆工具调用;主 Agent 拆完就不管执行细节。
- 子任务必须是契约:含
depends_on、timeout、retry、success_criteria——否则没法自动重试,也判断不了该降级还是转人工。 - 串并行看数据依赖,再叠加硬顺序(合规 / 资金)、副作用(写操作单写者)、并发成本、人工节点这四类约束,最终得到一组执行波次。
- 模型给意图,代码定 DAG:模型只能从动作注册表里选动作,代码按
requires / produces算偏序;模型多说的依赖只当提示,缺依赖就保守串行。 - 并行的收益是延迟不是成本:写操作与合规节点永远单独成波。
口径说明:两级分配、“看子任务之间有无数据依赖”、编排层只托管不分配,来自项目资料;L1/L2/L3 三层入口与部分字段是按职责推导的分层设计,对外讲时说”我们的分层设计”即可。
16 主 Agent 的任务拆分与串并行决策
https://aicooleo.cn/blog/posts/multiagent-16-task-split/