工作流与编排
把复杂任务拆成可恢复的步骤,并通过明确合同协调执行单元。
如何拆解复杂任务,并协调步骤或多个 Agent?概念校准
子 Agent
- 它是什么
- 被委派明确目标、输入、权限和返回合同的受限执行单元。
- 它不是什么
- 不等于简单并行;没有合同的角色拆分只会放大成本和冲突。
规划
- 它是什么
- 为多步骤、高成本或需要恢复的任务建立可检查、可更新的执行结构。
- 它不是什么
- 不是所有 Agent 的必备步骤,简单任务常常不需要独立规划器。
验证与评测
- 它是什么
- 用测试、Schema、引用、状态和验收规则检查单次结果,并用数据集衡量系统表现。
- 它不是什么
- 不等于让模型对自己的输出再给一次意见;验证必须落到外部可检查证据。
多 Agent 协作与任务编排
通过消息合同、共享状态、冲突处理和成本评测判断何时拆分角色。
Python 3.12 · pytest · Pydantic · SQLite · deterministic Mock概念校准\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
用明确目标、输入、权限和输出 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,阻止不合法的委派产生,从而保证系统安全。
本阶段的边界决定: 当委派请求的权限或输出键超出角色边界时,必须抛出异常而不是静默调整,因为静默调整会掩盖调用者的错误并可能引入安全漏洞。
使用可关联、可验证的消息封装 Agent 间请求和结果
先看一个具体问题
响应必须来自被委派的 Agent
在 Stage 01 中,我们定义了角色契约,但 Agent 之间交换的请求和结果还没有统一的消息结构。
如果任何 Agent 都能回复一个关联 ID 匹配的消息,那么 reviewer 可以冒充 researcher 提交结果,导致任务被错误地标记为完成。
本阶段需要实现 create_message 和 correlate 函数,确保响应消息的发送者与原始请求的接收者一致,并且消息类型受限。
先做判断
在实现 correlate 函数时,以下哪个条件对于防止响应伪造是必要的?
- 响应消息的
correlation_id必须与请求相同 - 响应消息的
sender必须等于请求的recipient - 响应消息的
recipient必须等于请求的sender - 响应消息的
kind必须是result或error
判断依据: 正确答案是第二个选项。虽然所有条件都是有效关联的一部分,但防止伪造的关键是验证响应发送者身份:响应必须来自请求的接收者。如果只检查关联 ID 和接收者,攻击者可以伪造发送者。
它是什么
消息契约是什么
消息契约是一组规则,确保 Agent 之间的通信可关联、可验证。
每个消息包含 message_id、correlation_id、sender、recipient、kind 和 payload,其中 correlation_id 用于关联请求和响应。
correlate 函数验证响应是否合法:关联 ID 匹配、发送者和接收者身份反转、消息类型为 result 或 error。
它不是什么
消息契约不是什么
消息契约不是简单的数据容器,它强制实施身份和类型约束。
它不是任何 Agent 都可以随意回复的机制;响应必须来自被委派的 Agent。
它不保证消息内容正确,只保证消息来源和类型符合协议。
它与相邻概念的关系
与其他概念的关系
消息契约建立在 Stage 01 的角色契约之上,角色定义了谁可以做什么,消息契约定义了如何交换信息。
create_message 在创建时验证消息的合法性,correlate 在关联时验证响应身份,两者共同维护通信完整性。
消息类型限制(request、result、error、cancel)防止协议混乱,确保只有预期类型的消息被处理。
本阶段的边界决定: 在 correlate 中必须检查 response.sender == request.recipient,否则任何 Agent 都可以伪造响应;同时,create_message 必须拒绝未知类型和自发送消息。
对互斥提案应用确定性安全优先规则,并保留决议理由
先看一个具体问题
多个 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 的胜者。
按能力、权限和当前负载选择最小权限 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 打破平局。
根据必需角色和最小成功数判断任务可继续、降级或失败
先看一个具体问题
当部分 Agent 失败时,如何决定协作是成功、降级还是失败?
在 Stage 05 中,Supervisor 已经能够将任务路由给不同的 Agent,但还没有定义当部分 Agent 失败时如何评估整体结果。
现在需要实现一个评估函数 assess_outcomes,它接收每个 Agent 的结果、必需角色集合和最小成功数,然后返回一个决策对象,状态为 complete、degraded 或 failed。
一个常见的错误是只检查成功数量是否达到阈值,而忽略了某些角色是必需的,即使数量足够,缺少必需角色也必须判定为失败。
本阶段将实现这个策略,并通过测试验证:必需角色失败导致整体失败,可选角色失败导致降级,全部成功才是完整。
先做判断
如果 required_roles 包含 builder,但 builder 失败了,而其他两个角色成功,且 minimum_successes=2,assess_outcomes 应该返回什么状态?
complete,因为成功数量达到了阈值degraded,因为有一个角色失败failed,因为必需角色builder失败failed,因为成功数量不足
判断依据: 正确答案是 failed,因为必需角色失败必须导致整体失败,即使成功数量达到了阈值。这体现了角色语义优先于数量阈值的策略。
它是什么
部分失败评估策略
assess_outcomes 是一个纯函数,它根据三个输入(结果列表、必需角色集合、最小成功数)计算协作状态。
它首先收集所有成功角色的集合,然后计算缺失的必需角色(必需角色减去成功角色)。
如果缺失的必需角色非空,或者成功数量小于最小成功数,则返回 failed 状态,并附带缺失的必需角色元组。
否则,如果存在任何失败的角色(即结果状态不是 succeeded),则返回 degraded 状态。
如果所有角色都成功,则返回 complete 状态。
它不是什么
不是简单的数量阈值
这个策略不是仅仅比较成功数量和 minimum_successes;它必须优先检查必需角色是否全部成功。
它也不是对每个角色一视同仁;必需角色和可选角色在失败时的影响不同。
它不会因为成功数量足够而忽略必需角色的缺失;必需角色的失败是致命的。
它不会在存在失败角色时返回 complete;只有全部成功才是完整。
它与相邻概念的关系
与 Supervisor 和消息的关系
assess_outcomes 被 Supervisor 在收集所有 Agent 结果后调用,以决定下一步行动。
它依赖于 AgentOutcome 数据类,该数据类包含 role_id 和 status 字段。
它返回 CollaborationDecision 数据类,包含 status 和 missing_required 字段。
这个策略是 Stage 05 路由逻辑的补充,为 Supervisor 提供了失败处理能力。
本阶段的边界决定: 当必需角色失败时,无论成功数量多少,都必须返回 failed;当只有可选角色失败且成功数量达到阈值时,返回 degraded;当所有角色成功时,返回 complete。
组合最小权限路由、消息关联、共享状态和部分失败策略,生成可审计协作结果
先看一个具体问题
为什么顺序执行任务还不够?
在 Stage 06 中,你已经实现了部分失败策略,但还没有一个统一的协调器来串联所有机制。
当前代码中不存在 forge/multiagent/orchestrator.py,因此测试在导入 WorkItem 和 coordinate 时直接失败。
你需要创建一个协调器,它必须为每个工作项选择最小权限代理、记录必需角色、更新共享状态,并根据结果评估整体状态。
如果只是按顺序调用处理函数而不跟踪必需角色,那么当必需代理失败时,运行会被错误地降级而不是失败。
先做判断
在实现 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 和状态(succeeded 或 failed)。
协调器将这些组件串联起来,形成一个可审计的端到端运行。
本阶段的边界决定: 协调器必须记录每个必需工作项所选代理的角色 ID,并在最终评估时传递该集合;否则,必需代理的失败会被错误地视为可选失败。
完成本章
增加研究、实现和评审角色,并证明何时多 Agent 不值得使用。
本地实验自检
- 未开始
- 2阅读中
- 3实验已下载
- 4测试结果已读取
- 5本地自检通过
verification.json 只在当前浏览器中解析,不会上传。这里验证的是实验合同,不是服务器认证或第三方背书。
概念校准与一周复习
三道题检查你是否掌握了本章边界、交付证据和恢复方法。答案只保存在当前浏览器。