跳到主要内容

DAG 优先的智能体编排:为什么线性链在大规模场景下会失效

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数多智能体系统最初都是以链式结构开始的。智能体 A 调用智能体 B,B 调用 C,C 调用 D。这种模式在演示中运行良好,在处理简单任务的五个智能体中也运行良好。但当你增加到第六个、第七个智能体时,原本 8 秒就能运行完的流水线开始需要 40 秒。你在第三步增加了一个重试机制,结果第三步的失败会无声地级联,导致第六步出现状态损坏。你尝试添加一个并行分支,却发现你的框架从设计之初就无法支持。

问题不在于智能体的数量。问题在于执行模型。线性链式结构固有的缺陷在于它将本可以并行的工作串行化,且故障只能单向传播,这使得局部恢复在结构上变得不可能。解决方法并不是在现有系统之上增加更多的基础设施,而是从一开始就围绕有向无环图(DAG)重建执行模型。

线性链式结构究竟错在哪?

线性流水线感觉很自然,因为人类的工作流程通常是顺序的:研究,然后起草,接着审核,最后发布。但一旦任务包含独立的子组件,这种直觉就会失效。在研究型智能体的工作流中,从三个不同的 API 获取数据、并行运行两种不同的分析并合并结果,这本质上是一个 DAG。将其强行塞进一个链式结构会不必要地串行化所有操作。

这些病态问题会不断累积:

人为的串行化。 如果步骤 B 和 C 都依赖于 A,但互不依赖,线性链仍然会在运行 C 之前先运行 B。C 等待 B 完成的每一秒都是纯粹的浪费。在智能体工作流中,每个步骤都会调用 LLM,这会导致延迟迅速累积 —— 每个步骤可能需要 2-5 秒,五个独立步骤串行运行而非并行运行,会导致总时长无端增加 10-20 秒。

全有或全无的失败。 线性链只有单一的执行路径。当第四步失败时,下游的所有操作都会被阻塞。第二步和第三步的中间结果通常会被丢弃。恢复意味着从头开始(昂贵)或实现与特定链条形状紧密耦合的自定义检查点逻辑(脆弱)。这两种方式都无法扩展。

僵化的控制流。 线性链只能表达一种执行顺序。条件路由(例如,“如果检索失败,回退到网页搜索;如果网页搜索也失败,则上报给人工”)需要将复杂的条件逻辑封装在链条之外,而不是将其包含在执行模型内部。

DAGs 作为执行模型

有向无环图(DAG)为每个任务分配一个节点,为每个依赖关系分配一条边,并通过无环约束保证不存在循环等待。执行按拓扑排序进行:当一个节点的所有前序节点都完成后,该节点就进入待运行状态。独立节点并行运行。调度器不需要预先知道你具体工作流的形状,它只需评估就绪状态。

这一改变解锁了线性链无法干净利落提供的三项能力。

真正的并行性。 独立节点同时执行。如果你的研究流水线有一个“从数据库获取”节点和一个“搜索网页”节点,它们都是“综合发现”节点的前提条件,那么它们默认会并行运行。LLMCompiler 架构通过将工具调用表示为 DAG 节点并同步分发,展示了比顺序执行快 3.6 倍的速度提升。处理 1000–1500 字内容工作流的团队报告称,从顺序执行切换到并行节点执行后,端到端时间减少了 36–37%。

条件路由作为一等公民的边。 在 LangGraph 和类似的框架中,边带有基于共享工作流状态的谓词。节点的输出更新状态;调度器评估每条出边的条件并路由到适当的下游节点。这意味着失败模式、回退路径和上报分支都编码在图结构本身中,而不是散落在应用程序代码中。执行模型和控制流模型合二为一。

局部故障恢复。 当 DAG 中的某个节点失败时,调度器能精确知道哪些下游节点被阻塞(那些从失败节点可达的节点),而哪些没有(依赖链不经过失败节点的节点)。未受影响的分支继续执行。恢复只需处理失败的子图。通过检查点机制 —— 将完成的节点输出保存到持久存储中 —— 重启可以从最后一个成功的节点恢复,而不是从头开始。这不仅仅是理论上的优势,这在线性链中是结构上无法干净利落实现的。

三种打破线性链的模式

扇出/扇入模式(Fan-out/fan-in pattern)。 一个规划节点拆分任务,并将子任务作为独立节点发布。三个、五个或十个工作节点并行执行。一个综合节点收集它们的输出。这种模式涵盖了大多数研究和分析工作流。在线性链中这很别扭(你通常最终还是串行化了工作节点),但在 DAG 中却非常自然。

条件回退模式(Conditional fallback pattern)。 检索节点首先运行。如果成功,生成节点使用其输出。如果失败,则运行网页搜索节点,生成节点使用网页搜索的结果。来自检索节点的 DAG 边携带“成功”谓词;来自网页搜索的边携带“失败”谓词。执行模型处理路由,无需外部的 if/else。将此模式扩展到多级上报 —— 重试、重新规划、以不同方式拆分任务、最后人工介入 —— 只需添加节点和边,无需重写框架代码。

用于恢复的 Saga 模式(Saga pattern for recovery)。 涉及外部系统(写入数据库、发送电子邮件、调用计费 API)的长时智能体工作流需要优雅地处理部分完成的情况。Saga 模式为每个动作节点分配一个补偿事务:如果工作流在写入数据库后但在 API 调用前失败,补偿操作将回滚数据库写入。DAG 结构使 Saga 模式变得简单直接,因为依赖图明确编码了如果发生下游故障,哪些操作需要补偿。

基准测试数据的实际意义

来自基于 DAG 执行的性能提升是真实的,但需要合理解读。在图智能体架构中,3.6 倍的加速数字适用于那些很大一部分工作可以并行的工作流。对于高度顺序化的任务——即每一步都严格依赖上一步的完整输出——并行化没有帮助,而且 DAG 调度器的协调开销还会增加微小的成本。

实用规则:测量工作流的关键路径。关键路径是从开始到结束最长的一串严格顺序依赖关系。任何程度的并行化都无法将实际运行时间降低到关键路径时长总和之下。对于许多任务相互独立的工作流,关键路径远短于总工作量,DAG 执行可以显著降低延迟。而对于关键路径占据大部分工作的工作流,收益则较小。

在研究背景下的延迟改进(LAMaS 研究中关键路径减少了 38–46%)、内容工作流(端到端提速 36%)以及生产金融系统(通过并行化子任务提升了 80.9% 的吞吐量)都属于第一类:具有大量可并行组件的工作流。在迁移之前,请映射你工作流的实际依赖结构。如果大多数节点都依赖于其直接前驱节点,那么你拥有的是一个恰好用 DAG 表达的线性工作流,其开销可能并不值得。

团队在迁移时遇到的失败模式

将顺序状态并行化。 最常见的错误:在节点实际上共享可变状态时,将它们视为独立的。两个节点并行执行并向同一个状态键写入内容会产生竞态条件,从而产生非确定性的结果。DAG 框架能清晰地处理只读共享状态;但在写入时,它们需要显式的同步(或状态隔离)。在设计图形状之前,先设计你的状态架构(State Schema)。

依赖定义不足。 没有正确指定边的 DAG 会变成一个 Bug。如果节点 C 实际上依赖于节点 B 的输出,但你忽略了那条边,C 将在 B 完成之前执行,并基于过时或缺失的数据运行。由此导致的失败并不明显,因为 C 本身不会报错——它只是在错误的输入上运行。依赖规范需要像代码审查一样严谨。

过度并行化小型工作负载。 在小规模下,调度开销至关重要。为一个只有三个步骤的工作流启动线程池、管理状态转换并协调十个并发的 LLM 调用,所增加的延迟会超过并行化带来的收益。当你的工作流至少有几个真正独立的逻辑分支,并且运行频率足够高、值得优化时,采用 DAG 优先的架构才是合适的。

缺乏可观测性的复杂性。 与链式结构相比,DAG 使得执行流在视觉上更难追踪。线性链的执行日志是一个列表;而 DAG 的执行日志是一个图。如果没有一个能在展示节点输出和时长的同时,呈现执行图的框架,调试并行失败要比调试顺序失败困难得多。对于生产规模的应用,对可观测性的投入——特别是能够渲染每次 DAG 运行实际执行路径的工具——是必不可少的。

工具选择

LangGraph 是构建 LLM 原生智能体系统的团队的主流选择。它的状态管理模型、原生检查点支持以及对条件边的支持,使其非常适合上述智能体模式。它的局限性在于不提供生产级的调度基础设施——如 Cron 触发器、带有指数退避的重试策略、SLA 监控和运行历史。对于需要按计划运行或作为更广泛数据平台一部分的工作流,LangGraph 在工作流编排器(如 Prefect、Airflow、Dagster)内部运行效果最佳,由 LangGraph 处理 LLM 原生逻辑,而外部编排器处理运营层。

选择哪种外部编排器取决于运营需求。Airflow 是大型数据流水线的成熟选择,适用于具有广泛运营需求和大量现有投入的场景。Prefect 的混合执行模型将编排与执行分离,为需要动态智能体工作流且无需沉重基础设施的团队提供了灵活性。当你的智能体系统产生明确定义的数据产物且数据血缘(Lineage)至关重要时,Dagster 以资产为导向的模型非常适合。

要避免的反模式:当问题本身出在执行模型时,却在线性执行模型之上构建自定义编排层。这会产生用回调链和共享字典实现的 DAG 形状逻辑,它继承了两种方法的调试难题,却没能获得任何一种的优势。

起始点

如果你现有一个线性流水线,并想评估迁移到 DAG 是否值得:

  • 计算工作流中不依赖于紧邻前驱节点的节点数量。如果答案超过两个,说明你没有充分利用并行性。
  • 测量关键路径时长与总工作时长的比例。比例低于 0.5(意味着理论上你有一半以上的工作可以并行运行)表明 DAG 优化有很大的提升空间。
  • 找回上一次步骤 N 的失败需要从步骤 1 重新开始的情况。重新开始的成本就是没有局部恢复(Partial Recovery)的成本。如果这种情况经常发生且重启成本高昂(在 Token、延迟或金钱方面),那么局部恢复值得迁移带来的开销。

DAG 优先的编排并不是解决多智能体复杂性的万灵药。它解决不了智能体输出错误、定义清晰的任务边界或是并行 LLM 调用成本等问题。它解决的是执行模型的结构性问题——即那些将本应并发运行的工作串行化,并在本可以隔离时传播失败的模型。随着智能体系统的规模扩大,这些问题会不断累积,而处理这些问题的最佳时机是在规模扩大之前。

References:Let's stay in touch and Follow me for more thoughts and updates