显式 AgentInvocation
所有 Agent 间派单走单一入口;不允许直连模型或私下互调
父子 Run 树
每次派单创建独立 child Run,DelegationTrace 串成可回放的协作树
Round 0/1/2 节奏
规划 → 执行 → 汇总三段式;Tier 3 内核完全不感知 Round 概念
详细设计说明
把多 Agent 协作收敛为「显式协议 + 父子 Run + 必经 ResultSink」
这份文档定义 Agent 之间在 Tier 2 层如何派单、协作、回收结果。核心三件事:用 AgentInvocation 替代隐式互调;用父子 Run + DelegationTrace 保证协作可回放;用 Round 0/1/2 + AnswerObservationProtocol 把 Tier 3 单 Run 内核与 Tier 2 协作语义干净分层。
设计主张
多 Agent 协作不是 Tier 3 内核的能力,而是 Tier 2 Coordinator 的编排责任。每个 Agent 的执行仍是一个独立 Run、独立 RunState、独立审计;协作是把这些 Run 用 DelegationTrace 串成树,让父能等子、子能回结果给父。
评审关注点
- 父子 Run 的终态如何聚合给父
- Round 2 汇总轮是不是另起 Run
- 子 Run 边界是否真的隔离
- Tier 3 是否完全不感知协作语义
- DelegationTrace 能否单库回放
Agent Platform · 多 Agent 协作协议
状态:v1 草案
日期:2026-05-07
定位:Tier 2 Coordinator + JobDispatcher 上的协作编排协议;Tier 1 暴露 @单 Agent / @协作群两类入口;Tier 3 完全不感知 Round 与 Delegation
关联:与 Agent Teams 设计(协作单元的承载实体)、Harness 内核设计(单 Run 状态机契约)、可观测性设计(Trace 字段扩展)配套
目标:让多 Agent 协作具备「可派单、可等待、可汇总、可回放」四项硬能力,而不引入新的运行时身份
1. 定位与概念
一张图看清 AgentInvocation 住哪一层、Coordinator 与 JobDispatcher 怎么分工、Run 树是如何被 DelegationTrace 串起来的。本章是阅读其余章节的入口。
1.1 总体架构图
下图把三层定位与协作树状态叠加在一张图上:横向 3 个 tier 带,T1 区给出协作工作台暴露的 2 类入口,T2 区显示 Coordinator + JobDispatcher 协同 + Round 0/1/2 节奏,T3 区只跑独立 Run(每个 child Run 一个独立 RunState)。Run 树由 DelegationTrace 串成。
1.2 两个典型时序图
下面两张图分别对应 T1 入口的两类语义:Scenario A 是 @ 单 Agent 派单(Parent 起一个 Child);Scenario B 是 @ 协作群派单(Parent 起 N 个 Child + Round 2 汇总)。点击 lifeline 头部色块跳到对应章节。
最简形态:Parent Run 在执行中决定把一个子任务委托给 Agent B。Coordinator 起 Child Run,DelegationTrace 落两行(请求 / 完成),ResultSink 把结果回填 Parent。没有 Round 2。
AgentInvoke Tool 调用——状态保持 EXECUTING 阻塞在 Tool 控制器上,直到 ⑩ ResultSink 把 observation 当作 Tool 返回值回填,T3 完全不感知"在等子 Run"。DelegationTrace 落 2 行(请求 + 完成)即足以回放。完整三段式:Round 0 Coordinator 展开协作群 / 选 strategy;Round 1 并行起 N 个 child Run;Round 2 全部 ResultSink 收齐后起一个 Consolidator Run 整合。所有 N+1 个子 Run 都挂在同一个 parent_run_id 下。
1.3 为什么要做协作协议
当前缺口:
| 缺口 | 具体表现 | 影响 |
|---|---|---|
| Agent 间私下互调无契约 | Agent A 直接调 Agent B 的工具或读 B 的记忆 | 责任主体混淆;审计链断裂;权限放大 |
| 父子关系靠约定 | 没有 parent_run_id 字段,只能靠日志 join | 跨 Run 回放需要拼日志;成本无法聚合到父 |
| 协作群没有汇总轮承载 | 整合逻辑写在 Coordinator 进程里 | 整合过程不走 Harness 八步、不被 HITL 拦、不写 Trace |
| 子 Run 沙箱边界不明 | 有时被理解成"复用父沙箱" | 权限越权;状态污染;并行竞争 |
| Tier 3 被迫感知 Round | RunState 里塞 round_index 字段 | 内核被业务语义污染;难以演进 |
本协议解决的具体技术问题:
- 把所有 Agent 间派单收敛到一个 AgentInvocation 入口——不再允许任意路径互调
- 把父子关系从约定升级为字段——parent_run_id 是 RunTrace 的一等公民
- 把 Round 2 汇总变成另起一个独立 Run——汇总也享受 Harness 全套约束
- 明确 Tier 3 的 Run 是协作的最小调度单元——内核不感知 Round / Delegation 概念
1.4 三层职责定位
| 层 | 承担 | 不承担 |
|---|---|---|
| Tier 1 · 协作工作台 UI | 暴露 @ 单 Agent / @ 协作群两类入口;mention 解析;权限校验(用户可见的 Agent 列表);聊天侧把 invocation 串成可读时间线 | 不做协作策略决策;不持有父子 Run 关系 |
| Tier 2 · Coordinator 编排 | 展开 mention;选 strategy;推进 Round 0/1/2;通过 JobDispatcher 起 child Run;收 ResultSink;写 DelegationTrace;做 Round 2 起 Consolidator Run;终态聚合给 Parent | 不存储 Agent / Team 实体(属 T1);不直接调 LLM(属 T3 内核) |
| Tier 3 · Run 内核 | 每个 child Run 独立八步管道、独立 RunState、独立 RunState;通过 caller_extra 接收 parent_run_id 并落 Trace | 不感知 Round 概念;不感知 Delegation;不在 RunState 决策里读 parent;不跨 Run 共享上下文 |
2. 数据模型
列出协作协议引入的最小持久化与运行时实体,明确与 Run / Team / Trace 的关系。
2.1 AgentInvocation(运行时请求对象)
所有 Agent 间派单的统一入口,不存表,作为 Coordinator 内部消息结构。Tier 1 的 @ 提及和 Parent Run 内的"我要委托"都收敛到这一个对象。
| 字段 | 类型 | 说明 |
|---|---|---|
invocation_id | uuid | 幂等键;重试同一请求不会重复起 Run |
caller_kind | enum | user_mention / parent_run(区分两类来源) |
caller_run_id | uuid? | parent_run 来源时必填,user_mention 时可空 |
callee_agent_id | uuid | 被派单的 Agent |
group_session_id | uuid? | 协作群场景必填,单聊场景为空 |
round_index | int | 0 = 规划,1 = 执行,2 = 汇总;Coordinator 维护,不进 RunState |
task_brief | string | 自然语言任务描述(明文 + 已脱敏) |
context_subset | jsonb | 显式裁剪过的上下文子集,不是父 RunContext 的全量转发 |
expected_output | jsonb? | 期望返回 schema(Round 2 consolidator 必填) |
budget | jsonb | 本次 invocation 的成本上限、超时、HITL 阈值 |
2.2 GroupSession(协作群运行时上下文)
协作群 / @team 场景的共享运行时上下文。每次 @ 协作群都会创建一个新的 GroupSession(不是持久组织实体——那是 Team 的事)。
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid | 主键 |
parent_run_id | uuid | 关联的 Parent Run;同一次协作的 N+1 个 child 都挂这下面 |
team_id | uuid? | 关联的 Team(如果走 @team;ad-hoc 协作群可空) |
strategy | enum | fan-out-broadcast / sequential-relay / lead-driven(来自 Team 配置或全局默认) |
members | jsonb | 展开后的 callee_agent_id 列表 + 角色 |
round_state | jsonb | 当前 round_index、各 child 的 status / cost / latency |
consolidator_run_id | uuid? | Round 2 起的 Consolidator Run id(无汇总轮则空) |
created_at / closed_at | ts | 生命周期时间戳 |
2.3 DelegationTrace(持久化协作记录)
Tier 2 协作语义的可观测性主表。每次 invocation 落两行:delegation_requested + delegation_completed(或 _failed / _timeout)。回放协作树就是按 parent_run_id 查这张表。
| 字段 | 类型 | 说明 |
|---|---|---|
id | uuid | 主键 |
parent_run_id | uuid | 父 Run;查协作树的入口字段 |
child_run_id | uuid | 子 Run;与 RunTrace.run_id 对齐 |
group_session_id | uuid? | 协作群场景下与 GroupSession 关联 |
round_index | int | 0 / 1 / 2 |
caller_agent_id / callee_agent_id | uuid | 派单方向 |
event | enum | requested / accepted / rejected / completed / failed / timeout |
task_brief_hash | str | 任务描述指纹(防 PII 落库) |
context_subset_hash | str | 传递 context 指纹(用于审计是否泄漏敏感字段) |
cost_cents / latency_ms | int | 子 Run 实际消耗,回灌父用于聚合 |
created_at | ts | 事件时间 |
2.4 与现有实体的关系
| 关系 | 基数 | 说明 |
|---|---|---|
Parent Run → Child Run | 1 : n | RunTrace 表新增 parent_run_id 字段;查询用 WHERE parent_run_id=X |
GroupSession → Child Run | 1 : n | 同一次 @协作群产生的 N+1 个 child 都挂同一 group_session_id |
Team → GroupSession | 1 : n(可选) | 来自 @team 路径时关联;ad-hoc 协作群 team_id 为空 |
AgentInvocation → DelegationTrace | 1 : ≥2 | 每次 invocation 至少落 requested + 一条终态 |
3. 父子 Run 关系
定义 Parent / Child Run 在沙箱、Trace、终态聚合上的契约,让 Tier 3 内核在不感知协作的前提下仍能被父正确等待与聚合。
3.1 父 Run 等待协议
原则:不扩展 RunState、不让 T3 感知协作。AgentInvocation 在内核里就是一种 Tool 调用——T3 工具调用控制器认得 4 类调用(本地 Skill / 沙箱 Skill / LLM / AgentInvoke),AgentInvoke 这一类的执行路径是「交给 Coordinator → 等 ResultSink 回填」,对 T3 而言只是一次"慢一点的 Tool 调用"。
| 结果 | Tool 控制器看到 | Parent RunState |
|---|---|---|
| child 成功 + observation 回填 | Tool 调用返回正常结果 | 保持 EXECUTING,继续 Plan 下一步 |
| child 失败 + 父策略 = abort | Tool 调用抛错,不重试 | EXECUTING → FAILED → TRACING |
| child 失败 + 父策略 = retry / fallback | Tool 调用抛错,控制器按 manifest 重试 / 切 fallback agent | 保持 EXECUTING |
| 超 budget.deadline 未回填 | Tool 调用超时 | EXECUTING → TIMEOUT → TRACING |
这样 RunState 仍是 11 态、T3 状态机不变;"在等子 Run"只是 Tool 调用阻塞的一种具体形态,和"在等沙箱命令返回"没本质区别。Coordinator 在 T2 持有 parent_run_id → 待回填 invocation_id 的映射;ResultSink 触发时把 AnswerObservation 投回 Parent 的 Tool 控制器,调用栈自然解阻塞。
3.2 子 Run 执行边界独立性
每个 child Run 都独立持有自己的:
- RunState 状态机——独立的 11 态机,与父无耦合
- RunContext——按 callee_agent_id 装配的 system / history / memory,不继承父的上下文
- Skill schema 集合——按子 agent 权限过滤,不继承父的 Skill 列表
- Trace 序列——child 自己有完整 RunTrace;parent_run_id 作为字段,不是嵌入
- HITL 通道——child 触发 HITL 不影响父的等待态以外的语义
- 计费归属——child cost 单独累计,由 Coordinator 在父终态前聚合到
delegation_total_cost
沙箱(按需):只有当 child 调用沙箱类 Skill(如 claude_code_execute / shell_exec)时才申请独立沙箱容器,与 parent 沙箱不共享;纯 LLM / DB / 外部 API 调用的 child 不进沙箱(见 沙箱边界 ↗)。
父和子之间唯一的运行时联系是:Coordinator 在子完成后通过 ResultSink 把 observation 回填给父。其余资源边界严格隔离。
3.3 终态聚合规则
| 父策略 | 子终态 = SUCCEEDED | 子终态 = FAILED / REJECTED | 子终态 = TIMEOUT |
|---|---|---|---|
abort_on_child_fail(默认) | 父继续 EXECUTING | 父 → FAILED | 父 → TIMEOUT |
retry_on_child_fail | 父继续 | Coordinator 重起一次 child(最多 N 次) | 降级为 abort |
fallback_on_child_fail | 父继续 | 父收到 fallback observation 继续 | 父 → TIMEOUT |
collect_all(仅 Round 1 协作群) | 计入 N 个结果之一 | 计入但标记失败 | 计入但标记 timeout |
成本聚合:父 Run 终态前,Coordinator 把所有挂在该 parent_run_id 下的 child Run cost 累加到父 RunTrace 的 delegation_total_cost 字段;父自身 cost 仍单独保留。同一棵协作树的 cost 等于 parent.cost + parent.delegation_total_cost。
3.4 防滥用边界
| 威胁 | 缓解 |
|---|---|
| 无限委托链 A → B → A → B → ... | Coordinator 检测 cycle(在协作树深度 + agent_id 重复路径);超过 max_delegation_depth(默认 5)拒绝 |
| 大流量推给 child 把成本推爆 | 父 budget 透传给 child;child 自身的 budget = min(parent.remaining, child.config);超 budget 直接 REJECTED |
| 跨 User / 跨 Workspace 委托 | Coordinator 在起 child 前校验 user / workspace 一致;不一致直接拒绝 |
| context_subset 漏 PII | Coordinator 按 schema 白名单裁剪;不在白名单的字段不进 child invocation |
4. Round 机制
说明 Round 0 / 1 / 2 在 Tier 2 Coordinator 的语义和推进规则,明确每一轮的输入、输出、终止条件。
4.1 Round 0 · 规划
触发:Tier 1 收到 @ 协作群消息,或 Parent Run 内部决定 fan-out 时。
承担方:Coordinator 进程(不起 Run)。
| 输入 | 动作 | 输出 |
|---|---|---|
| 用户消息 + group_session 或 team_id | 展开成员列表(按 Team 配置或 mention 命中) | members 数组 |
| Team.collaboration_strategy | 选择本次走 fan-out / sequential / lead-driven | strategy 枚举 |
| 用户消息 + Team.config | 构造每个 child 的 task_brief 与 budget | N 份 AgentInvocation |
终止条件:N 份 AgentInvocation 全部构造完成 → 进入 Round 1。Round 0 失败(如成员展开为空、策略不存在)直接把 Parent 标为 FAILED 不进 Round 1。
4.2 Round 1 · 执行
承担方:JobDispatcher 把 N 个 invocation 入队,每个生成一个独立 child Run;Tier 3 内核完成单 Run 八步执行。
| strategy | 调度模式 | 失败处理 |
|---|---|---|
fan-out-broadcast | N 个 child 并行入队,全部 ResultSink 收齐进 Round 2 | 父策略 = collect_all:失败计入但标记 |
sequential-relay | 按 position 顺序起 child;前一个 ResultSink 出来再起下一个 | 任意一棒失败:链熔断;父按 abort_on_child_fail 处理 |
lead-driven | 先起 lead Run 拆子任务;lead 内部再触发 fan-out | lead 失败 = 整个 Round 1 失败;executor 失败按 collect_all 收 |
终止条件:所有 child 都到达终态(SUCCEEDED / FAILED / REJECTED / TIMEOUT)→ 进入 Round 2 或直接终态聚合。
4.3 Round 2 · 汇总(可选)
触发:Team.config.consolidation_required = true(fan-out 默认 true、sequential 默认 false)。
承担方:另起一个独立的 Consolidator Run,挂在同一个 parent_run_id 下。Consolidator 的 callee_agent_id = Team 的 lead 或一个专用 consolidator persona。
| 输入 | 动作 | 输出 |
|---|---|---|
| Round 1 的 N 份 ResultSink observation | Consolidator 走完整 Harness 八步:装配 context 时把 N 份 observation 一起塞进去 | 整合后的统一输出(按 expected_output schema) |
关键差异(对比"在 Coordinator 进程内做整合"):
- Consolidator Run 走完整八步,能被 Policy / HITL 拦截
- Consolidator 写自己的 RunTrace,整合过程可回放
- Consolidator cost 单独统计,父总成本透明
- Consolidator 失败可重试,不污染 Round 1 已完成的 child
终止条件:Consolidator 终态 → ResultSink 回填 Parent → Parent 终态聚合。
4.4 Round 计数与内核解耦
round_index 是 Coordinator 维护的字段,存在 GroupSession 与 DelegationTrace 表里;不进 RunState 也不进 RunContext.run_metadata 的决策路径。Tier 3 内核每次只看到「起一个独立 Run」,不知道它属于哪个 Round。这是 D4(Round 0/1/2)+ D3(执行边界独立)+ Tier 3 不感知协作的复合约束。
5. ResultSink 与 AnswerObservationProtocol
定义 Tier 3 child Run 终态如何把结果交回 Tier 2 Coordinator。这是协作树唯一的「子 → 父」语义出口。
5.1 ResultSink · Tier 3 的唯一交付点
每个 child Run 在终态收口阶段(Harness 第八步 TRACING 之前)必经 ResultSink。ResultSink 是 Coordinator 向 Tier 3 注入的回调钩子(不是新协议层),它从 RunContext 拿到三件事:
| 字段 | 来源 | 用途 |
|---|---|---|
terminal_state | RunState | SUCCEEDED / FAILED / REJECTED / TIMEOUT |
final_answer | Plan/Tool/HITL 终态产物 | 核心输出(文本 / 结构化 / 工具结果摘要) |
run_summary | RunTrace 聚合 | cost / latency / token 用量 / HITL 路径 |
ResultSink 的写入原子地完成两件事:写 child 自己的 RunTrace 终态行 + 给 Coordinator 投递一条 child_terminal 消息(带 parent_run_id 做路由)。Coordinator 收到消息后才推进 Round 状态。
没有 ResultSink 就没有终态——这是协作协议对 Tier 3 唯一的硬约束。
5.2 AnswerObservationProtocol · 父侧消费协议
Coordinator 收到 child_terminal 后,把 final_answer 包装成一个 AnswerObservation 投回 Parent Run 的 RunContext。Parent 把它当作普通的 observation 继续 Plan/Execute 循环。
| 字段 | 类型 | 说明 |
|---|---|---|
kind | enum | 固定值 agent_invocation_result,让 Parent 的 Plan 能识别 |
invocation_id | uuid | 对应 Parent 当初发出的 AgentInvocation |
callee_agent_id | uuid | 哪个 Agent 回的 |
terminal_state | enum | 透传 child 终态 |
answer_payload | jsonb | final_answer 的结构化版本(如 expected_output 指定,按 schema 校验) |
cost_cents / latency_ms | int | 用于父侧聚合 |
error | jsonb? | FAILED / REJECTED 时的错误结构 |
5.3 写回路径
| 步骤 | 动作 | 位置 |
|---|---|---|
| 1 | Child Run 进入终态收口 | Tier 3 · Harness 第七步前 |
| 2 | ResultSink 原子写 RunTrace 终态 + 投递 child_terminal 消息 | Tier 3 ↔ Tier 2 边界 |
| 3 | Coordinator 路由消息:按 round_index 决定推进 Round 1 / Round 2 / 父终态 | Tier 2 · Coordinator |
| 4 | Coordinator 写 DelegationTrace 终态行(cost / latency 落表) | Tier 2 · 持久化 |
| 5 | Coordinator 把 AnswerObservation 投回 Parent RunContext | Tier 2 → Tier 3 |
| 6 | Parent 的 AgentInvoke Tool 调用收到返回值(observation),工具控制器解阻塞,EXECUTING 继续 Plan | Tier 3 · Parent Run |
5.4 失败语义
- child 走 ResultSink 写入
terminal_state = FAILED不算失败——Parent 收到的就是「子失败」的合法 observation - child 没走 ResultSink 就退出(进程崩溃 / 沙箱挂掉)= 真失败:Coordinator 通过 watchdog 检测 RunTrace 终态行缺失,强制写 TIMEOUT,触发父策略
- Coordinator 自身崩溃 = Round 推进卡住,但所有持久化字段(DelegationTrace + GroupSession.round_state)都已落表,重启后能恢复
6. 与其他模块关系
说明本协议引入后已有模块的修改点,避免双方对边界产生分歧。
| 模块 | 修改点 | 边界对齐 |
|---|---|---|
| Agent Teams 设计 | Team.collaboration_strategy 由本协议消费;Team.config 提供 max_round / consolidation_required / hitl_threshold 等运行时阈值 | Team 是「协作单元的承载实体」(名词),本协议是「机制」(动词),互不替代 |
| Harness 内核设计 | 工具调用控制器新增 AgentInvoke 第 4 类调用(与本地 Skill / 沙箱 Skill / LLM 并列);RunContext.caller_extra 接收 parent_run_id;ResultSink 作为 Tier 3 收口前的强制钩子 | RunState 仍是 11 态、内核仍只跑单 Run;不引入 Round / Delegation 概念,"协作"对 T3 只表现为一种慢 Tool 调用 |
| Memory / Context 设计 | 父 → 子的 context_subset 走 schema 白名单裁剪;不直接转发 RunContext;team_memory 召回路径不动 | Memory scope 仍按 (user, persona, team) 三维隔离;本协议不引入新 scope |
| 可观测性 / Replay | RunTrace 增加 parent_run_id / delegation_total_cost 字段;新增 DelegationTrace 表;Replay 支持按 parent_run_id 拉整棵树 | 所有 trace 字段对齐 RunTrace 现有 schema 风格,不破坏既有查询 |
| 治理 / HITL | HITL 阈值可在 Team.config 覆盖;Consolidator Run 也走 HITL 通道(Round 2 与 Round 1 共享同一套 Policy) | HITL 仍是 Tier 2 的策略层 + Tier 3 的等待态,不为协作再造一套 |
| Skill 版本治理 | child Run 的 Skill 集合按 callee_agent_id 装配;不继承父的 Skill;context_subset 不带父的 Skill 调用结果(除非显式 expected_output 包含) | Skill 层与父子无关,仍按 Agent 维度隔离 |
核心契约不变量(评审请确认这五条):
- 所有 Agent 间派单走 AgentInvocation 单一入口(D1)
- 每次派单产生独立 child Run + 独立 RunState(D2 + D3)
- Round 0/1/2 是 Tier 2 概念,Tier 3 不感知(D4)
- 所有 child 的终态必经 ResultSink(D5)
- 跨 User / 跨 Workspace 委托永久禁止;context 严格按 schema 白名单裁剪