WRITING / 2026.09.23

复杂任务如何编排:工作流、单 Agent 与多 Agent 的取舍

用同一个研发排障任务比较固定工作流、单 Agent 动态调用与多 Agent 分工,说明串并行、路由和委派的适用条件。通过 SQLite 检查点演示中断恢复、身份隔离及绑定方案的人工确认,并分析重试、重复副作用和协调成本。

查询一个指标只需要一次工具调用,完成一次排障却往往需要多个步骤。系统要读取状态、核对变更、检索手册,再形成建议。如果查询超时,应该从头开始还是继续?如果不同角色给出冲突意见,谁负责收敛?这些问题属于任务编排,而不只是提示词设计。

本文继续使用虚构的结算服务超时场景。案例中有真实的本地状态持久化与恢复逻辑,但没有多个模型自主协作,也不会实施生产变更。多角色部分用于展示输入输出边界,不能据此宣称多 Agent 比单 Agent 更准确、更快或更便宜。

先把任务拆成能够验收的阶段

目标是输出有依据、可供人工评审的排障方案。围绕这个目标,可以拆出资料检索、状态检查和方案整理三个阶段。每个阶段都应有明确输入、结果与完成条件,否则“已经做过了”只是一句难以验证的描述。

资料检索的结果是适用的文档编号,而不是“读过知识库”。状态检查的结果是带时间和版本的快照,而不是“服务不正常”。方案整理的结果是候选动作、目标服务、相关变更与证据引用,而不是一段无法执行或评审的泛泛建议。

阶段划分不等于必须增加服务数量。三个阶段可以在同一个进程中完成,先通过清晰的数据结构和函数边界获得可观察性。只有并发规模、资源隔离或独立扩缩容提出需求时,再决定是否拆成多个服务或任务队列。

验收条件也应允许停在中间。缺少适用文档时,可以结束为证据不足;查询超时时,可以进入可恢复错误;需要变更时,可以等待人工决定。只有成功一种结束方式的流程,往往会迫使系统把不完整结果包装成完成。

三种执行方式解决不同问题

固定工作流由代码定义阶段顺序,适合任务结构稳定的场景。本例中的标准诊断可以按检索、检查、方案顺序执行。每个阶段都可以调用模型,但模型不能任意跳过必需的检查。业务需要稳定的审计顺序时,这种方式容易建立明确约束。

单 Agent 动态调用适合路径取决于现场证据的任务。状态显示缓存命中率下降时,它可能转向缓存手册;出现 DNS 错误时,则查询解析资料。应用控制工具和预算,模型在边界内提出下一步。动态选择提高适应性,同时也扩大需要评测的路径空间。

多 Agent 将子任务分配给不同角色,例如观察者读取指标,检索者寻找资料,审核者核对建议。它可能帮助分离上下文或并行处理独立任务,但也引入交接、重复查询和冲突处理。角色名称不同,并不自动意味着能力互补。

方式 路径的主要决定者 需要重点验证
固定工作流 应用的状态转换 节点契约与分支覆盖
单 Agent 模型的动态决策 工具选择与停止策略
多 Agent 调度与各角色共同参与 委派边界、信息交接与收敛

对于一个只有几个稳定步骤的小任务,先建立固定流程基线有实际价值。它提供可比较的完成时间、调用数和失败类型。如果引入动态决策后没有改善关键任务表现,就应该重新检查增加复杂度的理由,而不是因为架构图更丰富就保留它。

串行与并行取决于信息依赖

状态查询与变更记录读取可能互相独立,可以并行执行。形成最终建议则需要综合两者,必须等待相关结果。判断能否并行,应查看数据依赖和副作用,而不是只看两个函数是否能够放进线程池。

并行读取还需要处理不一致时间。两个工具分别查询不同时间窗口,可能产生看似矛盾的结果。例如一份快照在变更前,另一份在变更后。返回值应保留观测时间,聚合阶段明确比较条件,不能把同时返回当成同时发生。

并行写入更需要谨慎。如果两个角色都决定创建相同事故工单,即使各自调用都成功,整体任务仍然可能错误。需要共享业务标识、幂等约束或单一的写入负责人,避免让协调问题扩散到下游系统。

本例没有用本地函数的并发耗时证明多 Agent 性能。角色演示执行两次只读工具调用,并输出它们的结果与建议检查。真实并行收益还要扣除调度、模型调用和结果汇总开销,应在相同任务集与资源预算下测量。

路由与任务委派应留下证据

路由决定请求进入哪一条路径,例如先识别是缓存问题还是连接问题。若路由来自模型,应保留选择的目标和依据,并允许无法分类的结果进入通用诊断。强制把所有请求分到已知类别,容易让未知故障被送入不适用流程。

委派除了子任务描述,还需要明确输入范围、可用工具、结果格式与截止时间。“帮我查清楚”缺少边界,很容易让不同角色重复工作。更清楚的委派是“读取指定服务在该时间窗口的变更,返回记录编号与配置差异,不提出生产操作”。

父任务应检查子任务是否真正完成。如果子任务只返回一段流畅总结,却没有所需证据编号,父任务不能直接把它当成合格结果。委派输出仍是需要校验的输入,而不是因为来自另一个 Agent 就具有更高权威。

角色之间也不要通过无限转发会话来协调。每次交接附上整个历史,会增加上下文并放大不可信内容的传播范围。明确的任务摘要、结果引用和未决问题,通常比一个越来越长的聊天记录更容易保持职责边界。

把任务状态显式保存下来

示例用 SQLite 的 runs 表保存任务编号、所属用户和状态。状态包含已完成阶段的结果、各阶段尝试次数以及当前阶段。任务恢复时,先读取已有记录并检查用户,再跳过已经完成的阶段。

任务编排状态图:检索、检查、方案逐步完成,失败进入可恢复状态,恢复复用检查点,方案确认后仅结束模拟

这不是一个通用工作流引擎,而是用少量代码展示恢复语义。它没有任务租约、分布式锁或复杂调度器。读者应首先观察一个阶段什么时候被认定为完成,以及该事实保存在哪里,再决定需要引入哪些运行基础设施。

from demo import database, workflow

with database("workflow.sqlite") as db:
    first = workflow(db, "incident-42", "alice", fail_at="inspect")
    second = workflow(db, "incident-42", "alice")
    print(first["phase"])
    print(second["attempts"])

第一次调用完成资料检索,在状态检查阶段注入失败。第二次调用从记录中恢复,不重新检索,只继续检查与方案整理。输出中的尝试次数分别为检索一次、检查两次、方案一次。测试还会关闭数据库连接后重新打开,确认恢复依赖持久化记录而非内存对象。

这个结果能证明当前示例重用了已完成检索,但不能证明任何外部工具都能精确执行一次。读取结果持久化与外部副作用之间,仍然存在需要单独处理的失败窗口。

重试为什么不等于重新执行整个任务

重试应该针对明确的失败单元。检查阶段超时,不必重跑已经成功的资料读取。若从头运行,不但增加开销,还可能取得不同时间的证据,导致最终方案混用两次现场。恢复时应明确哪些结果可以复用,哪些需要刷新。

同时,复用也不能无限持续。任务暂停几小时后,原来的状态快照可能已经失效。生产检查点应记录结果时间与适用条件,恢复时重新判断。示例使用固定数据,所以省略动态失效;文章不能把这个简化当成普适恢复策略。

最危险的窗口发生在写操作已经成功、检查点尚未保存的时候。进程此时退出,恢复后可能再次提交写入。仅靠“保存已完成节点”无法避免这个问题。需要让外部写操作具有业务幂等键,或根据实际系统使用事务消息、查询收据等机制。

如果工具超时导致结果未知,应区分“确定失败”与“不知道是否成功”。后者适合先查询业务结果,而不是马上重试。第二篇的排障记录示例使用用户与请求编号组成唯一键,就是为了让同一业务意图能够被识别。

补偿也不同于回滚数据库事务。发送了一条通知或触发了一个外部任务,可能无法真正撤回。工作流应事先定义可补偿的动作及其业务含义,不能只因为代码有一个逆向函数,就声称整个任务可以恢复到从未发生的状态。

人工介入必须绑定具体方案

排障助手可以提出检查或回滚建议,但高影响操作需要独立的授权边界。一个可靠的确认过程应让人知道目标对象、拟执行动作、参数、影响范围和证据,而不是只出现一个笼统的“继续”按钮。

示例将方案结构序列化后计算摘要,把等待确认的状态与这个摘要绑定。提交错误摘要会得到 APPROVAL_MISMATCH,未提交摘要则保持等待状态。这样可以演示为什么确认不能与任意后续方案混用。

with database() as db:
    state = workflow(db, "incident-42", "alice")
    approved = workflow(
        db, "incident-42", "alice",
        approve_digest=state["approval_digest"],
    )
    assert approved["phase"] == "approved_simulation"

这个代码片段仅展示摘要绑定机制,测试代码直接提供摘要,不代表真实人工审批。生产系统必须从受信任的用户操作取得授权,校验审批人身份、有效期和方案版本。知道摘要不等于拥有权限,摘要只能帮助识别被确认的是哪一个方案。

本例在确认后结束为 approved_simulation,不会重启服务、改配置或执行 Shell。真实执行还需要单独设计受控工具与结果校验。把“方案已确认”和“变更已成功”保存为不同状态,能够避免产品误把审批通过显示成事故已经解决。

多角色分工的价值与成本

下载包里的 role_collaboration 让观察者提供状态,让检索者提供文档,再由一个确定性检查函数检查建议类别是否冲突。角色决策来源被标记为固定样例,输出保留各自结果。读者可以看到交接结构,但这里没有多模型推理。

实际多 Agent 系统可能产生不同解释。审核角色不能只根据文字表达是否完整来投票,应核对引用、目标范围和关键约束。几个角色引用同一份错误文档,并不等于获得多个独立证据;一致意见可能只是共享输入造成的重复。

成本也不只体现为调用次数。每个角色需要自己的上下文,交接过程可能复制材料,父任务还要汇总。增加角色可能减少某一轮的上下文,却增加总体 token 和等待时间。因此,比较时应同时观察任务成功、资源消耗和恢复复杂度。

最适合委派的子任务通常有可分离的输入与可检查的输出。需要持续共享大量隐含上下文的任务,拆开后反而容易产生信息损失。决定是否采用多 Agent 时,可以先尝试用明确的普通函数边界拆分;如果连函数契约都写不清,角色提示词通常也难以解决根本问题。

并发恢复还缺少哪些条件

当前 SQLite 示例按单个调用方顺序执行。生产环境中,两个工作节点可能同时恢复同一个任务,都会看见某阶段尚未完成,随后重复执行。需要用任务版本、租约或原子领取机制控制执行权,并处理持有者失联后的接管。

租约过期也不代表旧执行者已经停止。慢请求可能在新的执行者接管后返回,因此写入时还需要检查版本或隔离令牌,防止旧结果覆盖新状态。这个问题与 Agent 无关,但模型调用较慢、任务持续时间较长,会让它更常见。

任务数据还需要可追溯的变更记录。什么时候失败、由谁恢复、使用了哪个版本的工具、是否重新取得授权,都是诊断的重要信息。保存最后一个状态有利于继续执行,保存状态转换历史则有利于解释为什么变成这样,两者服务于不同需求。

Java 后端怎样选择实现层次

对于短小、确定的任务,可以先使用显式枚举状态与数据库事务;对于需要跨进程执行的任务,再结合队列、调度和幂等服务。已有工作流系统可以承接模型节点,但要为模型输出增加契约校验和预算控制,不能把自由文本直接当作下一节点的可信指令。

如果采用 Agent 框架,重点核对其检查点保存内容、恢复入口、并发控制和人工介入语义。框架提供一个恢复函数,不等于已经解决外部副作用的重复执行。应通过具体故障注入验证,而不是从功能名称推导可靠性保证。

本系列的恢复测试覆盖中断后复用结果、方案摘要匹配、未确认时保持等待和其他用户不能恢复任务。真实上线还要增加进程强制退出、并发抢占、外部写入结果未知等场景。这些验证决定系统在不顺利的时候能否保持正确,而不仅是在演示时完成一次任务。

在方案评审中保留未决问题

一份可评审方案除了动作,还应列出尚未确认的假设。本例将配置变更与等待指标关联起来,但没有证明它是唯一原因。人工评审应能看见流量、下游耗时等仍需核对的因素,避免因为方案结构完整就误认为调查已经结束。

如果审核者要求补充证据,任务可以回到指定的读取阶段,并使旧方案的确认失效。新方案应产生新的版本或摘要,再由人决定。这样,补查、修订和确认形成明确的状态变化,既不需要从头丢弃全部材料,也不会让一次历史确认授权无限变化的后续操作。

参考资料

AI Agent 工程实践:从原理到可靠运行

  1. AI Agent 是如何工作的:从一次模型调用到任务执行闭环
  2. 让 Agent 真正执行任务:Tool Calling 与 MCP 实战
  3. 从知识库问答到 Agentic RAG:让 Agent 找到可靠答案
  4. Agent 的上下文与记忆:如何记住有用信息
  5. 复杂任务如何编排:工作流、单 Agent 与多 Agent 的取舍 · 当前文章
  6. Agent 从 Demo 到上线:评测、可观测性与安全边界

按时间浏览