我把十个任务拆给五个 agent 并行跑
五个 AI 队友协同工作
Mio 是我在做的一个 AI 伴侣 Telegram bot,五个不同性格的 AI 角色,每个角色有自己的人格配置文件、记忆系统和对话风格。功能已经跑起来了,但一直有三个大模块没做:多模态输入(语音、图片、视频)、增强版 onboarding(角色预览、自定义故事、用户画像采集),还有自拍生成。
这三个模块加起来的量不小:数据库迁移、新的 media 处理模块、Telegram 文件下载、五个角色的配置更新、onboarding 流程重写、system prompt 改造、selfie 生成管道,横跨三层架构、十几个文件。
这种量直接把 plan 扔给一个 agent 串行干是不行的,十个任务下来 context window 肯定撑不住;自己花半天把任务拆好一个一个写,也挺费劲。这次我用了 claude code 的 agent team。
agent team 是什么
简单说就是一个 lead agent 负责协调,多个 teammate agent 并行干活。每个 teammate 都是独立的 claude 实例,有自己的 context window,能读写文件,能发消息给 lead 汇报进度。
核心机制大概是这几个:TaskCreate / TaskUpdate 负责创建任务、设依赖关系、追踪状态。TeamCreate 负责把 teammate agent 拉起来、分配任务。SendMessage 是 lead 和 teammate 之间的消息通道。再加一个文件所有权:每个 teammate 只编辑分给自己的文件,别人的不碰。
它不是「把大任务拆小然后顺序执行」,是五个 agent 真的同时在写代码,各自 commit,lead 在中间协调依赖和调度。
十个任务,三批执行
我给 claude code 的输入是一份完整的实现计划:三个 step、九个子步骤,还有一张带依赖关系的实现顺序表。它先花了两分钟探索 codebase,然后把整个计划拆成了十个任务:
| # | 任务 | 依赖 |
|---|---|---|
| 1 | 数据库迁移(aboutUser, customStory 字段) | — |
| 2 | Media 处理模块(transcribe, vision, process) | — |
| 3 | Telegram 文件下载工具 | — |
| 4 | 接入 media 管道到 server | 2, 3 |
| 5 | 角色摘要 + 自定义故事 + UI | — |
| 6 | Onboarding 增强(故事流、用户画像采集) | 1 |
| 7 | aboutUser/customStory 注入 system prompt | 6 |
| 8 | /reonboard 支持切换角色 | 5, 6 |
| 9 | Selfie 生成模块 | — |
| 10 | Selfie 触发接入 | 9 |
这里面关键的是依赖关系。1、2、3、5、9 互相独立,可以同时开始;4 要等 2 和 3 都做完;6 要等 1;7 要等 6;8 要等 5 和 6;10 要等 9。就是一个典型的 DAG(有向无环图)。claude code 自己识别出来,这十个任务可以分三批并行执行。
十个有依赖关系的任务被识别成一张 DAG,按依赖解除的先后分三批并行执行,每批投入的 agent 数量递减
五个 agent 同时开工
第一波直接五个 agent 一起启动:
5 agents launched
├─ @schema-migrator → #1 DB migration
├─ @media-builder → #2 Media module
├─ @file-downloader → #3 Telegram file download
├─ @preset-designer → #5 Preset summaries + UI
└─ @selfie-builder → #9 Selfie module
每个 agent 拿到的任务描述里写清楚了要创建或修改哪些文件、参考哪些现有代码、具体的实现规格。更关键的是,每个 agent 只被允许编辑特定的文件:schema-migrator 只碰 schema.ts,media-builder 只碰 media/ 目录下的文件。文件所有权就是防止冲突的核心机制。lead 的显示面板会实时更新:
并行时每个 agent 只被允许编辑分给自己的文件,虚线隔开各自领地,伸手去碰别人文件会被拦下——文件所有权是防冲突的核心机制
┌─────────────────┬──────────────────────────────────────┬─────────┐
│ Agent │ Task │ Status │
├─────────────────┼──────────────────────────────────────┼─────────┤
│ schema-migrator │ #1 - DB migration │ Running │
│ media-builder │ #2 - Media module │ Running │
│ file-downloader │ #3 - Telegram file download │ Running │
│ preset-designer │ #5 - Preset summaries + UI │ Running │
│ selfie-builder │ #9 - Selfie module │ Running │
└─────────────────┴──────────────────────────────────────┴─────────┘
lead 到底在干嘛
跑起来之后我才看明白 lead 这个角色具体在干嘛。第一个完成的是 file-downloader,任务简单,一个文件就搞定了。然后 schema-migrator 也完成了。这时候 lead 做了一个判断:#1 完成了,#6(onboarding 增强)的依赖就解除了,立刻把第六个 agent 拉起来:
● Task #1 done. That unblocks #6. Spawning onboarding-enhancer now.
这个过程不需要我干预。lead 会一直追踪依赖图,每完成一个节点,就检查哪些被阻塞的任务现在可以开始了。后面都是同一个逻辑:selfie-builder 做完 #9,lead 立刻启动 selfie-wirer 去做 #10;media-builder 做完 #2,加上之前已经完成的 #3,lead 就启动 media-wirer 去做 #4。
整个过程一共推进了三波:第一波并行跑 #1、#2、#3、#5、#9,五个 agent;第二波依赖解除后跑 #4、#6、#10,三个 agent;第三波跑 #7 和 #8,两个 agent。
并行的代价:噪声
并行也有代价。这个项目配了 git hooks,规则是 agent 空闲但工作目录里有未提交的文件时,hook 会触发警告。麻烦的地方在于,五个 agent 同时在写代码,每个 agent 只提交自己的文件,工作目录里永远有别人还没提交的文件。于是 hook 开始对已经干完活的 agent 反复报警:
@file-downloader❯ Hook blocks me — uncommitted files belong to other teammates
@schema-migrator❯ Idle — uncommitted files belong to other teammates
lead 的判断是误报。它没有让 agent 去提交不属于自己的文件,也没有把 hook 关掉。它识别出这是并行工作不可避免的代价,选择忽略,继续等活跃的 agent 干完。
服务器掉线,teammate 自己把活干完了
中间还出了一件挺有意思的事。OAuth token 过期,服务器断连了。
❯ continue on this task, server was down
⎿ API Error: 401 — OAuth token has expired
我重新登录之后,lead 做的第一件事是跑 git status 和 git log。工作目录是干净的,说明在服务器掉线期间,最后两个 agent(prompt-wirer 和 reonboard-builder)自己把活干完并且提交了。
这是 agent team 一个很有意思的属性:teammate 是独立运行的,不依赖 lead 的轮询或者确认来完成工作。lead 掉线了,teammate 继续干;lead 回来,活已经干完了。
最终整合
十个任务全部完成后,lead 开始收尾。先跑 pnpm typecheck,发现两个 TS18048 错误(ctx.callbackQuery 可能为 undefined),直接修掉。再跑 pnpm test,确认没有新的测试失败。然后过了一遍代码,检查所有新文件是否存在、导出是否正确。最后又拉起两个 subagent,并行去更新文档和 TODO。最终产出是这样的:
┌─────┬──────────────────────────────────────┬────────────────────┐
│ # │ Task │ Commits │
├─────┼──────────────────────────────────────┼────────────────────┤
│ 1 │ Schema migration │ 10aa550 │
│ 2 │ Media module │ 8aec253, b39551b │
│ 3 │ Telegram file download │ f243f20 │
│ 4 │ Wire media pipeline │ 620829d │
│ 5 │ Preset summaries + backstories │ 3733f3b, e8903da │
│ 6 │ Onboarding enhancements │ bd148cc, 90da519 │
│ 7 │ System prompt wiring │ 0f94c71 │
│ 8 │ Reonboard with preset switch │ 3d15317, af668b4 │
│ 9 │ Selfie module │ b749684 │
│ 10 │ Selfie trigger wiring │ 387c378, b02f291 │
└─────┴──────────────────────────────────────┴────────────────────┘
整个改动一个 session 就干完了。每个任务都有自己的 commit,翻 git log 能看到谁干的哪一块。
什么时候该用 team,什么时候不该
用了几次之后,我现在的判断标准大概是这样的。适合用 team 的情况:有三个以上可以并行的独立子任务,任务之间有明确的依赖 DAG。改动涉及三个以上不同的目录或者模块。单个 agent 的 context window 装不下全部任务。
不适合的情况:改动集中在一两个文件,直接编辑就行。任务完全串行、没有并行空间,用 subagent 就够了。还有需要深度 context 理解的活,比如仿真调试那种反复迭代的调试。team 里每个 agent 的 context 是隔离的,没法共享调试状态。
另外一个感受是 context 隔离,这个可能比速度还值钱一些。每个 agent 只需要理解自己负责的那片代码,不用把整个改动面全读进 context;lead 只需要理解依赖关系和最终接口,每个文件具体怎么实现的它不用管。
回头看,lead 干的活比单纯派任务要复杂不少。它要维护依赖图,节点一完成就检查哪些下游可以启动;干完活的 agent 关掉,需要新 agent 的时候再拉起来,不能让干完活的人干等着。它还得分得清什么是真问题、什么只是并行工作不可避免的代价。五个 agent 同时干活,工作区里有别人的未提交文件,hook 报警不等于出事了。服务器掉线的时候 lead 自己也会断,但回来之后它靠 git 状态把进度重建出来。所有 teammate 跑完之后统一跑 typecheck 和 test,有问题当场修。
之前在别写代码了,学管 AI 吧里写过,人的角色在变成 manager。这次实际的分工是:计划和验收标准是我出的,中间的协调全是 lead 在做,代码是 teammate 写的。具体代码我确实一行都没看,就最后看了一眼 typecheck 的结果。反正下次再有这种改动面大的活,我应该还是会用 team 去跑,到时候再看看它在更乱的依赖图上顶不顶得住。
三层协作结构:我在最上层出计划和验收标准,Lead 在中间协调依赖,五个 teammate 在底层写代码,人一行代码都没看
