欢迎光临
我们一直在努力

多Agent协作的3种编排模式:串行、并行与DAG

为什么你需要关心Agent编排

如果你只做过单Agent的demo,你会发现一个规律:只要业务稍微复杂一点,一个Agent就扛不住了。

比如一个"智能报工系统",客户需求是这样的:

  • 解析客户发来的工单(可能是邮件、微信消息、PDF附件)
  • 根据工单内容查询产能系统,生成排产方案
  • 根据排产方案生成质检标准
  • 把所有结果汇总成一份报告,推送给项目经理
  • 这里面涉及4个完全不同的能力:文档理解、产能计算、规则匹配、报告生成。你不可能让一个Agent同时精通这4件事——上下文太长,Prompt太复杂,Token成本爆炸。

    正确的做法是拆成多个Agent,每个Agent专注一件事,然后用编排层把它们串起来。

    但怎么串?这里有3种基本模式,每种都有明确的适用场景。


    模式一:串行编排(Sequential)

    核心逻辑

    Agent A的输出,作为Agent B的输入。像流水线一样,一个接一个处理。

    [输入] → Agent A → Agent B → Agent C → [输出]

    适用场景

    业务流程是线性的,每一步的输出都是下一步的输入,且各步骤之间强依赖。

    典型案例:

    • 文档解析 → 信息提取 → 数据入库
    • 需求分析 → 代码生成 → 代码审查
    • 客户提问 → 意图识别 → 知识检索 → 回答生成

    代码结构

    class SequentialOrchestrator:
    """串行编排器:按顺序执行Agent链"""

    def __init__(self, agents: list[Agent]):
    self.agents = agents

    async def run(self, input_data: dict) > dict:
    context = input_data

    for i, agent in enumerate(self.agents):
    print(f"[Step {i+1}/{len(self.agents)}] 执行: {agent.name}")
    context = await agent.execute(context)

    # 可选:每步结果落盘,方便排查
    await self.save_checkpoint(i, context)

    return context

    工程取舍

    优点:

    • 实现简单,调试容易(每一步的输入输出都能单独看)
    • 适合长流程、强依赖的业务
    • 每一步可以单独替换和升级

    缺点:

    • 总延迟 = 各步骤延迟之和(串行 = 慢)
    • 一个环节出错,整条链路中断
    • 不适合有并行需求的场景

    踩坑点

    串行编排最常见的坑是上下文膨胀。每经过一个Agent,context里就多一堆中间结果。到第三个Agent时,context可能已经有几万字,Token成本飙升。

    解法: 每个Agent只传递下一步需要的字段,不要把全量context往下透传。设计一个明确的output_schema,每个Agent只输出下游需要的结构化数据。

    class DocumentParserAgent:
    """只输出下游需要的字段,而不是把整个文档内容往透传"""

    output_schema = {
    "customer_name": str,
    "order_id": str,
    "products": list[dict], # [{name, quantity, spec}]
    "delivery_date": str,
    }


    模式二:并行编排(Parallel)

    核心逻辑

    多个Agent同时执行,各自处理不同的任务,最后汇总结果。

    ┌→ Agent A →┐
    [输入] ──┤→ Agent B → ├── [汇总] → [输出]
    └→ Agent C →┘

    适用场景

    任务之间相互独立,没有依赖关系,可以同时执行。

    典型案例:

    • 用户同时问了一个包含产品咨询、价格查询、售后政策的复合问题 → 3个Agent并行回答,最后合并
    • 竞品监控:同时抓取5个平台的价格数据
    • 报告生成:并行生成摘要、数据分析、风险评估三个章节

    代码结构

    import asyncio

    class ParallelOrchestrator:
    """并行编排器:同时执行多个Agent,汇总结果"""

    def __init__(self, agents: list[Agent], aggregator: Agent | None = None):
    self.agents = agents
    self.aggregator = aggregator # 可选的汇总Agent

    async def run(self, input_data: dict) > dict:
    # 所有Agent同时启动
    tasks = [agent.execute(input_data) for agent in self.agents]
    results = await asyncio.gather(*tasks, return_exceptions=True)

    # 处理异常(一个Agent挂了不影响其他)
    valid_results = []
    for i, result in enumerate(results):
    if isinstance(result, Exception):
    print(f"[ERROR] {self.agents[i].name} 执行失败: {result}")
    else:
    valid_results.append(result)

    # 可选:用汇总Agent合并结果
    if self.aggregator:
    return await self.aggregator.execute({
    "partial_results": valid_results,
    "input": input_data
    })

    return {"results": valid_results}

    工程取舍

    优点:

    • 总延迟 = 最慢那个Agent的延迟(而不是所有之和)
    • 容错性好:一个Agent挂了,其他Agent的结果仍然可用
    • 适合需要同时获取多维度信息的场景

    缺点:

    • 不适合有依赖关系的任务
    • 汇总逻辑可能复杂(多个Agent的结果格式、粒度可能不一致)
    • 并发高时对API调用配额有压力

    踩坑点

    并行编排最常见的坑是汇总质量差。把多个Agent的回答简单拼接,往往会出现重复、矛盾、格式混乱。

    解法: 加一个专门的"汇总Agent",用LLM做一次合并去重和冲突消解。这个汇总Agent的Prompt要明确告诉它如何处理矛盾:

    AGGREGATOR_PROMPT = """
    你是一个结果汇总助手。你会收到多个Agent的回答,请执行以下操作:

    1. 去重:多个Agent回答了同一个问题,只保留最准确的那个
    2. 消解冲突:如果Agent A和Agent B的答案矛盾,优先采信带有数据/引用来源的那个
    3. 格式化:输出统一的Markdown格式

    注意:不要遗漏任何有价值的信息,但也不要简单拼接。
    """


    模式三:DAG编排(有向无环图)

    核心逻辑

    Agent之间的依赖关系不是简单的线性或并行,而是一个有向无环图(DAG)——有些步骤可以并行,有些步骤必须等前置步骤完成后才能开始。

    ┌→ Agent A ──────→┐
    [输入] ──┤ ├── Agent D → [输出]
    └→ Agent B → Agent C ┘

    上面这个DAG里:

    • Agent A 和 Agent B 可以并行(无依赖)
    • Agent C 必须等 Agent B 完成
    • Agent D 必须等 Agent A 和 Agent C 都完成

    适用场景

    业务流程是混合依赖的——部分步骤可以并行,部分步骤有严格的先后顺序。

    这是生产环境中最常见的模式,因为真实业务几乎不可能只有纯线性或纯并行。

    典型案例:

    • 智能客服系统:
      • 并行:意图识别 + 用户身份验证
      • 串行:意图识别完成后 → 路由到对应Agent
      • 汇总:回答生成 + 工单创建 → 合并推送
    • 供应链决策:
      • 并行:需求预测 + 库存查询 + 供应商报价采集
      • 依赖:采购方案 = f(需求预测, 库存, 报价)
      • 最终:生成采购报告

    代码结构

    DAG编排的核心是一个任务调度器,它根据依赖关系决定哪些Agent可以并行执行、哪些需要等待。

    from collections import defaultdict
    import asyncio

    class DAGOrchestrator:
    """DAG编排器:按依赖关系调度Agent执行"""

    def __init__(self):
    self.agents = {} # name -> Agent
    self.dependencies = defaultdict(set) # name -> {前置依赖}
    self.results = {} # name -> result

    def add_agent(self, name: str, agent: Agent, depends_on: list[str] = None):
    self.agents[name] = agent
    if depends_on:
    self.dependencies[name] = set(depends_on)

    def _get_ready_agentss(self) > list[str]:
    """获取所有前置依赖已完成的Agent"""
    ready = []
    for name in self.agents:
    if name in self.results:
    continue # 已执行
    if self.dependencies[name].issubset(set(self.results.keys())):
    ready.append(name)
    return ready

    async def run(self, input_data: dict) > dict:
    self.results = {"__input__": input_data}

    while len(self.results) < len(self.agents) + 1:
    ready = self._get_ready_agents()

    if not ready:
    raise RuntimeError("DAG中存在循环依赖或死锁")

    # 所有就绪的Agent并行执行
    tasks = []
    for name in ready:
    # 收集该Agent需要的前置结果
    dep_results = {
    dep: self.results[dep]
    for dep in self.dependencies[name]
    }
    tasks.append(self._execute_agent(name, dep_results, input_data))

    await asyncio.gather(*tasks)

    return self.results

    async def _execute_agent(self, name: str, dep_results: dict, input_data: dict):
    agent = self.agents[name]
    result = await agent.execute({
    "input": input_data,
    "dependencies": dep_results
    })
    self.results[name] = result

    使用示例

    用上面的"智能报工系统"举例:

    dag = DAGOrchestrator()

    # Step 1: 文档解析(无依赖,最先执行)
    dag.add_agent("doc_parser", DocumentParserAgent())

    # Step 2: 产能查询(无依赖,和文档解析并行)
    dag.add_agent("capacity_query", CapacityQueryAgent())

    # Step 3: 排产方案(依赖文档解析 + 产能查询)
    dag.add_agent("scheduling", SchedulingAgent(), depends_on=["doc_parser", "capacity_query"])

    # Step 4: 质检标准(依赖排产方案)
    dag.add_agent("quality_check", QualityCheckAgent(), depends_on=["scheduling"])

    # Step 5: 报告生成(依赖排产方案 + 质检标准)
    dag.add_agent("report_gen", ReportGenAgent(), depends_on=["scheduling", "quality_check"])

    # 执行
    result = await dag.run(order_data)

    执行时序:

    时间轴 →
    doc_parser: ████████
    capacity_query: ████████
    ↓ 两者都完成
    scheduling: ████████████
    ↓ 完成
    quality_check: ████████
    ↓ scheduling + quality_check都完成
    report_gen: ██████████

    工程取舍

    优点:

    • 最灵活,能表达任意复杂的依赖关系
    • 最大化并行度(只要依赖满足就立即执行)
    • 生产环境最实用的编排模式

    缺点:

    • 实现复杂度高
    • 调试困难(需要可视化DAG图才能看清执行路径)
    • 状态管理复杂(需要处理部分完成、部分失败的情况)

    踩坑点

    坑1:循环依赖。 Agent A依赖B,B依赖C,C又依赖A——死锁。DAG编排器必须在注册时做拓扑排序,检测并拒绝循环依赖。

    坑2:错误传播。 一个Agent挂了,依赖它的所有下游Agent怎么办?需要设计明确的错误处理策略:

    • 跳过(下游Agent也跳过,标记为"因上游失败未执行")
    • 降级(下游Agent用默认值/缓存值继续执行)
    • 重试(上游Agent重试N次,超过后走降级)

    class ErrorStrategy:
    SKIP = "skip" # 跳过下游
    FALLBACK = "fallback" # 用默认值继续
    RETRY = "retry" # 重试上游

    坑3:结果传递的数据量。 DAG里的中间结果可能很大(比如一个Agent生成了10页的排产方案),全部传递给下游会导致内存和Token成本暴涨。解法是只传递下游需要的摘要,而不是全量结果。


    三种模式的选型指南

    维度串行并行DAG
    适用场景 线性流水线 独立子任务 混合依赖
    实现复杂度
    总延迟 各步骤之和 最慢步骤 关键路径长度
    容错性 差(一环断全断) 好(互不影响) 中(需策略)
    调试难度
    生产推荐度 简单流程可用 数据聚合场景 复杂业务首选

    一个经验法则: 如果你的Agent协作逻辑超过了5步,几乎一定需要DAG。因为真实业务流程很少有纯线性的,总会有"这两步可以同时做"或"这一步要等那两步都完成"的情况。


    关于编排框架的选择

    目前主流的Agent编排框架:

    • LangGraph:LangChain生态,基于图(Graph)的编排,原生支持DAG。适合已经用LangChain的团队。
    • CrewAI:基于角色的多Agent框架,内置串行/并行/层级三种模式。上手快,但复杂场景不够灵活。
    • AutoGen(微软):基于对话的多Agent框架,Agent之间通过消息传递协作。适合需要Agent间"讨论"的场景。
    • 自研编排层:如果业务足够复杂,建议自己写编排层。上面的代码就是一个最小可用的骨架,生产环境加上持久化、监控、重试机制即可。

    框架不是银弹。理解底层逻辑,才能在框架出问题时知道怎么修。


    结语

    多Agent协作不是"把几个Agent拼在一起"那么简单。编排模式的选择,本质上是在延迟、容错、复杂度三者之间做取舍。

    串行最简单但最慢,并行最快但只适合独立任务,DAG最灵活但实现最复杂。生产环境中,大部分业务最终都会走向DAG——因为真实世界的业务流程,本来就不是线性的。

    理解了这三种模式,再去选框架、做设计,才不会迷路。


    栈上月明(ZSoftYM)是一家专注于AI智能体开发与多Agent系统架构的西安技术团队。


    赞(0)
    未经允许不得转载:171主机测评 » 多Agent协作的3种编排模式:串行、并行与DAG
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址