3. Multi-Agent:通信、协作、任务分发与 SuperAgent 路由
3. Multi-Agent:通信、协作、任务分发与 SuperAgent 路由
3.1 原理
Multi-Agent 的难点不是“并行调用多个模型”,而是任务图、状态所有权和结果一致性。核心组件包括:
- Agent Registry:记录 agent id、父子关系、角色、能力、状态、所在 Runtime。
- Task Graph:任务依赖、所有者、优先级、输入、验收条件、重试策略。
- Message Bus/Mailbox:点对点消息、广播、结果通知、ack、去重、超时。
- Scheduler/Router:根据能力、负载、上下文成本和依赖选择执行者。
- Join/Aggregator:等待部分或全部结果,做冲突检测、排序、合并和最终验收。
所谓 SuperAgent 更适合理解为根协调角色,而不是拥有无限能力的 Agent。它负责拆解、路由、等待、合并和终止;业务执行下沉给工作 Agent。为了避免单点上下文膨胀,SuperAgent 只保留任务摘要、状态和证据索引,不复制所有子 Agent 全量历史。
3.2 Claude Code 源码
runAgent创建独立agentId、记录父 id、准备上下文与工具,并支持工作树隔离和持续消息回调:runAgent.ts:248、runAgent.ts:347。- Fork 子 Agent 会继承父对话,但过滤不完整工具调用;为 Prompt Cache 共享构造字节一致的前缀,并使用占位 ToolResult 保持 API 配对不变量:
forkSubagent.ts:91、forkSubagent.ts:96。 - 恢复 Agent 时读取 transcript/metadata、过滤未解决 ToolUse、重建替换状态并恢复 worktree:
resumeAgent.ts:42、resumeAgent.ts:64。
3.3 Codex 源码
AgentControl在 spawn 前检查全局执行容量和最大线程数,并继承环境/执行策略:spawn.rs:382。- 创建子 Thread 后持久化父子边、发送初始输入或
InterAgentCommunication,再启动完成监听:spawn.rs:452、spawn.rs:538。 - 通信事件明确区分
Spawn/Message/Followup/Result,并记录 sender、receiver、communication id:agent_communication.rs:6、agent_communication.rs:44。
源码没有名为 SuperAgentRouter 的单体类。对应能力由根 Agent 的模型决策、AgentControl、父子 Thread 图、Mailbox 和完成监听共同实现。这是“架构推导”,工程评审时要如实表述。
3.4 调度伪代码
function route(task, agentRegistry): // 为一个可运行任务选择最合适且有权限的 Agent
candidates = agents.filter(a => // 从注册表中过滤不满足硬约束的执行者
a.status == IDLE && // 只选择当前有容量、未执行其他任务的 Agent
a.capabilities covers task.requiredCapabilities && // Agent 能力集合必须覆盖任务所需能力
a.permissionProfile permits task.scope) // Agent 的权限上限必须允许访问任务资源
score(a) = skillMatch(a, task) // 能力和历史专长匹配越高,基础得分越高
- contextTransferCost(a, task) // 传输大量上下文会增加 Token、延迟和泄漏风险
- currentLoad(a) // 当前负载越高,排队与超时风险越大
- historicalFailureRate(a, task.type) // 同类任务历史失败率用于降低不可靠 Agent 得分
return argmax(candidates, score) // 返回综合得分最高的候选 Agent
async function superAgentExecute(goal): // 定义根协调 Agent 执行复杂目标的流程
graph = planner.decompose(goal) // 将目标分解为带依赖和验收条件的任务 DAG
while graph.hasRunnableNodes: // 只要仍有依赖已满足的节点就继续调度
for node in graph.runnableNodes: // 遍历当前所有可并行执行的任务节点
agent = route(node, registry) // 根据能力、权限、成本和负载选择执行者
mailbox.send(agent, assignment(node)) // 通过可追踪 Mailbox 发送结构化任务分配
events = await mailbox.waitAnyResult() // 等待任一 Agent 的进度、结果或失败事件
graph.apply(events) // 幂等地更新节点状态、证据和后继节点可运行性
if conflict(events): // 检测多个结果之间的文件、事实或结论冲突
createVerificationTask(events) // 创建独立验证任务,而不是任意选择一个结果
return aggregator.mergeAndVerify(graph.results) // 汇总所有结果并通过最终验收器后返回
3.5 工程检验题
问:子 Agent 之间共享内存还是消息通信?
优先使用消息通信,并用执行产物(Artifact)传递大结果;共享文件系统只作为受控的数据通道。直接共享可变内存会造成竞态、难回放和权限越界。消息需要 id、sender/receiver、父任务、版本、确认状态和去重键。
问:如何解决多个 Agent 修改同一文件?
按文件或模块分区,使用独立 Worktree 或补丁产物(Patch Artifact);合并前检查基线哈希,冲突交给专门的集成 Agent 或根 Agent;禁止“最后写入者获胜”。
3.6 原理深化:Multi-Agent 是四个一致性域的组合
3.6.1 四个一致性域怎么读
如何读图: 从 Router 向下分别检查 Spawn、Message、Task 和 Workspace,不要先问笼统的“子 Agent 成功了吗”。Spawn 回答执行实体是否存在并能进入终态;Message 回答指令和结果是否可关联、可去重、可确认送达;Task 回答责任所有者、依赖和验收状态;Workspace 回答写空间是否隔离、产物能否合并。四条链都有确定终态,Aggregator 才能宣告协作完成。
例如子 Agent 进程退出但任务仍为 running,说明 Spawn 已终止而 Task 未收敛;任务标记为 completed 但补丁没有进入集成分支,说明 Task 已终止而 Workspace 未合并。恢复时先定位失效的域,只重放该域允许重放的动作,避免重新 Spawn 后又重复执行已经完成的外部副作用。
Claude Code 的 AgentTool 先解析 Agent Definition,再由 runAgent() 计算模型、工具池、权限模式和同步/异步执行方式;它不会简单复制父 Agent 全部上下文与权限。allowedTools、Agent 自带 mode、父 Session mode 和后台执行规则共同决定子 Agent 的有效能力:runAgent.ts:412、runAgent.ts:436、runAgent.ts:465。
消息和任务是两种协议:Mailbox 传递指令、结果和审批通知;Task Store 保存 owner/status/blocks/blockedBy 并通过锁或原子更新认领。消息已发送不代表任务已转移,任务 owner 改变也不代表上下文已完整交接。Claude Code 在 owner 更新时额外发送分配消息,正是为了同步这两个平面:TaskUpdateTool.ts:276、TaskUpdateTool.ts:300。
Codex 的 agent/control 管生命周期与层级,agent_communication 把消息工具路由到目标线程。父 Agent 应持有调度责任,但不能直接篡改子线程内部 Step;跨线程只交换结构化消息、任务状态和 Artifact 引用,以保持可恢复边界。
并行写代码还要单独走 Worktree 链:创建独立目录、持久化 worktree state、执行、验证、汇总 Diff、由集成者合并。Worktree 不改变 Task owner,也不授予额外权限。工程评审时把 Spawn、Message、Task、Workspace 四条链分别画出,通常能避免把 Multi-Agent 误讲成“开多个线程”。
四条链之所以必须分开,是因为它们的成功条件不同:Spawn 成功只表示执行实体存在,Message 成功只表示消息可达,Task 转移成功只表示责任归属变化,Worktree 创建成功只表示文件视图隔离。真正的端到端完成需要 Join/Aggregator 同时验证任务终态、结果证据、工作区 Diff 和子 Agent 生命周期;否则可能出现 Agent 已退出但任务仍 running,或任务已完成但补丁从未进入集成分支。
3.7 DeepSeek Harness:Subagent 是能力 seam 与可持久调用链
packages/subagent/ 把子 Agent 拆成 ctx.subagents Service Definition、进程内 spawn/fork 提供方、ACP/Codex/Claude Code/DSH SDK 进程外提供方,以及 tool-subagent、tool-subagent-control、tool-subagent-report Consumer。同一父 Agent 因此可按提供方选择一次性或可继续子会话,而不需改 Agent Loop。
subagent/src/child-agent.ts 把 parentSession、origin: subagent、delegationDepth、继承历史的 seedLength 和 Agent Preset 写入子 Session header,并在创建窗口将父组装加入子 Scope。子 Agent 只继承父会话明确的 Sandbox mode,Approval policy 固定为 never;这些覆盖以 source: delegation 事件记入子日志,保证重启后委派权限不会意外扩大。
subagent/start 与 subagent/end 只表示一次运行 epoch,终止原因从子 Session 自己的 turn/end 折叠,不用“进程退出了”代替“任务完成了”。这一点直接对应任务所有权、委派深度、权限递减和可恢复的四个 Multi-Agent 不变量。