跳转至

第六章:复杂任务拆分与调度

6.1 任务拆分解决什么问题

用户说“调研三家竞品并写比较报告”,为什么不直接丢给一次模型调用?如果资料需要现查、不同来源口径不一、失败后还要继续,这个目标就需要可跟踪的执行步骤。复杂任务可能具有以下特征:

  • 步骤数量多;
  • 中间状态多;
  • 部分信息需要执行后才能获得;
  • 子任务之间存在依赖;
  • 不同步骤需要不同工具或专业能力;
  • 单个步骤可能失败并需要重试;
  • 需要跨多次模型或工具调用才能完成。

任务拆分要做的是把复杂目标改写成一组:

可执行、可验证、可调度、可恢复的任务单元。

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    G["复杂目标"] --> D["任务拆分"]
    D --> T1["子任务 1"]
    D --> T2["子任务 2"]
    D --> TN["子任务 N"]
    T1 --> V["独立验证"]
    T2 --> V
    TN --> V
    V --> S["结果组合"]

本章用虚构的竞品研究任务串联例子:调研最近半年的产品、定价和市场动态,最终交付带来源的报告。示例参数和时长用于演算,其中三个调研分支取 40、50、60 秒,合并取 10 秒,不是项目实测结果。

6.2 为什么要拆分

6.2.1 降低上下文压力

Agent 执行时间越长,产生的消息、工具结果和中间结论越多。如果把所有内容都持续加入上下文,会导致:

  • 超出 Context Window;
  • Token 成本和延迟上升;
  • 重要约束被噪音稀释;
  • 早期信息被压缩或遗忘;
  • 模型更难判断当前进度。

拆分后,每个子任务只需要最小充分上下文,并通过 Artifact、状态或引用传递结果。

6.2.2 降低单步推理难度

把“调研、分析、比较、验证、写报告”挤在一次调用里,容易混淆证据收集和结论生成。拆分有机会降低单次求解难度,但也会增加交接错误,仍需比较最终报告质量,而不能只看各子任务是否成功。

拆开后,每次模型调用的目标和输入都会更聚焦,输出格式也更容易约束,成功标准也更容易定义。

6.2.3 支持独立验证

完整任务失败时,很难定位问题来自搜索、分析还是结果合成。拆分后,可以为每个步骤设置验收条件:

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    T["执行子任务"] --> V["满足验收条件?"]
    V -->|是| NEXT["进入后续任务"]
    V -->|否| R["局部重试或回退"]
    R --> T

6.2.4 支持局部重试和恢复

如果第 8 个步骤失败,应先确定哪些已完成结果仍然有效,再重试受影响的步骤,而不是一律从第 1 步重新执行。若上游输入、权限或业务状态已经变化,旧结果不能仅凭 done 标记复用。

这要求:

  • 子任务具有稳定 ID;
  • 中间结果可以持久化;
  • 已完成步骤支持 checkpoint;
  • 写操作尽量幂等;
  • 失败影响范围可以追踪。

6.2.5 支持并行执行

数据依赖已满足且没有共享资源冲突的子任务可以并发执行。没有显式依赖边不等于真正独立:例如两个任务都修改同一配置文件,就需要串行、锁或版本检查。

6.2.6 支持专业化

不同子任务可以分配给不同执行器:

  • 小模型处理分类和提取;
  • 强模型处理复杂规划;
  • 搜索 Agent 负责资料收集;
  • 代码 Agent 负责实现;
  • 确定性程序负责计算和验证。

6.3 什么时候不应该拆

任务拆分也会产生额外成本:

  • Planner 调用;
  • 状态持久化;
  • 子任务调度;
  • 上下文切换;
  • 中间结果序列化;
  • 最终结果合并;
  • 更多失败点。

以下情况通常不需要复杂拆分:

  • 单次模型调用即可稳定完成;
  • 任务只有一个原子操作;
  • 子任务之间高度耦合,拆分后需要反复同步;
  • 拆分和合并成本高于执行本身;
  • 不存在独立验收标准。

拆分是一种成本优化和可靠性手段,不是复杂任务的固定仪式。

6.4 好的任务单元是什么

“以原子操作为标准”是一个好的起点,但工程上更准确的定义是:

一个任务单元应当在输入和前置条件满足后,能够单独执行、验证和恢复,并具有明确的输出与副作用边界。

一个完整的 Task Contract 通常包含:

字段 作用
id 稳定标识
goal 子任务目标
inputs 必需输入和引用
dependencies 前置任务
executor Tool、模型、Agent 或 Workflow
outputs 结构化输出或 Artifact
success_criteria 验收条件
side_effects 是否写入外部系统
timeout_seconds 最大执行时间,明确以秒为单位
retry_policy 重试与回退策略
risk_level 权限和审批等级
plan_version / input_versions 识别当前计划及输入版本,防止复用过期结果
idempotency_key 有副作用的业务操作在重试时保持同一语义
{
  "id": "research-competitor-a",
  "goal": "收集竞品 A 最近六个月的产品更新",
  "inputs": {
    "competitor": "A",
    "time_range": {
      "start": "2026-02-28T00:00:00Z",
      "end_exclusive": "2026-08-28T00:00:00Z"
    }
  },
  "dependencies": [],
  "executor": "research-agent",
  "outputs": {
    "type": "artifact",
    "schema": "competitor-update-list"
  },
  "success_criteria": [
    "产品更新优先使用官方发布说明;无法证实的内容标为待核实",
    "每项更新包含发布日期和链接"
  ],
  "timeout_seconds": 120,
  "retry_policy": {
    "max_attempts": 2
  },
  "risk_level": "read-only"
}

这是教学用契约,不是任何框架的完整 API。示例中的时间和重试次数只是配置值;来源数量也不保证事实可靠,两篇转载可能源于同一错误。验收规则应与业务标准一致,而不能完全交给 Planner 临时决定。

契约把“最近六个月”固定成 UTC 起止时间,包含起点、不包含终点,避免几天后的重试悄悄改变研究范围。max_attempts: 2 在本例中包含首次执行,即最多再尝试一次;这类语义也要写清楚。

6.5 粒度:不能太粗,也不能太细

6.5.1 粒度过粗

例如:

调研所有竞品并写一份完整战略报告。

问题包括:

  • 输入和输出范围过大;
  • 很难定义单步成功标准;
  • 失败后只能整体重试;
  • 无法充分并行;
  • 中间过程不可观测。

6.5.2 粒度过细

例如:

  1. 打开搜索工具;
  2. 输入一个关键词;
  3. 点击搜索;
  4. 读取第一条结果;
  5. 复制一句话。

问题包括:

  • 调度开销过高;
  • 模型调用次数增加;
  • 状态过于碎片化;
  • 每个步骤缺乏独立业务价值;
  • 整体连贯性下降。

6.5.3 合理粒度

更合理的子任务是:

搜索竞品 A 最近六个月的官方产品更新,并输出带来源的结构化列表。

它同时具备:

  • 明确目标;
  • 有限范围;
  • 独立输出;
  • 可验证标准;
  • 可重试性;
  • 可与其他竞品调研并行。

6.5.4 粒度判断清单

一个子任务如果同时满足多数条件,通常粒度较合适:

  • 可以由一个执行器在有限时间内完成;
  • 输入和输出能够结构化描述;
  • 有明确的 Done Definition;
  • 失败后可以局部重试;
  • 与其他任务通过有限接口交互;
  • 不需要持续共享大量隐含上下文;
  • 完成后能产生可复用的 Artifact。

6.6 静态拆分

静态拆分由开发者预先定义步骤和依赖,适合流程稳定、规则清晰的场景。

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    IN["客户问题"] --> C["意图分类"]
    C --> R["检索知识库"]
    R --> G["生成回答"]
    G --> S["安全检查"]
    S --> OUT["返回结果"]

6.6.1 优势

  • 行为可预测;
  • 容易测试;
  • 成本和延迟容易估算;
  • 权限边界清晰;
  • 适合审计和合规。

6.6.2 局限

  • 无法覆盖所有未知情况;
  • 流程变化需要修改代码;
  • 分支过多时维护复杂;
  • 不适合开放式目标。

静态拆分通常由 Workflow、DAG 或状态机实现。

6.7 动态拆分

动态拆分由 Planner 根据目标和当前环境生成子任务。

规划、执行并检查整体验收。

%%{init: {"flowchart": {"nodeSpacing": 10, "rankSpacing": 16, "padding": 6}}}%%
flowchart TB
    G["用户目标"]
    P["LLM Planner"]
    PLAN["生成任务列表或 DAG"]
    E["Executor"]
    O["Observation"]
    DONE["整体验收通过?"]
    OUT["交付结果"]
    G --> P
    P --> PLAN
    PLAN --> E
    E --> O
    O --> DONE
    DONE -->|是| OUT

整体验收失败后,先检查限制,再决定是否继续。

%%{init: {"flowchart": {"nodeSpacing": 10, "rankSpacing": 16, "padding": 6}}}%%
flowchart TB
    DONE["整体验收通过?"]
    LIMIT["仍有预算且允许继续?"]
    STOP["暂停或报告未完成"]
    RP["需要重规划?"]
    E["Executor"]
    P["LLM Planner"]
    DONE -->|否| LIMIT
    LIMIT -->|否| STOP
    LIMIT -->|是| RP
    RP -->|否| E
    RP -->|是| P

适合:

  • 无法预先确定步骤数量;
  • 不同输入需要完全不同的执行路径;
  • 任务依赖外部环境反馈;
  • 需要探索和动态决策。

6.7.1 优势

  • 灵活;
  • 能处理开放式任务;
  • 可以根据新信息调整;
  • 适合长周期研究和编码任务。

6.7.2 局限

  • 规划质量不稳定;
  • 可能遗漏关键步骤;
  • 可能产生无法执行的任务;
  • 容易过度拆分或拆分不足;
  • Planner 调用增加成本和延迟。

因此,动态计划必须经过 Schema 校验、依赖检查和可执行性检查。

6.8 分层拆分

复杂任务不适合一次性拆到最底层。更稳健的方法是分层规划:

  1. 先生成高层里程碑;
  2. 只展开当前里程碑;
  3. 执行并验证;
  4. 再展开下一层。
%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart LR
    G["战略报告"] --> M1["资料收集"]
    G --> M2["对比分析"]
    G --> M3["报告生成"]

    M1 --> T11["竞品 A"]
    M1 --> T12["竞品 B"]
    M1 --> T13["行业趋势"]

    M2 --> T21["功能对比"]
    M2 --> T22["价格对比"]
    M2 --> T23["风险分析"]

这借鉴了 Hierarchical Task Network(HTN)的分层思路:

  • 高层任务描述目标;
  • 方法定义如何展开;
  • 叶子任务最终由 Tool、模型或 Agent 执行。

但自然语言多级清单不等于完整 HTN 规划器。经典 HTN 还需要明确定义可用分解方法、方法适用条件、任务顺序约束及原子动作语义;随意生成层级并不会继承形式化规划的保证。

图中的连线表示拆分层级,不表示执行顺序。资料收集、对比分析和报告生成虽然都是目标的子任务,仍须另行建立数据依赖,不能据此同时启动。

分层拆分的优势

  • 避免一次生成过长计划;
  • 降低早期错误影响范围;
  • 保留全局方向;
  • 可以按阶段分配预算;
  • 更适合长时间运行的 Agent。

6.9 增量拆分与滚动规划

滚动规划(Rolling Horizon Planning)不试图一次规划整个未来,而是:

  1. 规划最近的若干步骤;
  2. 执行其中一步或一个阶段;
  3. 获取真实反馈;
  4. 更新剩余计划;
  5. 再向前展开。
%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    S["当前状态"] --> P["规划近期步骤"]
    P --> E["执行下一步"]
    E --> O["获取反馈"]
    O --> U["更新状态"]
    U --> P

它适合环境变化快、远期信息不可靠的任务。

与完整 Plan-and-Execute 相比:

完整计划 滚动规划
一开始生成较完整步骤 每次只规划有限范围
全局可见性强 对环境变化适应性强
远期计划容易失效 需要更多规划轮次

6.10 自适应拆分

自适应拆分根据运行情况动态决定:

  • 是否继续拆分;
  • 是否合并过细任务;
  • 是否重新规划;
  • 是否改变执行器;
  • 是否提高或降低并行度。

执行产生用于判断任务粒度的信号。

flowchart TB
    T[当前任务]
    E[执行或试探]
    M[监控信号]
    D{是否需要调整粒度?}
    T --> E
    E --> M
    M --> D

调整粒度或计划后,返回当前任务。

%%{init: {"flowchart": {"nodeSpacing": 10, "rankSpacing": 16, "padding": 6}}}%%
flowchart TB
    D["是否需要调整粒度<br/>?"]
    SPLIT["继续拆分"]
    MERGE["合并任务"]
    REPLAN["重新规划"]
    T["当前任务"]
    D -->|任务过大或不确定| SPLIT
    D -->|任务过细或开销过<br/>高| MERGE
    D -->|关键假设失效| REPLAN
    SPLIT --> T
    MERGE --> T
    REPLAN --> T

计划仍合适时继续原有执行。

flowchart TB
    D{是否需要调整粒度?}
    KEEP[保持当前计划]
    E[执行或试探]
    D -->|计划仍合适| KEEP
    KEEP --> E

6.10.1 自适应信号

不确定性

  • 模型置信度低;
  • 候选方案差异大;
  • 输入信息不足;
  • 依赖关系不清晰。

模型自报的置信度只能作为线索,不能直接当作正确概率;更稳妥的触发条件还包括缺失输入、候选冲突和可复现的验证失败。

执行反馈

  • Tool 连续失败;
  • 输出无法通过验收;
  • 实际结果偏离计划;
  • 发现新的关键事实。

资源压力

  • Context Window 接近上限;
  • Token 或费用消耗过快;
  • 单个任务运行时间过长;
  • 并发资源不足。

进度信号

  • 多轮没有新增有效信息;
  • 相同步骤反复执行;
  • 任务之间出现重复工作;
  • 某些子任务长期阻塞。

6.10.2 自适应策略

信号 可选策略
子任务目标过于宽泛 继续向下拆分
大量微任务只传递少量信息 合并任务
外部环境发生变化 重规划受影响部分
Context 压力过高 外部化 Artifact、压缩或分阶段
关键路径阻塞 提高优先级或更换执行器
多个任务重复检索 共享 Artifact 或合并检索
验证连续失败 改变方法、升级模型或请求人工帮助

6.11 依赖关系与任务 DAG

拆分完成后,必须分析子任务之间的依赖关系。

设每个节点表示任务,每条有向边表示“后一个任务依赖前一个任务”;确认没有循环后,才得到任务 DAG:

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    A["收集竞品 A"] --> D["产品对比"]
    B["收集竞品 B"] --> D
    C["收集行业趋势"] --> E["趋势分析"]
    D --> F["生成报告"]
    E --> F
    F --> G["事实与引用检查"]

6.11.1 依赖类型

数据依赖

后续任务需要前置任务的输出。

控制依赖

只有满足某个条件才执行后续任务。

资源依赖

多个任务竞争相同的限流 API、数据库连接或执行环境。

资源竞争通常由容量、信号量或锁处理,不一定要为任意两个任务添加顺序边。条件分支还要规定未选分支如何标记为 skipped,汇总节点是等待全部、任一,还是某个指定子集,避免等待永远不会运行的节点。

安全依赖

某个步骤必须在审批、认证或检查通过后执行。

6.11.2 DAG 校验

执行前至少需要检查:

  • 是否存在循环依赖;
  • 是否存在缺失节点;
  • 输入是否由前置步骤提供;
  • 是否有永远无法满足的条件;
  • 写操作顺序是否安全;
  • 并发执行是否会产生竞争。

每版 DAG 保持无环,重试次数、重规划版本和循环退出条件由外层状态机记录。任务不是只有 pending/done:至少应能区分就绪、运行中、成功、失败、阻塞、跳过与取消。分配任务时原子领取或加租约;仅靠“先查询待办,再执行”会让多个 Worker 重复领取。

租约也不是万能的:旧 Worker 可能在租约过期后继续运行。每次重新分配任务都应更新领取标识,结果存储端在提交时原子校验这个标识和计划版本,拒收过期执行器的结果。对外部写入则仍需工具端的并发控制和幂等契约,调度器丢弃迟到结果无法撤销副作用。

6.12 并行执行

依赖已满足、可用资源允许并发的任务可以采用 Fan-out / Fan-in,即分发到多个分支,再汇总结果:

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    P["Planner"] --> A["任务 A"]
    P --> B["任务 B"]
    P --> C["任务 C"]
    A --> J["Join / Aggregate"]
    B --> J
    C --> J

6.12.1 顺序执行时间

若任务依次执行,忽略调度开销时:

\[ T_{seq}=\sum_{i=1}^{n}d_i \]

其中 dᵢ 是第 i 个任务的执行时间。

6.12.2 理想并行时间

若所有任务互不依赖且资源充足,理想并行时间接近最慢任务:

\[ T_{parallel}\approx \max(d_1,d_2,\ldots,d_n)+T_{overhead} \]

其中 T_overhead 包含调度、通信和结果合并开销。

6.12.3 加速比

\[ Speedup=\frac{T_{seq}}{T_{parallel}} \]

并行节省比例为:

\[ Saving=1-\frac{T_{parallel}}{T_{seq}} \]

6.12.4 并行收益的限制

并行收益取决于:

  • 可并行任务比例;
  • 最慢分支;
  • 任务依赖;
  • API 限流;
  • 模型并发限制;
  • 调度与合并开销;
  • 失败和重试。

Amdahl 定律可以表示理论上限:

\[ Speedup(k)=\frac{1}{(1-p)+\frac{p}{k}} \]

其中:

  • p 是可并行部分比例;
  • k 是并行执行单元数量。

这个模型假设固定工作量、可并行部分可均分,并忽略通信和争用。实际 Agent 的分支长度、模型限流和重试都可能变化,不能用固定节省比例预测。并行调度通常缩短的是总完成时间,不会自动缩短 DAG 原有的依赖关键路径。

6.12.5 具体示例

按前述示例,三个独立调研任务分别耗时 40 秒、50 秒和 60 秒,结果合并耗时 10 秒。

计入两种方案共同需要的合并时间,顺序执行为:

\[ T_{seq}=40+50+60+10=160 \]

理想并行执行:

\[ T_{parallel}=60+10=70 \]

该示例中的节省比例约为:

\[ Saving=1-\frac{70}{160}=0.5625 \]

这里的 56.25% 来自这组具体数据,并不是所有任务都能达到的固定收益。

6.13 关键路径

任务 DAG 中按执行时长计的最长依赖路径称为 Critical Path。资源充足、任务立即就绪执行且忽略额外开销时,总完成时间等于这一路径长度;有并发上限、排队或资源争用时,它只是下界。

flowchart LR
    A[任务 A<br/>20s] --> C[任务 C<br/>40s]
    B[任务 B<br/>50s] --> D[任务 D<br/>10s]
    C --> E[汇总<br/>10s]
    D --> E

两条主要路径:

  • A → C → E:70 秒;
  • B → D → E:70 秒。

若只有一个执行器,总耗时为 130 秒而非 70 秒。图中两条路径并列关键,只缩短其中一条也不会突破另一条的 70 秒下界。

优化不在关键路径上的任务,可能不会降低整体完成时间。调度器应优先关注:

  • 关键路径上的慢任务;
  • 阻塞多个后续节点的任务;
  • 高失败率任务;
  • 稀缺资源任务。

6.14 并发不等于无限并行

无限提高并发可能导致:

  • API 限流;
  • 数据库连接耗尽;
  • Token 和费用瞬间增长;
  • 多个任务写入同一资源产生冲突;
  • 失败重试形成流量放大;
  • 汇总节点被大量结果淹没。

生产系统需要:

  • 最大并发数;
  • 每个 Tool 的独立限流;
  • 优先级队列;
  • Backpressure;
  • 超时和取消传播;
  • 并发写入控制;
  • 失败隔离。

6.15 任务之间如何传递结果

不应把每个子任务的完整对话直接复制给所有后续任务。更好的方式是生成结构化 Artifact。

{
  "artifact_id": "competitor-a-updates",
  "schema": "competitor-update-list",
  "producer": "research-competitor-a",
  "created_at": "2026-08-28T16:00:00Z",
  "summary": "竞品 A 最近六个月发布了三个主要更新",
  "data_uri": "artifact://competitor-a-updates.json",
  "sources": [
    "https://example.com/source-1",
    "https://example.com/source-2"
  ]
}

后续任务只读取:

  • Artifact 摘要;
  • 必需字段;
  • 可追溯来源;
  • 需要时再加载完整内容。

这可以降低上下文重复和信息污染。

Artifact 还应绑定输入版本、生产任务和校验结果;消费者需要检查数据时效与权限,而不只读摘要。摘要可能漏掉例外条件,来源网页也可能包含不可信指令,不能因为进入内部 Artifact 就提升为系统指令。

6.16 Planner、Scheduler、Executor 与 Verifier

复杂任务的拆分系统,通常会把下面四类职责分开:

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart TB
    G["Goal"] --> P["Planner"]
    P --> DAG["Task DAG"]
    DAG --> S["Scheduler"]
    S --> E["Executors"]
    E --> V["Verifiers"]
    V -->|通过| ART["Artifacts"]
    V -->|局部失败| S
    V -->|计划失效| P

Planner

负责:

  • 生成任务;
  • 确定依赖;
  • 指定验收条件;
  • 根据反馈重新规划。

Scheduler

负责:

  • 找出当前可执行任务;
  • 控制并发;
  • 管理优先级;
  • 处理资源和限流;
  • 调度重试。

Executor

可以是:

  • Tool;
  • 普通程序;
  • LLM;
  • ReAct Agent;
  • 专业 Worker Agent。

Verifier

负责检查:

  • 输出 Schema;
  • 事实和引用;
  • 测试和规则;
  • 任务成功标准;
  • 安全和权限。

将这些职责分开,比让一个 LLM 同时规划、执行、验证和调度更容易控制。

6.17 失败处理

子任务失败后不应只有“无限重试”一种策略。

6.17.1 局部重试

适合临时网络错误、限流和偶发模型输出错误。

只读请求可按错误类型退避并加入抖动;写请求超时则可能是“服务端成功、响应丢失”。先用操作 ID 查询状态,或以同一幂等键重试,不要直接生成新请求再次执行。权限拒绝、非法输入和预算耗尽通常不应原样重试。

6.17.2 参数调整

根据结构化错误修改查询、参数或超时。

6.17.3 更换执行器

例如:

  • 小模型失败后升级强模型;
  • 搜索 API 失败后切换备用数据源;
  • Agent 失败后进入人工处理。

6.17.4 重新拆分

如果任务本身过大,可以拆成更小步骤。

6.17.5 重新规划

如果关键假设失效,需要修改依赖和后续计划。

更新时保留历史版本,而不是原地覆盖正在执行的计划。应计算受影响子图,取消尚未开始的旧节点,对运行中任务请求取消,并隔离其迟到结果;已完成 Artifact 只有在输入与前提仍有效时才可复用。取消请求不保证外部副作用已撤销。

6.17.6 补偿操作

对于已经产生副作用的操作,不能简单重试。需要设计:

  • 回滚;
  • 补偿事务;
  • 幂等键;
  • 人工确认。

AWS 的幂等 API 设计说明【466】强调,用调用者提供的请求标识表达同一次操作,并在服务端协调去重记录与副作用提交。Task ID 本身不会产生 exactly-once 保证;幂等键的有效期、参数一致性和作用域都要明确。补偿也不是时间倒流:退款不等于撤销发货,已发送邮件通常无法回滚。

在局部修复瞬时故障与参数错误。

flowchart TB
    F[任务失败]
    C{失败类型}
    R[退避重试]
    A[调整参数]
    F --> C
    C -->|临时错误| R
    C -->|参数错误| A

结构性失败与不可逆影响需要不同处理。

%%{init: {"flowchart": {"nodeSpacing": 10, "rankSpacing": 16, "padding": 6}}}%%
flowchart TB
    C["失败类型"]
    S["继续拆分"]
    P["重新规划"]
    H["补偿或人工处理"]
    C -->|任务过大| S
    C -->|计划失效| P
    C -->|不可逆副作用| H

6.18 自适应拆分控制器

一个能落地的自适应拆分控制器大致会这样运行:

flowchart TB
    P[分层规划器] --> SCH[调度器]
    SCH --> EX[执行器池]
    EX --> VER[验证器]
    VER --> CTRL[自适应控制器]
    CTRL -->|保持| SCH
    CTRL -->|修改计划| P

目标进入分层规划器,生成的任务 DAG 交给调度器。执行观察结果进入验证器,进度、质量、成本与风险指标再供控制器判断。修改计划包括继续拆分、合并任务、修改依赖或替换失效计划。只有必要任务与整体验收均通过,才能交付结果;预算耗尽或无进展时停止并报告,高风险时转人工审核。

图中调整粒度或依赖后重新生成、校验一版 DAG,不直接修改正在运行的图。控制器不应只听 Planner 的自然语言判断,还应使用可观测指标:

  • 任务完成率;
  • 验证通过率;
  • 重试次数;
  • 重复调用比例;
  • Context 使用量;
  • Token 和费用;
  • 关键路径变化;
  • 阻塞时间;
  • 风险等级。

6.19 复杂研究任务示例

目标:

调研三家竞品最近半年的产品、定价和市场动态,并形成带来源的比较报告。

6.19.1 高层拆分

%%{init: {"flowchart": {"nodeSpacing": 16, "rankSpacing": 16, "padding": 10}}}%%
flowchart LR
    G["竞品研究报告"] --> R["资料收集"]
    G --> A["对比分析"]
    G --> W["报告撰写"]
    G --> V["事实验证"]

这张图列出工作范围,不是并行调度图。对比分析依赖资料,报告依赖分析;资料收集时可以先核查来源,成稿后还要再核查组合出的结论。

6.19.2 展开资料收集

flowchart LR
    R[资料收集] --> A1[竞品 A]
    R --> B1[竞品 B]
    R --> C1[竞品 C]
    R --> T[行业趋势]

在数据访问已授权、彼此不写同一资源且并发预算足够时,四个任务可以并行执行。

6.19.3 结构化输出

每个竞品调研任务输出统一 Artifact:

{
  "competitor": "A",
  "product_updates": [],
  "pricing_changes": [],
  "market_events": [],
  "sources": [],
  "open_questions": []
}

6.19.4 动态调整

如果竞品 A 出现重大收购事件:

  1. Planner 插入“收购事件专项调研”;
  2. Scheduler 将它设为高优先级;
  3. 分析任务等待该 Artifact;
  4. 报告结构增加“战略影响”章节。

6.19.5 验证

先检查研究材料,成稿后再检查最终报告,避免合成阶段引入新错误:

  • 关键结论是否至少有一个可靠来源;
  • 时间范围是否一致;
  • 不同竞品是否使用同一比较维度;
  • 是否区分事实、推断和建议;
  • 是否存在过时或冲突信息。

6.20 常见反模式

一次性生成几十个详细步骤

远期步骤建立在尚未验证的假设上,很快会失效。

只有任务名称,没有 Task Contract

“调研竞品”无法明确判断是否完成。

所有子任务共享完整上下文

可能增加成本、干扰决策并让子任务看到不需要的敏感内容。扩大可见数据范围不等于获得合法权限,仍需按每个执行器的授权过滤上下文。

所有任务都交给同一个大模型

忽略了普通代码、小模型、Tool 和专业 Agent 的成本优势。

没有依赖分析就并行

可能读取未完成数据,或产生写入竞争。

失败后从头开始

浪费已完成结果,也难以定位错误。

Planner 自己验证自己的计划

容易共享相同盲点。应使用规则、Schema、独立 Verifier 或人工审核。

把并发收益写成固定百分比

并行收益必须根据 DAG、关键路径和真实指标计算。

6.21 如何选择拆分策略

flowchart TB
    DEP[构建依赖 DAG] --> PAR[并行调度]
    PAR --> ADAPT[运行时自适应调整]

构建 DAG 前先选择规划结构。已知且稳定的步骤适合静态工作流;部分已知的步骤适合固定结构加动态子任务;步骤未知时需要动态规划器,其中长任务采用分层加滚动规划,其余采用 Plan-and-Execute。这四种选择都汇入图中的依赖 DAG、并行调度与运行时自适应调整。

落地时通常按这个顺序收敛:

  1. 优先确定性静态拆分;
  2. 对未知部分使用动态 Planner;
  3. 长任务采用分层和滚动规划;
  4. 对可调度步骤建立依赖图,循环由有界状态机或分版 DAG 表达;
  5. 只并行真正独立的任务;
  6. 用运行指标驱动自适应拆分。

6.22 评估任务拆分质量

指标 含义
Completion Rate 子任务和整体任务完成率
Validation Pass Rate 子任务首次通过验收的比例
Retry Locality 失败是否只重试受影响部分
Parallel Efficiency 在关键路径和容量下界约束下,资源利用能否缩短总完成时间
Planning Overhead 规划成本占总成本的比例
Context Efficiency 每个任务是否只获得必要上下文
Artifact Reuse 中间结果是否被有效复用
Replan Rate 计划失效和重规划频率
Duplicate Work 不同任务重复工作的比例
Recovery Time 失败后恢复所需时间

一个拆分方案不是任务数越多越好,而是:

以更低的总成本和风险,提高整体任务成功率与可恢复性。

6.23 本章总结

复杂任务拆分可以分为三个层次:

为什么拆

  • 降低上下文和推理压力;
  • 支持独立验证;
  • 支持局部重试;
  • 支持并行和专业化。

怎么拆

  • 静态拆分适合稳定流程;
  • 动态拆分适合开放目标;
  • 分层拆分避免一次展开过深;
  • 滚动规划利用最新反馈;
  • 自适应拆分根据运行指标调整粒度。

拆完之后

  • 定义 Task Contract;
  • 建立依赖 DAG;
  • 分析关键路径;
  • 调度可并行任务;
  • 持久化 Artifact;
  • 设置验证、重试、回退和人工介入。

归根结底,粒度要同时满足两个要求:

每个任务应当足够小,以便独立执行、验证和重试;同时足够大,能够产生有意义、可复用的业务结果。

参考资料

本章的参考资料、阅读建议与来源说明见集中参考资料章节。