← 工程跃迁 / 第 12 章
架构定位

工作流与编排

把复杂任务拆成可恢复的步骤,并通过明确合同协调执行单元。

如何拆解复杂任务,并协调步骤或多个 Agent?

概念校准

子 Agent

它是什么
被委派明确目标、输入、权限和返回合同的受限执行单元。
它不是什么
不等于简单并行;没有合同的角色拆分只会放大成本和冲突。

规划

它是什么
为多步骤、高成本或需要恢复的任务建立可检查、可更新的执行结构。
它不是什么
不是所有 Agent 的必备步骤,简单任务常常不需要独立规划器。

验证与评测

它是什么
用测试、Schema、引用、状态和验收规则检查单次结果,并用数据集衡量系统表现。
它不是什么
不等于让模型对自己的输出再给一次意见;验证必须落到外部可检查证据。

多 Agent 协作与任务编排

通过消息合同、共享状态、冲突处理和成本评测判断何时拆分角色。

本章只做一件事加入研究、实现和评审角色,并用对照实验说明是否值得。
开始前,Forge 已经具备已完成 Module 11 的“Planner/Executor 与 Agentic 工作流”,其通过验收的 solution 是本章起点。
完成后,Forge 将能够增加研究、实现和评审角色,并证明何时多 Agent 不值得使用。
卡住时的最小恢复点退回单 Agent 基线,逐个加入角色并记录新增成本。
下载本章实验仓库Python 3.12 · pytest · Pydantic · SQLite · deterministic Mock
阶段能力链
01用明确目标、输入、权限和输出 Schema 定义受限角色02使用可关联、可验证的消息封装 Agent 间请求和结果03通过乐观并发控制防止 Agent 静默覆盖彼此的状态更新04对互斥提案应用确定性安全优先规则,并保留决议理由05按能力、权限和当前负载选择最小权限 Agent,无法安全委派时明确拒绝06根据必需角色和最小成功数判断任务可继续、降级或失败07组合最小权限路由、消息关联、共享状态和部分失败策略,生成可审计协作结果
每一步只增加一种可验证能力;后一步建立在前一步已经通过的代码和测试上。

概念校准\n\n在开始操作代码前,先把本章涉及的概念放回正确的工程边界。\n\n
\n\n### 子 Agent sub-agent\n\n- 它是什么: 被委派明确目标、输入、权限、预算和返回合同的受限执行单元,完成后把可验证结果交还协调者。\n- 它不是什么: 它不等于简单并行,也不是为每个角色复制完整上下文;没有边界和返回合同的角色拆分会增加冲突与成本。\n- 与相邻概念的关系: Planning 决定可委派节点,Harness 限制权限与预算,版本化合同交换状态,Supervisor 处理冲突和部分失败。\n- 在 HeatStack Forge 中的位置: Forge 将研究、实现和评审角色限制在不同工具与目录中,每个角色返回结构化证据,协调者验证后才接受。\n- 典型误用与修正: 误用是同时启动多个角色并期待自然协作。修正方法是先定义任务独立性、消息合同与冲突策略,再比较单 Agent 基线。\n\n### 规划 planning\n\n- 它是什么: 为多步骤、高成本或需要恢复的任务建立可检查、可更新的执行结构,明确依赖、预算、状态和重新规划条件。\n- 它不是什么: 它不是所有 Agent 的必备阶段;简单低风险任务可能直接执行更快、更可靠,独立规划器必须证明价值。\n- 与相邻概念的关系: Agent Loop 可直接选择下一动作或执行计划节点;Verification 决定节点完成,State 与 Checkpoint 支持局部重试。\n- 在 HeatStack Forge 中的位置: Forge 将复杂请求转成带依赖、预算、验收和恢复点的任务图,执行器只领取可运行节点,失败后按证据局部重新规划。\n- 典型误用与修正: 误用是生成很长的静态计划并严格照做。修正方法是保持计划最小、状态可验证,并允许输入或工具变化时局部更新。\n\n### 验证与评测 evaluation\n\n- 它是什么: Verification 用测试、Schema、引用、状态或验收规则检查一次结果;Evaluation 用数据集和指标衡量多次运行表现。\n- 它不是什么: 它不是让模型对自己的回答再给一次意见;通过与否必须落到外部可检查证据。\n- 与相邻概念的关系: Agent Loop 依赖 Verification 决定停止,RAG 需要分层评测,Planning 需要节点验收,Observability 提供运行记录。\n- 在 HeatStack Forge 中的位置: Forge 为单次运行保存合同测试与 evidence,再用固定场景集比较质量、延迟、成本、权限和恢复能力。\n- 典型误用与修正: 误用是把一个综合分数当成全部结论。修正方法是定义分层指标、失败样本和版本基线,并结合产品风险评审。\n\n

STAGE 01

用明确目标、输入、权限和输出 Schema 定义受限角色

先看一个具体问题

委派时权限失控

在 Module 11 的 Planner/Executor 工作流中,执行器可以执行任意动作,没有按角色限制权限。

现在要引入多 Agent 角色,但直接委派时可能把超出角色能力的权限交给子 Agent,导致越权操作。

例如一个只读研究员角色被委派了写代码的权限,就会破坏系统安全边界。

本阶段需要定义 AgentRole 和 delegate 函数,确保委派只能缩小权限,不能扩大。

先做判断

在 delegate 函数中,如果调用者请求的 allowed_capabilities 包含角色没有的权限,应该发生什么?

  • 直接创建委派,因为调用者知道自己在做什么
  • 抛出 ValueError,因为委派不能扩大角色权限
  • 自动忽略多余的权限,只保留角色已有的
  • 记录警告但继续执行

判断依据: 正确选项是抛出 ValueError。委派必须受角色边界约束,任何越权请求都应立即失败,而不是静默忽略或信任调用者。

它是什么

角色合同

角色合同是一个不可变的数据结构,明确声明角色的能力集合和可产生的输出键集合。

委派是角色的一个受限实例,它从角色继承身份,但只能使用角色能力的一个子集,并且必须产出角色可产出的键。

delegate 函数是合同的执行点,它验证委派请求是否在角色边界内,通过后才创建委派对象。

它不是什么

不是随意授权

角色合同不是简单的权限列表,它必须同时约束能力和输出,防止角色被要求做它做不到的事。

委派不是复制角色,它不能拥有角色没有的权限,也不能承诺输出角色无法生成的键。

delegate 函数不是信任调用者的便捷构造器,而是强制边界的安全检查点。

它与相邻概念的关系

角色与委派的关系

AgentRole 定义角色的静态边界,Delegation 是运行时的一次具体任务分配,必须满足角色的边界。

delegate 函数连接两者:它接收角色和委派参数,检查 allowed_capabilities 是角色 capabilities 的子集,expected_output_keys 是角色 output_keys 的子集。

如果检查失败,函数抛出 ValueError,阻止不合法的委派产生,从而保证系统安全。

本阶段的边界决定: 当委派请求的权限或输出键超出角色边界时,必须抛出异常而不是静默调整,因为静默调整会掩盖调用者的错误并可能引入安全漏洞。

STAGE 02

使用可关联、可验证的消息封装 Agent 间请求和结果

先看一个具体问题

响应必须来自被委派的 Agent

在 Stage 01 中,我们定义了角色契约,但 Agent 之间交换的请求和结果还没有统一的消息结构。

如果任何 Agent 都能回复一个关联 ID 匹配的消息,那么 reviewer 可以冒充 researcher 提交结果,导致任务被错误地标记为完成。

本阶段需要实现 create_messagecorrelate 函数,确保响应消息的发送者与原始请求的接收者一致,并且消息类型受限。

先做判断

在实现 correlate 函数时,以下哪个条件对于防止响应伪造是必要的?

  • 响应消息的 correlation_id 必须与请求相同
  • 响应消息的 sender 必须等于请求的 recipient
  • 响应消息的 recipient 必须等于请求的 sender
  • 响应消息的 kind 必须是 resulterror

判断依据: 正确答案是第二个选项。虽然所有条件都是有效关联的一部分,但防止伪造的关键是验证响应发送者身份:响应必须来自请求的接收者。如果只检查关联 ID 和接收者,攻击者可以伪造发送者。

它是什么

消息契约是什么

消息契约是一组规则,确保 Agent 之间的通信可关联、可验证。

每个消息包含 message_idcorrelation_idsenderrecipientkindpayload,其中 correlation_id 用于关联请求和响应。

correlate 函数验证响应是否合法:关联 ID 匹配、发送者和接收者身份反转、消息类型为 resulterror

它不是什么

消息契约不是什么

消息契约不是简单的数据容器,它强制实施身份和类型约束。

它不是任何 Agent 都可以随意回复的机制;响应必须来自被委派的 Agent。

它不保证消息内容正确,只保证消息来源和类型符合协议。

它与相邻概念的关系

与其他概念的关系

消息契约建立在 Stage 01 的角色契约之上,角色定义了谁可以做什么,消息契约定义了如何交换信息。

create_message 在创建时验证消息的合法性,correlate 在关联时验证响应身份,两者共同维护通信完整性。

消息类型限制(requestresulterrorcancel)防止协议混乱,确保只有预期类型的消息被处理。

本阶段的边界决定:correlate 中必须检查 response.sender == request.recipient,否则任何 Agent 都可以伪造响应;同时,create_message 必须拒绝未知类型和自发送消息。

STAGE 03

通过乐观并发控制防止 Agent 静默覆盖彼此的状态更新

先看一个具体问题

两个 Agent 基于同一旧版本更新导致丢失更新

在办公研究场景中,研究员 Agent A 和 Agent B 同时读取共享状态,都看到版本号为 0。

Agent A 先写入 research 键并成功更新版本到 1,但 Agent B 仍基于版本 0 写入 review 键,覆盖了 Agent A 的更新。

最终状态中 research 键丢失,系统没有检测到任何冲突,导致数据不一致且难以追踪。

你需要实现一个 SharedState 类,要求更新时必须提供读取时的版本号,否则抛出冲突异常,从而防止静默覆盖。

先做判断

如果两个 Agent 都基于版本 0 读取状态,然后依次更新不同的键,会发生什么?

  • 两个更新都会成功,最终状态包含两个键
  • 第二个更新会抛出冲突异常,因为版本已变化
  • 第二个更新会静默覆盖第一个更新,导致数据丢失
  • 系统会自动合并两个更新,无需版本检查

判断依据: 正确答案是第二个更新会抛出冲突异常。乐观并发控制要求每次更新必须携带读取时的版本号,如果版本不匹配则拒绝更新,从而避免丢失更新。

它是什么

乐观并发控制

乐观并发控制假设冲突很少发生,因此不提前加锁,而是在更新时检查版本号是否与读取时一致。

如果版本一致,则应用更改并将版本号加一;如果不一致,则抛出冲突异常,由调用方决定重试或放弃。

这种机制允许多个 Agent 并发读取,只在写入时进行冲突检测,提高了并发性能。

它不是什么

不是悲观锁或直接更新

它不是在读取时锁定资源,也不是在更新时无条件覆盖现有值。

它不保证所有更新都会成功,而是要求调用方处理冲突异常。

它不能解决所有并发问题,例如需要原子性复合操作时可能需要更复杂的机制。

它与相邻概念的关系

与快照和版本号的关系

SharedState 维护内部版本号和值字典,read 方法返回一个不可变的 StateSnapshot,包含当前版本号和值的深拷贝。

update 方法接收期望版本号和更改字典,首先比较期望版本号与当前版本号,如果不匹配则抛出 RuntimeError。

如果匹配,则应用更改、递增版本号,并返回新的快照。快照的深拷贝确保外部修改不会影响内部状态。

本阶段的边界决定: 更新必须提供读取时的版本号,否则抛出冲突异常;快照必须隔离可变状态,防止外部修改影响内部数据。

STAGE 04

对互斥提案应用确定性安全优先规则,并保留决议理由

先看一个具体问题

多个 Agent 提出互斥方案时,如何安全地选择?

在上一阶段,多个 Agent 通过共享状态交换了提案,但还没有决定采用哪一个。

现在有两个 Agent 提出了互斥方案:一个要删除数据(不可逆),另一个要归档数据(可逆)。

如果直接比较证据数量,删除方案可能因为证据更多而胜出,但这会带来不可逆的风险。

我们需要一个确定性的规则:不可逆提案必须显式批准才能参与,否则按风险等级和证据数量排序。

先做判断

在冲突解决中,如果有一个不可逆提案和一个可逆提案,且不可逆提案未获批准,你认为应该如何处理?

  • 直接选择证据数量更多的提案
  • 排除未批准的不可逆提案,然后选择风险最低的提案
  • 随机选择一个提案
  • 让 Agent 再次投票

判断依据: 正确答案是排除未批准的不可逆提案,然后选择风险最低的提案。因为不可逆操作一旦执行就无法撤销,必须显式批准才能参与;否则按风险等级和证据数量确定性排序。

它是什么

确定性安全优先规则

冲突解决是一个纯函数,输入提案列表和是否批准不可逆提案的标志,输出一个决议对象。

它首先过滤掉未批准的不可逆提案,然后在剩余提案中按风险等级升序、证据数量降序、Agent ID 升序排序,选择第一个作为胜者。

如果过滤后没有提案,则返回 reason 为 “approval_required” 的决议,表示需要批准不可逆提案才能继续。

它不是什么

不是简单的证据数量比较

它不是仅仅比较证据数量,因为风险等级更高的提案即使证据更多也不能自动胜出。

它也不是随机选择或让模型再次投票,而是基于明确规则的确定性排序。

它不会自动批准不可逆提案,除非调用者显式传入 approval_for_irreversible=True。

它与相邻概念的关系

与共享状态和消息的关系

冲突解决函数接收来自共享状态的提案列表,这些提案由不同 Agent 通过消息传递产生。

决议结果可以写回共享状态,供后续阶段使用,例如执行胜出的提案。

风险等级映射 RISK_ORDER 定义了 read_only、reversible、irreversible 的排序,确保低风险优先。

本阶段的边界决定: 当提案列表为空时返回 no_proposals;当过滤后为空时返回 approval_required;否则返回 lowest_risk_then_evidence 的胜者。

STAGE 05

按能力、权限和当前负载选择最小权限 Agent,无法安全委派时明确拒绝

先看一个具体问题

如何选择既能完成任务又拥有最少权限的 Agent?

当前系统中,多个 Agent 角色拥有不同权限集合,例如 reader 只有 read 权限,admin 拥有 read、write、process 权限。

当收到一个只读任务时,如果仅按当前负载选择,空闲的 admin 会被选中,导致不必要的权限暴露。

我们需要实现一个路由函数 choose_worker,在满足能力要求的前提下,优先选择权限最少的 Agent,以降低安全风险。

先做判断

在多个 Agent 都能完成任务时,你认为应该优先选择哪个 Agent?

  • 负载最低的 Agent
  • 权限最少的 Agent
  • 角色 ID 最小的 Agent
  • 随机选择一个 Agent

判断依据: 正确答案是“权限最少的 Agent”。因为最小权限原则要求只授予完成任务所必需的权限,减少潜在的安全风险。负载和角色 ID 仅用于打破平局。

它是什么

最小权限路由是什么

最小权限路由是一种选择策略:在所有能够满足任务所需能力的 Agent 中,优先选择权限集合最小的那个。

它通过比较每个 Agent 的能力集合大小(即权限广度)来排序,确保选中的 Agent 不会拥有超出任务需要的额外权限。

它不是什么

最小权限路由不是什么

它不是简单的负载均衡:负载最低的 Agent 可能拥有过多权限,即使它当前空闲,也不应被选中执行低权限任务。

它也不是随机选择或按角色 ID 排序:这些方法可能忽略权限差异,导致不必要的权限暴露。

它与相邻概念的关系

与其他概念的关系

最小权限路由建立在角色契约(Stage 01)之上:每个 Agent 角色明确定义了其能力集合,路由函数依赖这些能力集合进行筛选和排序。

它与冲突解决(Stage 04)互补:冲突解决处理多个 Agent 之间的意见分歧,而路由负责在任务开始前选择合适的执行者。

本阶段的边界决定: 选择必须首先最小化权限广度,然后才考虑负载和角色 ID 打破平局。

STAGE 06

根据必需角色和最小成功数判断任务可继续、降级或失败

先看一个具体问题

当部分 Agent 失败时,如何决定协作是成功、降级还是失败?

在 Stage 05 中,Supervisor 已经能够将任务路由给不同的 Agent,但还没有定义当部分 Agent 失败时如何评估整体结果。

现在需要实现一个评估函数 assess_outcomes,它接收每个 Agent 的结果、必需角色集合和最小成功数,然后返回一个决策对象,状态为 completedegradedfailed

一个常见的错误是只检查成功数量是否达到阈值,而忽略了某些角色是必需的,即使数量足够,缺少必需角色也必须判定为失败。

本阶段将实现这个策略,并通过测试验证:必需角色失败导致整体失败,可选角色失败导致降级,全部成功才是完整。

先做判断

如果 required_roles 包含 builder,但 builder 失败了,而其他两个角色成功,且 minimum_successes=2assess_outcomes 应该返回什么状态?

  • complete,因为成功数量达到了阈值
  • degraded,因为有一个角色失败
  • failed,因为必需角色 builder 失败
  • failed,因为成功数量不足

判断依据: 正确答案是 failed,因为必需角色失败必须导致整体失败,即使成功数量达到了阈值。这体现了角色语义优先于数量阈值的策略。

它是什么

部分失败评估策略

assess_outcomes 是一个纯函数,它根据三个输入(结果列表、必需角色集合、最小成功数)计算协作状态。

它首先收集所有成功角色的集合,然后计算缺失的必需角色(必需角色减去成功角色)。

如果缺失的必需角色非空,或者成功数量小于最小成功数,则返回 failed 状态,并附带缺失的必需角色元组。

否则,如果存在任何失败的角色(即结果状态不是 succeeded),则返回 degraded 状态。

如果所有角色都成功,则返回 complete 状态。

它不是什么

不是简单的数量阈值

这个策略不是仅仅比较成功数量和 minimum_successes;它必须优先检查必需角色是否全部成功。

它也不是对每个角色一视同仁;必需角色和可选角色在失败时的影响不同。

它不会因为成功数量足够而忽略必需角色的缺失;必需角色的失败是致命的。

它不会在存在失败角色时返回 complete;只有全部成功才是完整。

它与相邻概念的关系

与 Supervisor 和消息的关系

assess_outcomes 被 Supervisor 在收集所有 Agent 结果后调用,以决定下一步行动。

它依赖于 AgentOutcome 数据类,该数据类包含 role_idstatus 字段。

它返回 CollaborationDecision 数据类,包含 statusmissing_required 字段。

这个策略是 Stage 05 路由逻辑的补充,为 Supervisor 提供了失败处理能力。

本阶段的边界决定: 当必需角色失败时,无论成功数量多少,都必须返回 failed;当只有可选角色失败且成功数量达到阈值时,返回 degraded;当所有角色成功时,返回 complete

STAGE 07

组合最小权限路由、消息关联、共享状态和部分失败策略,生成可审计协作结果

先看一个具体问题

为什么顺序执行任务还不够?

在 Stage 06 中,你已经实现了部分失败策略,但还没有一个统一的协调器来串联所有机制。

当前代码中不存在 forge/multiagent/orchestrator.py,因此测试在导入 WorkItemcoordinate 时直接失败。

你需要创建一个协调器,它必须为每个工作项选择最小权限代理、记录必需角色、更新共享状态,并根据结果评估整体状态。

如果只是按顺序调用处理函数而不跟踪必需角色,那么当必需代理失败时,运行会被错误地降级而不是失败。

先做判断

在实现 coordinate 之前,请预测:如果某个必需工作项的处理函数抛出异常,但协调器没有记录该角色为必需角色,最终运行状态会是什么?

  • 运行状态为 failed,因为异常被捕获并记录为失败。
  • 运行状态为 degraded,因为失败的角色没有被标记为必需,评估逻辑将其视为可选失败。
  • 运行状态为 complete,因为异常被忽略,其他工作项成功。
  • 运行会直接崩溃,因为异常未被捕获。

判断依据: 正确答案是 degraded。因为 assess_outcomes 根据 required_roles 集合判断是否必须失败;如果集合为空,任何失败都只会导致降级。

它是什么

协调器是一个有状态的工作流引擎

协调器接收工作项列表、可用代理列表和处理函数映射,然后按顺序处理每个工作项。

对于每个工作项,它使用 choose_worker 根据所需能力选择最小权限代理,并记录分配结果。

如果工作项被标记为必需,协调器必须将所选代理的角色 ID 加入 required_roles 集合。

处理成功后,协调器将输出写入共享状态,并记录一个成功结果;处理失败则记录一个失败结果。

最后,协调器调用 assess_outcomes 评估整体状态,并返回包含状态、分配和状态版本的 CollaborationRun

它不是什么

协调器不是简单的顺序执行器

协调器不能忽略工作项的必需性;必需角色的失败必须导致整个运行失败,而不是降级。

协调器不能跳过共享状态的更新;每个成功的工作项输出都必须提交到状态中,以便后续审计。

协调器不能自行决定代理选择逻辑;它必须依赖 choose_worker 来保证最小权限路由。

协调器不能在没有可用代理时静默分配;如果找不到满足能力的代理,它必须抛出 ValueError

它与相邻概念的关系

协调器如何组合已有组件

choose_worker 来自 supervisor.py,它根据能力集合选择最小权限代理,如果找不到则抛出 ValueError

SharedState 来自 shared_state.py,它维护版本化的键值存储,每次更新都会递增版本号。

assess_outcomes 来自 resilience.py,它根据结果列表和必需角色集合决定最终状态。

AgentOutcome 是结果的数据结构,包含角色 ID 和状态(succeededfailed)。

协调器将这些组件串联起来,形成一个可审计的端到端运行。

本阶段的边界决定: 协调器必须记录每个必需工作项所选代理的角色 ID,并在最终评估时传递该集合;否则,必需代理的失败会被错误地视为可选失败。

完成本章

增加研究、实现和评审角色,并证明何时多 Agent 不值得使用。

FORGE / LOCAL CHECK

本地实验自检

  1. 未开始
  2. 2阅读中
  3. 3实验已下载
  4. 4测试结果已读取
  5. 5本地自检通过

verification.json 只在当前浏览器中解析,不会上传。这里验证的是实验合同,不是服务器认证或第三方背书。

概念校准与一周复习

三道题检查你是否掌握了本章边界、交付证据和恢复方法。答案只保存在当前浏览器。

  1. 1哪一项最能证明你真正完成了「多 Agent 协作与任务编排」?
  2. 2关于「子 Agent」,哪一种理解最准确?
  3. 3实验卡住时,哪个动作是本章建议的最小恢复点?