第 16 章:Squad 与多 Agent 协作
16.1 Squad 解决的不是并行调用,而是责任编排
如果只想同时跑两个 Agent,创建两个 Task 就够了。Squad 额外解决:
- 一个目标应由谁拆解;
- 哪个成员适合哪类工作;
- 委派怎样真正触发 Worker;
- Worker 结果怎样唤醒协调者;
- 谁拥有 Parent Issue 状态;
- 如何阻止 Agent-to-Agent 自激回环;
- 人类怎样看到“Leader 已评估但无需动作”。
因此 Squad 的核心是持久化协作协议,而不是进程内 Multi-agent Loop。
16.2 数据模型
查询入口在 server/pkg/db/queries/squad.sql,HTTP Handler 在 server/internal/handler/squad.go。
Squad 包含:
- Workspace;
- Name、Description、Instructions、Avatar;
- Creator;
- Leader Agent;
- Archived 状态。
Squad Member 记录:
- Member Type;
- Member ID;
- Role;
- 加入时间。
Leader 永远是 Agent;成员可以通过类型化引用扩展。数据库和 Handler 都验证 Workspace Scope 与可管理权限。
16.3 管理权限
Workspace Owner/Admin 可管理全部 Squad;普通成员只能管理自己创建的 Squad。
这项规则同时体现在:
- API Mutation Gate;
- Response 的
can_manage; - UI 编辑按钮。
前端显示不是授权本身。Server 必须再次校验,避免只靠隐藏按钮保护成员增删与 Leader 变更。
16.4 Issue 的 Assignee 仍然可以是 Squad
Issue 保存 assignee_type=squad 与 Squad ID。真正排任务时:
- 读取 Squad;
- 解析当前 Leader;
- Task 的
agent_id指向 Leader; - 同时写
is_leader_task=true; - 写
squad_id保留业务归因。
这样执行平面仍只需要运行一个具体 Agent,控制平面却知道它代表哪个 Squad。
16.5 为什么 Task 同时要 Agent ID 和 Squad ID
只存 Squad ID,Daemon 不知道启动哪个 Runtime;只存 Leader Agent ID,又会丢失:
- Squad Briefing;
- Roster;
- Parent 状态所有权;
- Squad Activity;
- Autopilot 的 Squad 归因;
- 归档时的转移语义。
这是一种“执行主体”与“业务受托主体”分离。
16.6 Leader Briefing
squad_briefing.go 在 Claim 时把系统级协议追加到 Leader Instructions,包含三部分:
- Squad Operating Protocol;
- Squad Roster;
- 用户配置的 Squad Instructions。
Roster 会列:
- Leader 自己;
- 成员姓名、类型、Role;
- Agent Skills;
- 可直接复制的完整 Mention Markdown。
Archived Agent 会被跳过,避免 Leader 把工作交给退休成员。
16.7 Leader 的固定职责
协议要求 Leader:
- 读 Issue 和最新讨论;
- 按 Role/Skills 选择成员;
- 发一条简短、精确的委派 Comment;
- 用 CLI 记录
action、no_action或failed; - 委派后立刻停止;
- Worker 更新时重新评估;
- 只在有权限时维护 Parent 状态。
它明确禁止 Leader 默认亲自实现。Squad 的价值来自分工,Leader 直接做完会绕过整个协议。
16.8 Mention Markdown 是真正的调度指令
必须使用:
[@Name](mention://agent/<UUID>)
普通 @Name 只是文本,不会被 Mention Parser 解析,也不会创建 Worker Task。
Briefing 故意把 Roster 中的可复制语法与 Server 的 Mention Regex 保持一致。这是 Prompt 和 Parser 之间的协议,改变任一侧都要同步测试。
16.9 两种委派方式不可重复使用
Leader 可以:
- 在 Parent Issue 评论中 @Worker;或
- 创建一个已分配给 Worker、状态为 todo 的 Child Issue。
Assignment 本身会触发 Worker。如果又在 Parent @同一 Agent 做同一工作,就会创建两个并行 Task。
Briefing 因此要求二选一,防止“双重调度”。
16.10 委派血缘
Agent 创建 Comment 时,Server 可把当前 Task 写入 Comment 的 source_task_id。后续 Mention Task 从它恢复:
- Originator User;
- Autopilot Rule Owner;
- Squad/Leader 上下文;
- 是谁授权了这次 Agent-to-Agent 调用。
这条血缘使 Worker 的动作可以追溯到人类或规则,而不把 Leader Agent 自己当最终权限来源。
16.11 编辑 Comment 会重算血缘
血缘不能永久附着在一段可编辑文本上:
- Agent 在自己的当前 Task 中编辑自己的 Comment,可以重新 Stamp;
- 其他编辑者,包括 Admin,不能借原 Agent 的 Invoke 权限;
- 不满足条件时清空
source_task_id; - 后续 Deferred Reconcile 也必须 Fail Closed。
否则管理员改一段旧评论,就可能复活原 Autopilot 的调用权。
16.12 Worker 完成怎样唤醒 Leader
Worker 的结果通常落在 Issue Comment/Task Terminal Event。Server 识别:
- 这是 Squad Member;
- 它的工作来自某个 Squad/Leader 链;
- Leader 需要重新评估下一步。
系统再为 Leader 排一个 Follow-up Task。Leader 读最新活动,决定:
- 委派下一阶段;
- 要求返工;
- 升级给人类;
- 更新 Parent 状态;
- 记录 no_action。
Leader 不是在内存里等待 Worker;协作靠数据库对象和事件恢复。
16.13 no_action 为什么是数据
“什么都不做”若只体现在 Agent 没发 Comment,会有两种解释:
- Leader 正确评估后认为无需动作;
- Leader 根本没运行、崩溃或漏掉触发。
squad_no_action.go 让系统查询某个具体 Task 是否已记录 no_action。Timeline 因而能证明 Leader 完成了决策。
16.14 Self-trigger Guard
Agent 的 Comment 可能 @自己、@所属 Squad 或触发一条会回到自己的链。若每次都排 Task,会形成无限循环。
系统综合:
- Comment Author;
- Source Task;
- Target Agent/Squad Leader;
- 当前 Parent Task;
- 是否已有 Active Work;
- Trigger Outcome
来抑制 Self-trigger。抑制不能伪装成 queued;API 要返回明确的 blocked/deferred/reason。
16.15 Agent-to-Agent ACK 的噪声问题
Worker 可能只回复“收到”“正在做”,Leader 被唤醒后又回复确认,双方不断互相 @。
Briefing 要求:
- Routine Progress 无需 Comment;
- Leader 记录 no_action 后静默退出;
- 一轮只发一条委派;
- 委派后不继续执行。
Server Guard 负责硬边界,Prompt Protocol 负责降低语义噪声,两者缺一不可。
16.16 Deferred Assignee Fallback
Comment 路由可能先希望线程 Parent Agent 或 Mention Agent 处理,但目标当前已有 Active Task。系统可创建 Deferred Fallback:
- 在主目标处理完成后再判断是否还需 Assignee;
- 若已有响应,取消 Fallback;
- 若仍无人接管,Promotion 为 queued;
- 保留 Authority/Originator;
- 编辑或血缘失效时拒绝借权。
这避免同一评论立刻唤醒多个 Agent,也避免目标未响应后工作完全丢失。
16.17 Parent Issue 状态所有权
Leader Briefing 分两种:
Issue 确实分配给该 Squad
- 首轮可移到
in_progress; - Worker 工作期间保持;
- 全局目标达到后移到
in_review; done留给 Human Review 或 PR Merge Integration。
Squad 只是在别人的 Issue 被 @
- 不允许改状态;
- 可以回答、委派或升级;
- Assignee 仍拥有状态弧。
这阻止“被拉来咨询的 Squad”接管别人的 Issue。
16.18 Leader Task 不占用户本地目录
local_directory.go 明确让 Leader Task 跳过 local_directory Assignment 和 Path Mutex。
Leader 主要写 Comment/Child Issue,不应占着用户仓库锁,阻塞真正要写代码的 Worker。
Leader 仍有 Daemon 管理的执行环境以运行 Provider/CLI;后续 Resume 只允许复用有 Managed Provenance、正确 Workspace/Issue/Agent Marker 的 Workdir。
16.19 Quick Create 选择 Squad
Quick Create 在 Task 表形态上有自己的 Payload,但 Claim 会:
- 解析 Picker Squad;
- 执行实际 Leader Agent;
- 注入 Squad Briefing;
- 告诉 Agent 创建新 Issue 时默认 Assignee 必须是 Squad UUID,不是 Leader UUID。
否则创建出的 Issue 会永久归个人 Agent,失去 Squad 状态与后续协作语义。
16.20 Autopilot 选择 Squad
Autopilot 也可以 assignee_type=squad:
- Dispatch 时解析最新 Leader;
- Run 保存 Squad Attribution;
- Leader 以协调者身份执行;
- Admission Error 用“Squad Leader Agent”描述实际问题。
Squad 归档时,关联 Issue 和 Autopilot 会转给 Leader Agent,避免留下悬空 Assignee。
16.21 Private Agent 与 Squad
把 Private Agent 放进 Squad 不能自动向所有成员开放它:
- Squad 管理、查看 Roster、调用 Agent 是不同权限;
- Leader/Worker Trigger 要走统一 Invoke Gate;
- Autopilot Rule Owner 与 Manual Clicker 的权限分别校验;
- Task/Activity Visibility 不能因带 Squad ID 就跳过 Agent Privacy。
“团队成员关系”不是通用权限提升。
16.22 成员状态是派生视图
Squad Detail 可以展示成员当前状态,但数据来自:
- 静态 Squad Membership;
- Agent Archived/Runtime 状态;
- Active Tasks;
- 可能的等待状态。
SQL 使用 Member × Active Task 连接后再聚合。它不是 Squad Member Row 上一个可写 Status 字段。
16.23 一次标准协作
16.24 扩展协作逻辑的检查清单
- 谁是业务受托主体,谁是实际 Agent;
- Task 是否保留 Squad ID 与 Leader Flag;
- Briefing 是否说明状态所有权;
- Delegation 是否使用可解析 Mention;
- Source Task 血缘是否随编辑正确更新;
- 是否有 Self-trigger 与 Duplicate Work Guard;
- Worker Terminal 是否唤醒 Leader;
- no_action 是否持久化;
- Local Directory 是否会被协调任务占住;
- Private Agent 权限是否 Fail Closed;
- Squad Archive 后引用如何转移;
- Quick Create/Autopilot 是否保持 Squad 归因。
16.25 本章结论
Squad 的关键机制是:
- Leader 只负责协调;
- Mention/Assignment 产生持久 Worker Task;
- Source Task 保存授权和委派血缘;
- Worker 结果重新唤醒 Leader;
- no_action 把“已评估”变成事实;
- 状态所有权和 Self-trigger Guard 限制回环。
它把多 Agent 协作从一次模型内部对话,升级成可恢复、可审计的工作流。