加载中...
  • 多 Agent 编排模式:什么时候并行,什么时候串行 loading

    吃透 AI Agent 开发 · 第 29 篇 · 第六章 · Multi-Agent

    并行能缩短等待,但会增加重复、合并和限流成本。编排应该由任务依赖和证据需求决定。

    并行能缩短等待,但会增加重复、合并和限流成本。编排应该由任务依赖和证据需求决定。

    先画依赖,不要先开线程

    “多 Agent”经常被误解成同时启动几个模型请求。真正的编排问题是:哪些子任务互不依赖,哪些必须读取前一项的证据,哪些结果可以合并,哪些动作不能重复。并行是依赖图的结果,不是默认姿势。

    flowchart LR
      Brief[任务简报] --> A[读取代码结构]
      Brief --> B[检查配置风险]
      Brief --> C[准备测试案例]
      A --> Merge[主 Agent 汇总]
      B --> Merge
      C --> Merge
      Merge --> Decision[决定是否修改]
      Decision --> Write[单一写入者]
    

    三个只读子任务可以并行,写入仍然由一个有明确权限的角色完成。这样并行缩短的是调查等待,不会把合并冲突和副作用扩散到每个子 Agent。

    用契约约束子任务

    interface WorkItem {
      id: string
      goal: string
      inputs: string[]
      allowedTools: string[]
      doneWhen: string
      output: 'inline' | 'artifact'
    }
    
    interface WorkResult {
      id: string
      status: 'completed' | 'failed' | 'killed'
      evidence: string[]
      preview: string
      artifactFile?: string
    }
    

    契约要写完成标准和允许工具,不要只写一句“帮我看看”。没有 doneWhen,主 Agent 很难判断子任务是完成、猜测还是等待更多输入。

    四种编排选择

    任务关系 编排方式 合并要求
    互不依赖的只读检查 并行 每项带独立证据
    后一步依赖前一步结论 串行 传递结构化结果
    多个方案需要比较 扇出后汇总 统一评分维度
    需要共同修改状态 单写者或显式锁 冲突检测和回滚

    q-code 中追踪团队边界

    团队切面 代码位置 要核对的协作边界
    1. Agent 注册 src/agents/registry.ts 子 Agent 能看到哪些能力
    2. 团队辅助 src/agents/team-helpers.ts 依赖和生命周期如何组织
    3. 团队上下文 src/agents/team-context.ts 主子上下文如何隔离
    4. Mailbox src/agents/teammate-mailbox.ts 消息是否带任务 id 和来源

    失败反例:并行所有事情再让主 Agent 猜谁对

    并行能缩短等待,但会增加重复、合并和限流成本。没有任务契约时,多个子 Agent 可能读取同一份旧文件、给出互相矛盾的结论,主 Agent 最后按语气而不是按证据选择结果。

    修复时给每个结果附上来源、时间、工具轨迹和未完成原因。合并器只消费结构化字段,不能把长篇自然语言直接拼进 Prompt 再让模型自行裁判。

    做一次依赖图实验

    练习:把一个真实任务拆成至少六个子任务,画出依赖关系,分别计算完全串行、只读并行和错误并行的总等待时间与合并成本。给两个子任务故意分配重叠文件,确认系统能在写入前发现冲突,而不是靠最后一次覆盖来解决。

    模式一:Fan-out / Fan-in 适合独立证据搜集

    研究“是否升级依赖”时,可以分别检查 API 变化、安全公告、性能基准和项目兼容性。四项共享依赖版本与目标环境,彼此不需要中间结果,适合一次扇出。

    fan-in 不是把四段 finalText 拼起来。每个结果使用统一字段:结论、证据、适用范围、风险、未检查项。合并阶段先去重来源、识别冲突,再形成决策。

    失败策略按证据覆盖决定。安全公告任务失败,升级结论缺关键维度,应阻断;性能基准失败,若本次目标只修安全漏洞,可以标 deferred。任务合同在扇出前就写好 required/optional。

    模式二:流水线用于产物逐步变形

    “收集需求 → 设计方案 → 实现 → 测试 → 评审”有强依赖,更像流水线。后一个角色消费前一个结构化 artifact,而不是重新读全部会话。

    每段定义输入 schema 与验收。设计阶段输出 plan v2;实现只接受已批准 hash;测试消费 commit/worktree;评审消费 diff、test report 和计划。上游变化使下游 artifact stale,不能继续沿用旧测试报告。

    流水线的风险是错误逐级放大。每段加入 cheap validation:计划有目标与 non-goals,patch 只触碰允许路径,测试报告关联当前 commit。发现不一致就回到具体阶段,不必整条从头跑。

    模式三:候选竞争适合探索,不适合事实表决

    两个 Agent 分别提出缓存设计,再由评审者按延迟、复杂度、故障恢复和成本比较,适合开放方案。它用额外 token 换思路多样性。

    候选必须独立生成,若共享前一个答案,第二个容易只做改写。评审维度在生成前固定,避免看到喜欢的方案后改规则。涉及事实的部分仍回到工具证据,不用三个模型投票决定测试是否通过。

    竞争数量通常 2 到 3 个足够。十个相似候选会增加合并噪声;只有一个明显方案时,直接实现再评审更省。

    模式四:生成者 / 评审者形成质量闸门

    一个 Agent 写迁移方案,另一个只读评审遗漏、风险和验证,不直接修改原文。生成者收到结构化 finding 后修订,最多迭代若干轮。

    要防止“礼貌循环”:评审者每轮都提无关小建议,生成者不断润色。doneWhen 写硬条件,评审 finding 有 severity,只有 blocker/major 触发重做;连续两轮没有新证据就结束。

    评审者的工具可以更窄,只读代码、测试和 diff,不拥有发布权限。角色隔离让它能独立挑战,而不是顺手把问题改掉导致证据消失。

    Map-reduce 只适合可聚合的大集合

    扫描 500 个日志文件可以分片 map:每个 Agent 提取错误码、时间范围和样本;reduce 按错误码聚合频次与代表证据。若每个 map 输出自由散文,reduce 会很痛苦,必须预先定义 schema。

    涉及跨文件顺序的故障不适合随意分片。一个 trace 被切到两个分区时,需要共享 correlationId 和时间边界,reduce 才能重新连接。Map-reduce 提高吞吐,但会牺牲全局上下文,要确认任务可分解。

    依赖图先求关键路径

    任务 A 4 分钟、B 5 分钟、C 2 分钟都无依赖,D 必须等 A/B 后运行 3 分钟,E 等 C 后运行 8 分钟。无限槽位下关键路径是 C→E 共 10 分钟,不是最长单任务 8 分钟。

    有限槽位调度应优先关键路径和阻断任务,而非按创建顺序塞满。预计耗时只是估算,运行中根据实际状态调整;已经接近完成的任务不必为了新高优先级频繁取消。

    A(4) ─┐
          ├─ D(3)
    B(5) ─┘
    C(2) ─── E(8)   <- critical path 10
    

    这类小图用普通 DAG 调度足够,不需要让模型每秒重排。

    资源标签防止“逻辑独立,现实冲突”

    两个任务改不同文件,却都运行同一数据库迁移;三个浏览器测试都占 3000 端口。依赖图看似独立,资源并不独立。

    WorkItem 声明模型配额、cwd/worktree、文件写范围、端口、数据库、外部 API 和 GPU 等标签。调度器对排他资源加锁,对限流资源用 semaphore,对只读共享资源允许并行。

    锁有 owner taskId 和 lease,任务崩溃后可回收;但外部副作用不能仅靠本地锁保证幂等,仍需要服务端 key 或状态查询。

    动态并行优先用于只读探索

    主 Agent 扫描任务图,选择所有依赖已完成、工具只读、结果可独立合并的节点,并在可用槽位内并发。写节点默认串行,或按不重叠 worktree/资源显式放行。

    只读不是绝对无副作用:外部查询可能收费和限流,Shell 测试会写缓存。工具 capability 与资源标签共同决定,不靠任务标题猜。

    调度每次选择留下 reason:ready、blocked-by、resource-busy、budget-limited。任务长时间 pending 时可以解释,而不是像线程池黑盒。

    合并器必须处理冲突和缺口

    结构化结果也会冲突:A 说函数无调用,B 找到动态 import。合并器保留 claim 与 evidence,不用最后写入覆盖;可以创建 verify 节点,专门跑 grep 或测试消解。

    结果缺少 required field 时退回子任务补充,artifact 不可读时标 incomplete。不要让主 Agent 根据 preview 猜完整报告。

    合并输出说明共识、冲突、未覆盖范围和最终决策者。对于安全 blocker,证据优先;对于风格偏好,可以由主 Agent 统一。

    失败隔离从任务边界开始

    一个候选方案 Agent 超时,不应让其他候选 artifact 丢失;流水线关键上游失败,则下游不应启动。编排器区分 task failure、infrastructure failure、cancelled 和 unknown。

    重试只重跑幂等、输入未变化的节点。若上游 artifact hash 变化,旧失败计数和缓存结果都失效;外部写节点状态未知时先 inspect。

    断路器可以在 provider 连续 429 时暂停新模型任务,让运行中的工具收尾。退避期间主 Agent仍可整合已有证据。

    预算分配要给收束阶段留份额

    把总 token 全分给 fan-out,最后没有空间合并,是常见错误。先预留 fan-in、验证和最终回答预算,再把剩余按任务价值分给子 Agent。子任务达到局部 maxTurns 返回现有证据,不继续借用全局预算。

    成本统计包含重复读取和被丢弃候选。并行缩短墙钟时间,往往增加总 token;是否值得由任务时效和风险决定。

    选择模式的一张现场判断

    问四件事:输入能否稳定切分;子结果能否结构化合并;失败是否可隔离;真实资源是否冲突。四项都好,fan-out;有强顺序,流水线;需要多方案,候选竞争;已有方案需挑战,生成者/评审者;都不满足,单 Agent 可能最好。

    模式不是团队角色名称。一个任务可先 fan-out 搜证据,再流水线实现与测试,最后 reviewer 闸门;每一段只用它解决的依赖关系。

    用发布评审做一次组合编排

    范围、文档、安全三个只读节点 fan-out;安全结果为 blocker 时先修复,设计与实现走流水线;两个实现方案确有取舍时才竞争;最终 reviewer 只读 diff、测试与计划,决定是否达到发布门槛。

    记录关键路径、槽位利用、总 token、重复工作和合并时间。若并行版只快 30 秒却多一倍成本,下次缩小 fan-out;若安全检查常成为关键路径,可以提前预取或给它优先槽位。编排由数据迭代,不由“Agent 越多越先进”的想象驱动。

    本文目录
    本文目录