@swifty.js/swifty 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/ch15.md DELETED
@@ -1,547 +0,0 @@
1
- # Agent Team
2
-
3
- ## Why
4
-
5
- ```bash
6
- brew install --cask iterm
7
- brew install tmux
8
- ```
9
-
10
- ### 解决的问题: 多 agent 通信
11
-
12
- - subagent 是星型拓扑, 主 agent 在中心, subagent 在边缘, subagent 只能和主 agent 通信, subagent 不能相互通信, subagent 的任务执行结束后, 结果加入主 agent 的上下文窗口
13
- - agent team 是网状拓扑, 每个 teammate 有自己的上下文窗口, teammate 可以相互发送邮件
14
- - 多 agent 协调能力: 基于共享的任务列表和每个 agent (leader + teammate) 独立的邮箱 (使用「收件箱」更准确)
15
- - 发送邮件不是使用网络传输, 而是使用邮箱 + 500ms 轮询, leader 和 teammate 可以在下一轮 agent loop turn 开始时收到邮件
16
-
17
- ### subagent 模式和 agent team 模式
18
-
19
- - subagent 模式: subagent 对话历史不会被持久化到磁盘, 适用于一次性的、边界清晰的小型任务
20
- - agent team 模式: teammate 对话历史会被持久化到磁盘, 适用于可以拆解为多个子任务的大型任务, 例如重构 4 个相互依赖的模块
21
-
22
- ### 对比 AutoGen, CrewAI, LangGraph
23
-
24
- _中心化编排_
25
-
26
- - AutoGen 的 GroupChat 使用 GroupChatManager, 按策略决定 agent 发言顺序
27
- - CrewAI 使用 Crew 对象编排 agent 和任务调度
28
-
29
- _有向图状态机_
30
-
31
- - LangGraph 将多 agent 协调建模为有向图状态机
32
-
33
- _去中心化协调_
34
-
35
- > 共享任务列表持久化到单个 jsonl 文件: `${workDir}/.swifty/tasks/${sessionId}.json`
36
-
37
- - Swifty 将多 agent 协调能力以工具的形式注入到每个 teammate 的工具集, agent 自己查看共享任务列表、自己从独立的邮箱中接收邮件
38
- - 代价: 可预测性较低
39
- - 收益: 新增协调模式只需要修改/新增工具, 不需要复杂的调度器
40
-
41
- ## 数据结构
42
-
43
- ```ts
44
- class Team {
45
- name: string;
46
- mode: "tmux" | "iterm" | "in-process"; // 运行后端, team 级别
47
- members: Map<string /** teammate name */, Member>;
48
- leadMailbox: FileMailbox; // leader 的邮箱
49
-
50
- // agent team 的邮箱目录: ${workDir}/.swifty/teams/{name}/
51
- // 每个 teammate 的邮箱对应一个 jsonl 文件
52
- // lead.jsonl # leader 的邮箱
53
- // lead.read # leader 的读指针
54
- // jane.jsonl # teammate jane 的邮箱
55
- // jane.read # jane 的读指针
56
- // john.jsonl # teammate john 的邮箱
57
- // john.read # john 的读指针
58
- mailboxDir: string;
59
- workDir: string;
60
-
61
- // 源码未实现
62
- configPath: string;
63
- }
64
-
65
- interface Member {
66
- name: string; // teammate name
67
- // true: teammate 活跃, agent looping...
68
- // false: 收到 shutdown_request、或 leader 调用 stop 方法、或崩溃
69
- active: boolean; // true 活跃, false 空闲
70
- cancel?: () => void; // 取消函数
71
- mailbox: FileMailbox; // teammate 独立的邮箱
72
- uiState?: TeammateUIState;
73
- conversation?: ConversationManager;
74
-
75
- // 源码未实现
76
- agentId: string; // teammate 对应的 agent ID
77
- agentType: string; // agent 角色
78
- model: string; // 模型
79
- worktreePath?: string; // worktree 路径, 可选
80
- backendType: "tmux" | "item2" | "in-process"; // 运行后端
81
- planModeRequired: boolean; // teammate 是否使用 plan 模式, 需要 leader 审批
82
- }
83
- ```
84
-
85
- <!-- 源码差异: 源码中 mode 是 team 级别, 不是 member 级别, 一个 team 中所有 teammate 的运行后端相同 -->
86
-
87
- - mode: 运行后端 (team 级别, 一个 team 中所有 teammate 的运行后端相同)
88
- - tmux, iterm: teammate 是 tmux/iterm pane 中的独立进程, 和 leader 完全隔离, 隔离性强
89
- - in-process: teammate 和 leader 运行在同一个进程, 隔离性弱, 但更轻量
90
- - 源码差异: 期望 mode 是 member 级别的, 一个 team 中, teammate 可以选择运行在独立进程, 也可以选择和 leader 运行在同一个进程, 运行后端由每个 teammate 自己选择
91
- - worktree: 可选, leader spawn 一个 teammate, 指定 teammate `isolation: "worktree"` 时, 为 teammate 创建独立的 worktree, 文件系统隔离; 否则 teammate 和 leader 共享工作目录
92
- - planModeRequired: 可选, leader spawn 一个 teammate, 指定 teammate `planModeRequired: true` 时, teammate 使用 plan 模式, 该 teammate 执行写操作或 Bash 命令前, 必须先提交 plan 给 leader 审批, 审批通过后才能执行写操作或 Bash 命令 (teammate 是 plan 权限模式: 读放行、写确认、shell 命令确认, 确认方是 leader)
93
- - team leader: 即主 agent, 主 agent 创建 agent team 后自动成为 team leader, 负责创建 team、spawn teammate, 拆解任务, 协调进度
94
- - teammate: 任务执行者, 每个 teammate 是一个独立的 agent 实例, 有自己的上下文窗口和工具集, teammate 可以是预定义的 (指定 subagent_type, 预定义的 subagent 角色, 无对话历史), 也可以是 fork 的 (不指定 subagent_type, 继承 leader 的完整对话历史) <!-- TODO: 源码未实现 -->
95
- - agent team 的邮箱目录: ${workDir}/.swifty/teams/{name}/, 每个 teammate 的邮箱对应一个 jsonl 文件和一个读指针
96
-
97
- ## Agent Team 引入独立的顶层工具
98
-
99
- - TeamCreate 工具: 创建 team
100
- - name: 如果存在同名 team, 则自动在 name 后面追加序号避免冲突 <!-- TODO: 源码未实现 -->
101
- - description: 可选 <!-- TODO: 源码未实现 -->
102
- - agent_type: 可选 <!-- TODO: 源码未实现 -->
103
- - Agent 工具: 向已有的 team 中 spawn 一个 teammate
104
- - team_name: 指定 team
105
- - SpawnTeammate 工具: 独立的 teammate 创建工具, 支持 team 不存在时自动创建
106
- - team: team 名称, team 不存在时自动创建
107
- - name: teammate 名称
108
- - task: 分配给 teammate 的任务描述
109
- - TeamDelete 工具负责删除 team
110
- - team: team 名称
111
-
112
- TeamCreate 工具做了什么?
113
-
114
- 1. 创建 team.json 配置文件 <!-- TODO: 源码未实现 -->
115
- 2. 检测运行后端: in-process, tmux, iterm
116
- 3. 注册 leader (主 agent 自己) 到该 team
117
- 4. leader 拆解任务, 创建共享任务列表
118
-
119
- ```js
120
- // 创建 team
121
- TeamCreate.prototype.execute({
122
- name: "migrate-react-app",
123
- description: "迁移 webpack 到 rsbuild、antd 到 shadcn",
124
- });
125
-
126
- // 调用 SpawnTeammate 工具 spawn 一个 teammate `jane`
127
- SpawnTeammate.prototype.execute({
128
- team: "migrate-react-app",
129
- name: "jane",
130
- task: "迁移 webpack 到 rsbuild",
131
- });
132
-
133
- // 调用 Agent 工具 (team_name 参数) spawn 一个 teammate
134
- // teammate 名称由 description 自动生成
135
- Agent.prototype.execute({
136
- // subagent_type: "general-purpose",
137
- team_name: "migrate-react-app",
138
- description: "迁移 antd 到 shadcn",
139
- prompt: "迁移 antd 到 shadcn",
140
- });
141
- ```
142
-
143
- ## 三种运行后端
144
-
145
- > 源码 `detectBackend()` 默认返回 "in-process", 除非用户显式配置 teammate mode, 才调用 `detectPaneBackend()` 检测 tmux/iterm
146
-
147
- - Swifty 支持 3 种运行后端: tmux / iterm / in-process
148
- - tmux 和 iterm: teammate 是 tmux/iterm pane 中的独立进程, 是 pane 后端
149
- - in-process: teammate 和 leader 运行在同一个进程, 是进程内后端
150
-
151
- 如果有 tmux/iterm, 则 teammate 是 tmux/iterm pane 中的独立进程
152
-
153
- - 一个 teammate 崩溃不会影响 leader 和其他 teammate
154
- - 只有 leader 可以 spawn teammate
155
- - tmux/iterm pane 的 teammate 可以调用 Agent 工具, 即可以 spawn subagent, 但是不能 spawn teammate
156
- <!-- TODO: 源码中, teammate 不能调用 Agent 工具 -->
157
-
158
- 如果没有 tmux/iterm, 则 fallback 到 in-process 进程内后端, teammate 和 leader 运行在同一个进程, 但是有独立的工具集
159
-
160
- in-process 更轻量, 但是:
161
-
162
- - teammate 的生命周期绑定 leader, leader 退出, 所有 in-process 的 teammate 都退出
163
- - in-process 的 teammate 可以调用 Agent 工具, 但只能 spawn 同步 subagent, 禁止 spawn 后台异步 subagent、禁止 spawn teammate
164
- <!-- TODO: 源码中, teammate 不能调用 Agent 工具 -->
165
-
166
- ## 协调机制
167
-
168
- - subagent 的子 agent 通过 TaskManager 管理后台异步任务
169
- - agent team 给 teammate 额外注入一组任务协调工具, 使得 teammate 间可以创建任务、同步进度、相互发送邮件
170
- - 任务管理工具: TaskCreate、TaskGet、TaskList、TaskUpdate
171
- - 通信工具: SendMessage, 使得 leader/teammate 间可以相互发送邮件
172
-
173
- ```js
174
- export const IN_PROCESS_TEAMMATE_ALLOWED_TOOLS = new Set([
175
- "TaskCreate", // 创建新任务
176
- "TaskGet", // 查看任务详情
177
- "TaskList", // 列出所有任务
178
- "TaskUpdate", // 更新任务状态, 包括 addBlocks, addBlockedBy 依赖字段
179
- "SendMessage", // 向 teammate 发送邮件
180
- // ...
181
- ]);
182
- ```
183
-
184
- - teammate 和 leader (主 agent) 都有 SendMessage 工具
185
- - subagent 没有 SendMessage 工具
186
-
187
- ## SendMessage 工具
188
-
189
- SendMessage 工具使得 leader/teammate 间可以相互发送邮件
190
-
191
- ```js
192
- SendMessage.prototype.execute({
193
- team: "migrate-react-app",
194
- to: "john",
195
- summary: "接口签名变更通知", // summary, 作为 UI 中的邮件预览, 源码未实现
196
- message: "接口 SwiftyConfig 的签名已变更, 新增 agentTeam 字段",
197
- });
198
- ```
199
-
200
- - summary: 作为 UI 中的邮件预览
201
- - to: 支持两种寻址 <!-- 源码未实现 -->
202
- - teammate 名称或者 agentId
203
- - \* 广播, 发送给所有 teammates
204
-
205
- SendMessage 也支持结构化邮件: <!-- 源码未实现 -->
206
-
207
- <!-- TODO: shutdown_request 只有 leader 可以发送 shutdown_request 结构化邮件 -->
208
-
209
- - shutdown_request: 请求某个 teammate 优雅退出, 目标 teammate 可以响应 shutdown_response 表示同意或拒绝
210
- - shutdown_response: shutdown_request 请求的响应, 包含 approve 或 reject 的原因, 只能发送给 leader
211
- - plan_approval_response: teammate 写操作 plan 的审批响应, 包含 approve 或 reject + feedback, 只有 leader 可以发送
212
-
213
- 协议化通信避免 teammate 间通过理解自然语言协调生命周期、权限审批的模糊性
214
-
215
- ## plan 审批 <!-- 源码未实现 -->
216
-
217
- 允许 leader 指定 teammate `planModeRequired: true`, teammate 使用 plan 模式,该 teammate 执行写操作前, 必须先提交 plan 给 leader 审批
218
-
219
- 1. teammate 分析任务、生成 plan file 执行计划
220
- 2. plan file 执行计划通过邮箱发送给 leader
221
- 3. leader 审批后, 使用 plan_approval_response 结构化消息响应审批结果
222
-
223
- - approve: 同意
224
- - reject + feedback: 拒绝 + 反馈
225
-
226
- 4. 审批通过后, teammate 继承 leader 的权限模式, 例如 leader 是 default 权限模式 (读放行, 写确认, shell 命令确认), 审批通过后 teammate 也会切换到 default 权限模式
227
-
228
- ## 邮件路由 <!-- 源码未实现 -->
229
-
230
- `Team.members: Map<string, Member>`: teammate 注册表, 保存 teammate 的名称到 teammate 实例 (Member 对象) 的映射, 使得 SendMessage 工具可以根据 teammate 名称拿到 teammate 实例, 将邮件发送给目标 teammate
231
-
232
- 1. tmux/iterm 后端: 写入邮箱, 通过 tmux/iterm send-keys 唤醒目标 tmux/iterm pane
233
- 2. in-process 后端: 只写入邮箱
234
-
235
- teammate 每轮 agent loop turn 开始时, 从邮箱中读邮件, 使用 `<system-reminder />` 标签包裹, 注入到 user 消息, teammate 在下一轮 agent loop turn 中可以看到该邮件
236
-
237
- > 并发写邮箱会不会导致并发冲突?
238
-
239
- - 每个邮箱 (jsonl 文件) 有一个 .lock 锁文件, 读/写前先使用 O_CREAT | O_EXCL `openSync(lockfile, "wx")` 尝试获取锁, 如果获取锁失败, 则使用 5ms-100ms 随机抖动重试, 最多重试 10 次, 重试 10 次后放弃, 防止「雪崩」
240
- - 一个进程占有锁最多 10s, 如果超过 10s 还未释放锁 (即删除 .lock 锁文件), 则判断为 stale 过期并清理, 防止某个进程崩溃导致死锁
241
-
242
- 1. O_CREAT: 如果文件不存在, 则创建
243
- 2. O_CREAT | O_EXCL (exclusive): 如果文件已存在, 则 open 调用立刻失败, 返回 EEXIST (Error: EXISTs) 错误
244
- 3. 这里的「雪崩」指的是 thundering herd problem 惊群问题:
245
- - 进程 A 占有锁, 进程 B、C、D 等待
246
- - 进程 A 释放锁 (即删除 .lock 锁文件) 的瞬间, 进程 B、C、D 同时发现锁可用、同时竞争锁
247
- - 只有一个进程可以成功, 其他进程失败后重试: 每次释放锁都会导致大量进程同时竞争、大量失败、大量重试, 浪费系统资源
248
-
249
- ## Agent Team 的生命周期
250
-
251
- 如果 Swifty 判断一个任务值得创建一个 agent team 来做, 则有以下步骤
252
-
253
- 1. 创建 team.json 配置文件, 检测运行后端 (tmux, iterm, in-process), 注册 leader (主 agent 自己) 到该 team
254
- 2. leader 拆解任务: 子任务的先后依赖、子任务是否可以并发执行
255
- 3. leader spawn 一或多个 teammate, 如果 leader 指定 teammate `isolation: "worktree"`, 则为 teammate 创建独立的 worktree; spawn 方式取决于运行后端 (tmux/iterm/in-process)
256
- 4. spawn 一个 teammate 有 2 种模式 (和 subagent 相同)
257
-
258
- - 预定义的 teammate: 指定 subagent_type, 预定义的 subagent 角色, 无对话历史
259
- - fork 的 teammate: 不指定 subagent_type, 继承 leader 的完整对话历史
260
-
261
- ```js
262
- TeamManager.prototype.create(name) {
263
- const team = new Team({
264
- name, // agent team name
265
- mode: detectPaneBackend() || detectBackend(), // "tmux" | "iterm" | "in-process"
266
- members: new Map(), // teammate 注册表
267
- mailboxDir: `${workDir}/.swifty/teams/${name}`, // agent team 的邮箱目录
268
- leadMailbox: new FileMailbox(mailboxDir, "lead"), // leader 的邮箱
269
- // 源码未实现
270
- leaderAgentId,
271
- configPath: `~/.swifty/teams/${sanitize(name)}/config.json`,
272
- });
273
- // 源码未实现
274
- writeFileSync(team.configPath, JSON.stringify(team));
275
- return team;
276
- }
277
- ```
278
-
279
- 例如一个大型任务, 可以拆解为 A、B、C、D 4 个子任务
280
-
281
- - task A 和 task B 没有依赖
282
- - task C 需要等待 task A 完成
283
- - task D 需要等待 task B 完成
284
-
285
- 同时创建任务依赖图和使用自然语言描述
286
-
287
- ### 任务依赖图
288
-
289
- 对于复杂场景, 使用 `addBlocks`, `addBlockedBy` 字段创建结构化的任务依赖图, teammate 调用 TaskList 工具可以查看共享任务列表; 调用 TaskGet 工具可以看到每个子任务的依赖, 哪些子任务可以接取、哪些子任务被阻塞
290
-
291
- ```js
292
- // task C 被 tas kA 阻塞
293
- TaskUpdate.prototype.execute({
294
- taskId: "C",
295
- addBlockedBy: ["A"],
296
- });
297
-
298
- // task B 阻塞 task D
299
- TaskUpdate.prototype.execute({
300
- taskId: "B",
301
- addBlocks: ["D"],
302
- });
303
- ```
304
-
305
- ### 自然语言描述
306
-
307
- 对于简单场景, leader 可以直接将依赖关系写到任务描述, 例如 task C 需要等待 task A 完成, task D 需要等待 task B 完成; teammate 阅读任务描述, 判断执行顺序
308
-
309
- ### spawn 一个 teammate 的流程
310
-
311
- 1. 如果是预定义的 teammate, 则读取 markdown 配置
312
- 2. 如果 leader 指定 teammate `isolation: "worktree"`, 则为 teammate 创建独立的 worktree, leader 创建的 worktree: `team-${teamName}/${teammateName}`
313
- 3. agent team 给 teammate 额外注入一组任务协调工具
314
- 4. 根据运行后端 (tmux/iterm/in-process), spawn 一个 teammate
315
- 5. 将 teammate 的名称注册到 Team.members
316
- - `Team.members: Map<string, Member>`: teammate 注册表, 保存 teammate 的名称到 teammate 实例 (Member 对象) 的映射
317
- - SendMessage 工具可以根据 teammate 名称拿到 teammate 实例, 将邮件发送给目标 teammate
318
- 6. 向 system prompt 中追加 team 通信协议, 告诉 teammate: 纯文本响应对其他 teammate 不可见, 必须调用 SendMessage 工具进行通信
319
-
320
- ```md
321
- IMPORTANT: You are running as an agent in a team.
322
- Just writing a response in text is not visible to others on your team.
323
- You MUST use the `SendMessage` tool. The user interacts primarily with the team lead.
324
- Your work is coordinated through the task system and teammate messaging.
325
- ```
326
-
327
- ## 执行任务
328
-
329
- teammate (任务执行者) 的工具集包括:
330
-
331
- - 任务协调工具
332
- - 共享任务工具: TaskCreate、TaskGet、TaskList、TaskUpdate, 提供任务管理能力
333
- - 通信工具: SendMessage, 使得 leader/teammate 间可以相互发送邮件
334
- - 任务实施工具: ReadFile、WriteFile、Bash ...
335
-
336
- <!-- TODO: 真的是使用 Agent 工具传递的吗? -->
337
-
338
- leader 调用 Agent 工具或 SpawnTeammate 工具传递 prompt 给 teammate 后 (Agent 工具和 SpawnTeammate 工具都可以 spawn 一个 teammate), teammate 进入自己的 agent loop
339
-
340
- 1. teammate 调用 TaskList 工具, 查看共享任务列表, 共享任务列表包含任务状态: 是否已完成、是否正在执行
341
- 2. 不是 leader 给 teammate 强制分配任务, 而是 teammate 基于自己的上下文选择任务、接取任务
342
-
343
- ## 收集结果
344
-
345
- 共享任务列表中的所有任务都 completed 后, leader:
346
-
347
- - 如果 teammate 使用了 worktree 文件隔离, 则需要合并到主分支
348
- - 如果 teammate 共享工作目录, 则不需要合并
349
-
350
- leader 调用 Bash 工具执行 git 命令, 调用 ReadFile 工具查看冲突文件, 以确定 merge/rebase/cherry-pick 顺序和冲突解决策略 (回顾 ch14: 为什么 Swifty 没有将 merge/rebase/cherry-pick 作为内置工具?)
351
-
352
- leader 不确定冲突解决策略时, 调用 AskUserQuestion 工具弹出对话框让用户确认 (HITL)
353
-
354
- ## teammate 空闲和恢复
355
-
356
- 例如 teammate jane 完成任务后空闲, leader 整合、分析 jane 的执行结果后, 决定补一个 playwright 测试, 重新 spawn 一个新 teammate john 很浪费: john 没有 jane 的完整上下文
357
-
358
- ```js
359
- function onTeammateStop(teammate, leadMailbox) {
360
- // 标记 teammate 空闲
361
- teammate.active = false;
362
- const idleNotification = `[idle] ${teammate.name} (reason: some reason...)`
363
- // 发送 idle 通知到 leader 的邮箱
364
- await leadMailbox.send(name, idleNotification);
365
- }
366
- ```
367
-
368
- leader 每轮 agent loop turn 开始时, , 从邮箱中读邮件, 使用 `<system-reminder />` 标签包裹, 注入到 user 消息, leader 在下一轮 agent loop turn 中可以看到该邮件, 知道哪些 teammate 空闲, 以判断是新增任务, 还是收集结果, 还是等待 teammate 完成
369
-
370
- ```xml
371
- <system-reminder>
372
- <task-notification team="migrate-react-app">
373
- </task-notification>
374
- </system-reminder>
375
- ```
376
-
377
- teammate 空闲后, leader 可以调用 SendMessage 工具向 teammate 发送邮件, 如果发现该 teammate 空闲, 则使用磁盘上的会话日志 (参考 ch9 会话持久化) 重建, 恢复该 teammate 完整上下文继续工作
378
-
379
- ```js
380
- // teammate jane 完成任务后空闲
381
- // leader 整合、分析 jane 的执行结果后, 决定补一个 playwright 测试
382
- SendMessage.prototype.execute({
383
- to: "jane",
384
- message: "在 tests 目录下补一个 playwright 测试",
385
- });
386
-
387
- // 发现 jane 空闲, 使用磁盘上的会话日志重建
388
- // 恢复 jane 的完整上下文继续工作
389
- ```
390
-
391
- ### teammate 对比 subagent
392
-
393
- - teammate 是特殊的 subagent
394
- - teammate 对话历史会被持久化到磁盘
395
- - subagent 对话历史不会被持久化到磁盘
396
-
397
- ## 清理
398
-
399
- <!-- src/tui/app.tsx taskListRef -->
400
-
401
- leader 删除 teammate, 删除 worktree (如果有)、删除 team 目录、删除共享任务列表文件
402
-
403
- ## leader 的任务拆解策略
404
-
405
- leader 的任务拆解策略, 直接决定 team 的效率:
406
-
407
- - 拆解的太粗, 会导致子任务的工作量太大, 并发度低
408
- - 拆解的太细, 会导致子任务间的依赖过多, teammate 等待
409
-
410
- ### 任务拆解原则
411
-
412
- - 明确子任务间的依赖关系: leader 拆解任务时, 如果 task C 需要等待 task A 完成 (task C 依赖 task A 的输出), 则使用 `addBlocks`, `addBlockedBy` 字段创建结构化的任务依赖图, 同时将依赖关系写到任务描述; 不要指望 teammate 知道先做 task A 后做 task C
413
- - 按文件边界拆解子任务: 只读子任务 (例如探索代码库) 可以并行, 如果两个子任务同时修改同一个文件, 则很容易冲突; 尽量让不同的子任务操作不同的文件, 如果不可避免的 task A、task B 两个子任务修改同一个文件, 则可以:
414
- - 对 task A、task B 两个子任务建立依赖关系, 串行执行
415
- - leader spawn teammate 时指定 `isolation: "worktree"`, 为 teammate 创建独立的 worktree, 文件系统隔离
416
- - 控制每个 teammate 的子任务数: 每个 teammate 安排 2-4 个子任务 (经验值)
417
- - 安排一个验证任务: 所有子任务完成后, 验证结果: typecheck + lint、test、build
418
-
419
- ## 预防冲突
420
-
421
- 多个 teammate 并发修改, 很容易冲突, agent team 在三个层面预防冲突
422
-
423
- ### 第一层: 任务拆解
424
-
425
- 按文件边界拆解子任务: 只读子任务 (例如探索代码库) 可以并行, 如果两个子任务同时修改同一个文件, 则很容易冲突; 尽量让不同的子任务操作不同的文件, coordinator 模式通过限制工具集和 prompt 约束 leader 专注调度, 不写代码
426
-
427
- ### 第二层: 可选的 worktree 文件隔离
428
-
429
- 如果不可避免的 task A、task B 两个子任务修改同一个文件, 则可以:
430
-
431
- - 对 task A、task B 两个子任务建立依赖关系, 串行执行
432
- - spawn teammate 时指定 `isolation: "worktree"`, 为 teammate 创建独立的 worktree, 文件系统隔离
433
-
434
- ### 第三层: 收集结果
435
-
436
- 如果 teammate 使用了 worktree 文件隔离, 则需要合并到主分支; leader 调用 Bash 工具执行 git 命令, 调用 ReadFile 工具查看冲突文件, 以确定 merge/rebase/cherry-pick 顺序和冲突解决策略; leader 不确定冲突解决策略时, 调用 AskUserQuestion 工具弹出对话框让用户确认 (HITL)
437
-
438
- ## Coordinator Mode: 让 leader 专注调度, 不写代码
439
-
440
- 场景: 任务复杂、teammates 数量较多时, leader spawn 所有的 teammates 后, 如果不约束 leader, 则 leader 可能自己调用 WriteFile 工具写代码、自己调用 Base 工具执行命令 (leader 有完整的工具集)
441
-
442
- 回顾 Plan Mode: 只规划不做事, 通过 prompt 约束 LLM 行为
443
-
444
- Coordinator Mode: 只调度不做事, 通过限制工具集和 prompt 约束 leader 行为, coordinator 模式独立于 Agent Team, 进入 coordinator 模式后, 剥夺 leader 所有写代码工具 (WriteFile、EditFile), 注入 coordinator system prompt, 让 leader 专注调度, 不写代码
445
-
446
- > 对于复杂任务, 推荐配合使用 Agent Team 和 Coordinator Mode
447
- > Claude 是 Delegate Mode
448
-
449
- ```js
450
- // 源码未实现
451
- function isCoordinatorMode() {
452
- // 配置文件
453
- if (!Boolean(config.coordinator_mode)) {
454
- return false;
455
- }
456
-
457
- // 环境变量
458
- return Boolean(process.env.COORDINATOR_MODE);
459
- }
460
- ```
461
-
462
- ### 限制工具集
463
-
464
- 开启 coordinator 模式后, 限制 leader 的工具集 (排除 WriteFile、EditFile)
465
-
466
- - 调度工具
467
- - Agent: spawn 和管理 teammate
468
- - TeamCreate, TeamDelete: 管理 team
469
- - TaskCreate, TaskGet, TaskList, TaskUpdate: 管理任务
470
- - SendMassage: 向 teammate 发送邮件
471
- - 读操作工具
472
- - ReadFile, Glob, Grep: 任务拆解、review teammate 的任务执行结果
473
- - Bash: 收集结果时, 如果 teammate 使用了 worktree 文件隔离, 则需要执行 git 命令
474
-
475
- ```js
476
- const COORDINATOR_ALLOWED_TOOLS = new Set([
477
- "Agent", // spawn 和管理 teammate
478
- "SendMessage", // 向 teammate 发送邮件
479
- "TaskCreate", // 创建新任务
480
- "TaskGet", // 查看任务详情
481
- "TaskList", // 列出所有任务
482
- "TaskUpdate", // 更新任务状态
483
-
484
- "TeamCreate", // 创建 team
485
- "TeamDelete", // 删除 team
486
- "ListTeams", // 列出所有 team
487
- "SpawnTeammate", // spawn 一个 teammate
488
-
489
- "ReadFile", // 任务拆解, 整合、分析 teammate 的执行结果
490
- "Glob", // 同上
491
- "Grep", // 同上
492
- "Bash", // 收集结果时, 如果 teammate 使用了 worktree 文件隔离, 则需要执行 git 命令
493
- ]);
494
- ```
495
-
496
- ## Coordinator Workflow
497
-
498
- <!-- TODO: 源码中注入的 coordinator system prompt 在哪? -->
499
-
500
- coordinator 模式不仅限制工具集 (排除 WriteFile、EditFile), 还会注入 coordinator system prompt, 提示 leader 使用 coordinator 4 阶段工作流
501
-
502
- | 阶段 | 执行者 |
503
- | -------------- | -------------------------------------------------------------------------- |
504
- | Research | teammate 探索代码库, 可以并行 |
505
- | Synthesis | leader (coordinator 模式), 整合、分析 teammate 的探索/执行结果, 拆解子任务 |
506
- | Implementation | teammate 调用 Agent/SpawnTeammate 工具 spawn teammate 执行子任务 |
507
- | Verification | leader 收集结果、解决冲突、teammate 验证结果 |
508
-
509
- > 回顾 subagent 的 `<task-notification />` 通知机制
510
- >
511
- > fork 的 subagent 必须后台异步运行, 执行结束后使用 `<task-notification />` 标签包裹执行结果, 作为一条 user 消息注入到主 agent 的上下文
512
-
513
- 相同的, teammate 子任务执行结束后, 使用 `<task-notification />` 标签包裹执行结果, 作为一条 user 消息注入到 leader 的上下文
514
-
515
- `<task-notification />` 的完整结构
516
-
517
- ```xml
518
- <task-notification>
519
- <task-id>${agentId}</task-id>
520
- <status>${ "completed" | "failed" | "killed" }</status>
521
- <summary>Agent "Migrate webpack to rsbuild" completed</summary>
522
- <!-- 文本消息 -->
523
- <result>${ teammate 的执行结果 }</result>
524
- <usage>
525
- <total_tokens>N</total_tokens>
526
- <tool_uses>N</tool_uses>
527
- <duration_ms>N</duration_ms>
528
- </usage>
529
- </task-notification>
530
- ```
531
-
532
- leader 收到 jane (teammate) `<task-notification />` 通知后, 可以调用 SendMessage 工具通知 jane 继续
533
-
534
- ```js
535
- SendMessage.prototype.execute({
536
- to: "jane",
537
- message: "继续迁移 rsbuild 打包的拆 chunk",
538
- });
539
- ```
540
-
541
- ```
542
- leader spawn teammate
543
- -> teammate 探索代码库/执行任务
544
- -> leader 收到 teammate 的探索/执行结果
545
- -> leader 整合、分析 teammate 的探索/执行结果, 拆解子任务
546
- -> leader 恢复空闲的 teammate, looping...
547
- ```