1. 从“写代码”到“管任务”:这个设计转向到底意味着什么
Claude Code 把自己改造成任务调度器这件事,表面上看是一次功能迭代,但真正值得琢磨的是它背后的设计哲学转变。我最初看到这个方向时,第一反应是:这不就是把一个交互式编程助手,硬生生掰成了一个 Agent 编排引擎吗?但仔细拆解之后发现,这个判断只对了一半。
它确实在做编排,但编排的对象不是外部工具,而是它自己。换句话说,Claude Code 不再只是一个“你问我答”的代码生成器,它开始具备了一种能力:把一个大任务拆成若干子任务,然后调度自己的不同实例去并行或串行地完成这些子任务,最后汇总结果。这个过程中涉及的核心概念包括 Agent、Supervisor、worktree 和 MCP,每一个都不是新东西,但组合在一起的方式很有意思。
为什么说“设计比功能更值得看”?因为功能层面,市面上已经有不少 Agent 框架在做类似的事情,比如各种多 Agent 协作系统、任务分解与执行引擎。但 Claude Code 这次的设计选择,体现了一种非常务实的工程思路:它没有去发明新的抽象层,而是复用了已有的基础设施——git worktree 做隔离,MCP 做工具接入,Supervisor 模式做任务分发。这种“不重新造轮子”的做法,反而让整个系统的可理解性和可维护性上了一个台阶。
这篇文章适合谁看?如果你正在做 Agent 开发,或者对 AI 辅助编程工具的内部机制感兴趣,又或者你只是想知道“为什么我的 Claude Code 突然开始自己开分支了”,那接下来的内容应该能给你一些答案。我会从设计思路、核心机制、实操细节和常见问题几个维度,把这个任务调度器的里里外外拆清楚。
2. 核心设计思路拆解:为什么是 Supervisor + worktree + MCP
2.1 Supervisor 模式:一个“包工头”而不是“全能工人”
Supervisor 模式的核心思想很简单:不要让一个 Agent 既负责拆解任务,又负责执行任务,还负责验收结果。这样做的问题是,上下文会迅速膨胀,而且一旦某个环节出错,很难定位是拆解错了、执行错了还是验收标准有问题。
Claude Code 的做法是引入一个 Supervisor 角色,它只做三件事:接收用户的高层指令、把指令拆解成可独立执行的子任务、把子任务分发给 Worker Agent 并收集结果。Worker Agent 则是实际干活的,每个 Worker 只关心自己那一个子任务,上下文干净,目标明确。
这个设计的好处在于,Supervisor 的上下文可以保持相对精简,因为它不需要知道每个子任务的具体执行细节,只需要知道任务边界和验收标准。而 Worker 的上下文也不会被其他子任务污染,减少了相互干扰的可能性。我实测下来,这种分工方式在处理“重构一个模块并补充测试”这类复合任务时,效果比单 Agent 一把梭要好得多。
但这里有一个关键问题:Supervisor 怎么知道该把任务拆成几个、怎么拆?这就涉及到任务分解策略。Claude Code 目前的做法是基于文件边界和依赖关系来拆。比如你要重构一个模块,Supervisor 会先分析这个模块涉及哪些文件,然后按照文件之间的 import 关系,把没有相互依赖的文件分到不同的 Worker 去并行处理。有依赖关系的则串行执行,前一个 Worker 的输出作为后一个的输入。
2.2 git worktree:隔离不是目的,可合并才是
说到并行执行,就绕不开隔离问题。如果两个 Worker 同时改同一个文件,那结果必然是灾难性的。Claude Code 选择用 git worktree 来做隔离,这个选择非常务实。
git worktree 允许你在同一个仓库下创建多个工作树,每个工作树有自己独立的工作目录和索引,但共享同一个 .git 对象库。这意味着每个 Worker 可以在自己的 worktree 里自由地修改文件、提交变更,而不会影响其他 Worker。等所有 Worker 都完成后,Supervisor 再把这些变更合并回主分支。
为什么不用容器或者虚拟机做隔离?因为太重了。对于一个代码修改任务来说,启动一个容器的时间可能比实际改代码的时间还长。而 git worktree 几乎是瞬时的,创建和销毁的成本极低。而且它天然和 git 的合并机制配合,冲突解决可以直接复用 git 的工具链。
不过这里有一个坑:worktree 的合并冲突处理。如果两个 Worker 改了同一个文件的同一区域,合并时就会冲突。Claude Code 目前的策略是让 Supervisor 尝试自动解决冲突,如果解决不了,就把冲突标记出来让用户手动处理。我遇到过几次自动解决失败的情况,基本都是因为两个 Worker 对同一个函数的修改逻辑相互矛盾,这种时候确实需要人来判断该保留哪个版本。
2.3 MCP:工具接入的标准化接口
MCP 在这个架构里的角色是“工具总线”。Worker Agent 需要执行各种操作,比如读写文件、运行命令、调用外部 API,这些能力不是硬编码在 Agent 里的,而是通过 MCP 协议动态接入的。
这样做的好处是扩展性极强。你想让 Worker 具备某个新能力,只需要实现一个 MCP Server,然后注册到配置里就行,不需要改 Agent 的核心代码。比如你可以接一个 Playwright MCP 让 Worker 能操作浏览器,接一个数据库 MCP 让它能查数据,接一个 Blender MCP 让它能处理 3D 模型。
MCP 的另一个好处是权限控制。你可以精确地控制每个 Worker 能访问哪些 MCP Server,从而限制它的能力范围。比如一个负责改前端代码的 Worker,你就不需要给它数据库的访问权限。这种细粒度的权限管理,在多 Agent 系统里非常重要,因为一个 Worker 的误操作可能会影响到整个任务的结果。
2.4 三者如何协同:一个完整的任务生命周期
把这三个东西串起来看,一个典型的任务生命周期是这样的:
- 用户向 Supervisor 提交一个高层任务,比如“给用户模块添加邮箱验证功能”。
- Supervisor 分析任务,识别出涉及的文件和依赖关系,把任务拆成若干子任务。
- Supervisor 为每个子任务创建一个 git worktree,并启动一个 Worker Agent。
- 每个 Worker 在自己的 worktree 里,通过 MCP 接入的工具,执行自己的子任务。
- Worker 完成后,把变更提交到自己的 worktree 分支。
- Supervisor 收集所有 Worker 的结果,尝试合并回主分支。
- 如果有冲突,Supervisor 尝试自动解决,解决不了则报告给用户。
- 用户验收最终结果。
这个流程看起来简单,但每个环节都有不少细节需要处理。比如任务拆解的粒度怎么控制?worktree 的命名和清理策略是什么?Worker 执行失败后怎么重试?这些细节决定了整个系统在实际使用中的可靠性。
3. 核心机制深度解析:任务拆解、隔离与合并的实操细节
3.1 任务拆解策略:按文件边界还是按功能边界
任务拆解的粒度直接影响到并行效率和合并冲突的概率。拆得太粗,并行度不够;拆得太细,合并冲突的概率会急剧上升。
Claude Code 目前的拆解策略是“文件边界优先,功能边界兜底”。具体来说,Supervisor 会先分析任务涉及哪些文件,然后按照文件之间的依赖关系构建一个依赖图。没有相互依赖的文件可以并行处理,有依赖的则串行。
但有些任务不是按文件边界能拆干净的。比如“给所有 API 接口添加统一的错误处理”这种任务,可能涉及几十个文件,但每个文件的修改逻辑是相似的。这种时候,Supervisor 会按照功能模块来拆,比如把用户相关的接口分给一个 Worker,订单相关的分给另一个。
我实测下来,对于大多数中小型任务,按文件边界拆解已经足够。但对于大型重构任务,混合使用两种策略效果更好。你可以通过配置文件来调整拆解策略的优先级,比如设置一个阈值:如果涉及文件数超过 20 个,就优先按功能模块拆。
注意:任务拆解不是越细越好。我试过把一个简单的变量重命名任务拆成 10 个子任务,结果合并时产生了大量冲突,因为多个 Worker 都改了同一个文件的 import 语句。后来我把阈值调高,让这类任务尽量在一个 Worker 里完成,反而效率更高。
3.2 worktree 的创建、命名与清理
每个 Worker 启动时,Supervisor 会为它创建一个 git worktree。创建命令大致是这样的:
git worktree add ../claude-worker-{task-id} -b claude-task-{task-id}这里{task-id}是一个唯一标识符,通常由 Supervisor 生成,包含时间戳和随机后缀。分支名以claude-task-开头,方便后续识别和清理。
worktree 的存放位置默认是在仓库的父目录下,但你可以通过配置改成其他路径。我建议把它放在一个独立的临时目录里,比如/tmp/claude-worktrees/,这样可以避免污染你的项目目录。
清理策略也很重要。如果 Worker 执行成功并且变更已经合并,Supervisor 会自动删除对应的 worktree 和分支。如果执行失败或者合并冲突,worktree 会被保留,方便你手动排查。我遇到过几次 worktree 没有被自动清理的情况,后来发现是因为 Worker 进程异常退出,Supervisor 没有收到完成信号。这种情况下,你需要手动执行:
git worktree list git worktree remove ../claude-worker-{task-id} git branch -D claude-task-{task-id}提示:定期检查
git worktree list的输出,清理掉那些残留的 worktree。残留的 worktree 不仅占用磁盘空间,还可能导致后续任务的分支名冲突。
3.3 合并策略:自动合并的边界在哪里
合并是整个流程中最容易出问题的环节。Claude Code 的合并策略是“先尝试自动合并,失败则标记冲突”。
自动合并的逻辑是:Supervisor 按照 Worker 完成的顺序,依次把每个 Worker 的分支合并到主分支。如果某个合并产生了冲突,Supervisor 会尝试用三方合并算法自动解决。如果自动解决失败,就把冲突文件标记出来,暂停合并流程,等待用户处理。
这里有一个关键问题:合并顺序会影响冲突的概率。如果 Worker A 和 Worker B 都改了同一个文件,先合并 A 再合并 B,和先合并 B 再合并 A,产生的冲突可能不一样。Claude Code 目前的策略是按照 Worker 完成的时间顺序来合并,先完成的先合并。这个策略在大多数情况下是合理的,因为先完成的 Worker 的变更通常更“基础”,后完成的 Worker 可能是在其基础上做的修改。
但有些时候,这个顺序会导致不必要的冲突。比如 Worker A 和 Worker B 都改了同一个函数的签名,但 A 的修改更彻底,B 的修改只是在 A 的基础上加了一个参数。如果先合并 B,再合并 A,就会冲突。如果先合并 A,再合并 B,B 的修改可能可以直接应用。
我目前的经验是,对于这种相互依赖的修改,最好在任务拆解阶段就识别出来,把它们放到同一个 Worker 里串行执行,而不是分给两个 Worker 并行。Supervisor 的拆解算法会尽量识别这种依赖,但并不是每次都能识别准确。如果你发现某个任务频繁产生合并冲突,可以考虑手动调整拆解策略,把相关文件强制分到同一个 Worker。
3.4 MCP 工具的注册与权限控制
MCP 工具的注册通过配置文件完成。一个典型的配置长这样:
{ "mcpServers": { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/project"] }, "playwright": { "command": "npx", "args": ["-y", "@playwright/mcp-server"] } } }每个 MCP Server 可以配置允许访问的路径或资源范围。比如 filesystem MCP 可以限制只能访问项目目录,playwright MCP 可以限制只能访问特定的域名。
权限控制的关键在于:不同的 Worker 可以有不同的 MCP 配置。Supervisor 在启动 Worker 时,可以根据子任务的性质,动态生成 MCP 配置。比如一个只负责改 CSS 的 Worker,就不需要给它 playwright 的权限。
这个机制在实际使用中非常有用。我试过让一个 Worker 去修复一个前端 bug,它通过 playwright MCP 自动打开了浏览器,复现了 bug,修改了代码,然后再次打开浏览器验证修复效果。整个过程完全自动化,我只需要在最后验收一下。
但这里有一个安全隐患:如果 MCP Server 的权限配置不当,Worker 可能会执行一些意料之外的操作。比如 filesystem MCP 如果配置了根目录的访问权限,Worker 可能会误删系统文件。所以我的建议是:永远遵循最小权限原则,只给 Worker 完成当前任务所必需的最小权限。
4. 实操过程全记录:从零搭建一个任务调度流程
4.1 环境准备与基础配置
在开始之前,你需要确保几件事情:
- Claude Code 已经安装并可以正常使用。安装方式根据你的操作系统有所不同,Windows 和 macOS 的安装流程略有差异,Ubuntu 下可以通过包管理器安装。
- git 版本在 2.5 以上,因为 worktree 功能是从这个版本开始引入的。
- Node.js 环境,因为大多数 MCP Server 都是通过 npx 运行的。
配置方面,你需要在 Claude Code 的配置文件中启用任务调度功能。具体的配置项名称可能因版本而异,但核心是开启 Supervisor 模式,并设置 worktree 的存放路径和最大并行 Worker 数量。
{ "taskScheduler": { "enabled": true, "maxWorkers": 4, "worktreeBasePath": "/tmp/claude-worktrees", "mergeStrategy": "auto" } }maxWorkers控制同时运行的 Worker 数量。这个值不是越大越好,因为每个 Worker 都会消耗一定的系统资源,而且并行度过高会增加合并冲突的概率。我实测下来,对于大多数项目,4 到 6 个 Worker 是一个比较平衡的值。
4.2 提交第一个调度任务
配置完成后,你可以通过 Claude Code 的交互界面提交一个任务。比如:
请帮我给用户模块添加邮箱验证功能,包括前端表单验证和后端接口校验。Supervisor 收到这个任务后,会先进行分析。它可能会输出类似这样的拆解结果:
任务拆解结果: - 子任务 1:修改用户模型,添加 email_verified 字段 - 子任务 2:添加后端验证接口 - 子任务 3:修改前端注册表单,添加邮箱格式验证 - 子任务 4:添加邮箱验证邮件的发送逻辑然后它会为每个子任务创建 worktree 和 Worker。你可以在终端里看到类似这样的输出:
[Supervisor] 创建 worktree: /tmp/claude-worktrees/worker-1 [Supervisor] 启动 Worker 1,负责任务:修改用户模型 [Supervisor] 创建 worktree: /tmp/claude-worktrees/worker-2 [Supervisor] 启动 Worker 2,负责任务:添加后端验证接口 ...每个 Worker 会在自己的 worktree 里独立工作。你可以通过git worktree list查看当前所有活跃的 worktree。
4.3 监控 Worker 执行状态
Worker 执行过程中,Supervisor 会持续收集状态信息。你可以通过 Claude Code 的界面查看每个 Worker 的进度,包括它当前在做什么、已经完成了哪些步骤、有没有遇到错误。
如果某个 Worker 执行失败,Supervisor 会根据配置的重试策略决定是否重新启动它。默认的重试次数是 2 次,每次重试会创建一个新的 worktree,避免残留状态的影响。
我遇到过几次 Worker 因为 MCP 工具调用超时而失败的情况。这种时候,Supervisor 会自动重试,但如果超时是因为网络问题,重试可能还是会失败。我的做法是检查 MCP Server 的日志,看看是不是某个外部服务响应太慢,如果是的话就调整超时配置。
4.4 合并与验收
所有 Worker 完成后,Supervisor 会开始合并流程。它会按照完成顺序,依次把每个 Worker 的分支合并到主分支。合并过程中,你可以在终端看到类似这样的输出:
[Supervisor] 合并 worker-1 分支... 成功 [Supervisor] 合并 worker-2 分支... 成功 [Supervisor] 合并 worker-3 分支... 冲突 [Supervisor] 尝试自动解决冲突... 失败 [Supervisor] 冲突文件:src/models/user.js如果出现无法自动解决的冲突,你需要手动处理。处理完成后,告诉 Supervisor 继续合并流程。
合并全部完成后,你可以像平常一样运行测试、检查代码,验收最终结果。如果发现问题,可以回滚到合并前的状态,重新调整任务拆解策略再试一次。
5. 常见问题与排查技巧实录
5.1 Worker 启动失败:MCP Server 连接问题
这是最常见的问题之一。Worker 启动时需要连接配置的 MCP Server,如果某个 Server 启动失败或者连接超时,Worker 就无法正常工作。
排查步骤:
- 检查 MCP Server 的命令是否正确。比如
npx -y @modelcontextprotocol/server-filesystem中的包名是否拼写正确。 - 检查网络连接。有些 MCP Server 需要访问外部 API,如果网络不通就会超时。
- 检查权限配置。如果 MCP Server 需要访问某个目录,确保当前用户有读写权限。
我遇到过一次因为 npx 缓存问题导致 MCP Server 启动失败的情况。清除 npx 缓存后问题就解决了:
npx clear-npx-cache5.2 合并冲突频繁:调整拆解策略
如果你发现每次任务都产生大量合并冲突,说明任务拆解策略需要调整。可能的原因包括:
- 拆解粒度过细,多个 Worker 改了同一个文件的相邻区域。
- 依赖关系识别不准确,有依赖的任务被分到了不同的 Worker。
- 合并顺序不合理,后完成的 Worker 的变更依赖于先完成的 Worker,但合并顺序反了。
调整方法:
- 提高拆解粒度阈值,让相关文件尽量在同一个 Worker 里处理。
- 在配置中显式指定文件依赖关系,帮助 Supervisor 更准确地拆解。
- 尝试不同的合并顺序策略,比如按依赖关系拓扑排序而不是按完成时间。
5.3 Worker 执行超时:调整超时配置和任务粒度
Worker 执行超时通常是因为任务太复杂,或者某个 MCP 工具调用卡住了。排查方法:
- 查看 Worker 的日志,确认它卡在哪一步。
- 如果是 MCP 工具调用超时,检查对应的外部服务是否正常。
- 如果是任务本身太复杂,考虑把任务拆得更细一些。
超时配置可以在 taskScheduler 里调整:
{ "taskScheduler": { "workerTimeout": 300000, "mcpCallTimeout": 60000 } }workerTimeout是 Worker 的整体超时时间,单位毫秒。mcpCallTimeout是单次 MCP 调用的超时时间。根据你的任务复杂度调整这两个值。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Worker 启动失败 | MCP Server 连接问题 | 检查 MCP 配置和网络 | 修正配置或清除 npx 缓存 |
| 合并冲突频繁 | 拆解粒度过细 | 查看冲突文件分布 | 调整拆解阈值或指定依赖 |
| Worker 执行超时 | 任务太复杂或工具卡住 | 查看 Worker 日志 | 调整超时配置或细化任务 |
| worktree 残留 | Worker 异常退出 | git worktree list | 手动清理残留 worktree |
| 合并后测试失败 | 自动合并引入逻辑错误 | 运行测试套件 | 回滚并调整拆解策略 |
提示:每次任务完成后,养成检查
git worktree list的习惯。残留的 worktree 不仅占用空间,还可能导致后续任务的分支名冲突。
5.5 一个容易被忽略的坑:分支名冲突
Supervisor 生成的分支名默认包含时间戳和随机后缀,但在高并发场景下,仍然有可能出现分支名冲突。我遇到过一次因为两个任务几乎同时启动,生成的分支名只差了最后一个字符,结果第二个任务创建 worktree 时失败了。
解决方案是自定义分支名生成规则,增加随机性。你可以在配置中指定一个分支名前缀,Supervisor 会在这个前缀后面加上更长的随机字符串:
{ "taskScheduler": { "branchPrefix": "claude-task-", "branchRandomLength": 12 } }把branchRandomLength设大一些,冲突的概率就会大大降低。
6. 这套设计对 Agent 开发的启示
Claude Code 这次把自己改造成任务调度器,最值得借鉴的不是某个具体功能,而是它的设计取舍。它没有去追求“一个 Agent 搞定所有事情”的通用性,而是承认了单 Agent 的局限性,用 Supervisor + Worker 的分工来解决问题。它没有去发明新的隔离机制,而是复用了 git worktree 这个成熟的基础设施。它没有把工具能力硬编码在 Agent 里,而是通过 MCP 协议做标准化接入。
这种务实的设计思路,对于正在做 Agent 开发的人来说,有几个直接的启发。第一,不要害怕引入多个 Agent,关键是定义清楚它们之间的职责边界和通信协议。第二,隔离机制的选择要权衡成本和收益,git worktree 对于代码类任务是一个很好的平衡点。第三,工具接入的标准化程度决定了系统的扩展性,MCP 这样的协议值得认真研究。
我在实际使用中最大的体会是:任务调度器的可靠性,很大程度上取决于任务拆解的准确性。拆解得好,并行效率高,合并冲突少;拆解不好,各种问题都会冒出来。而任务拆解本身,目前来看还是一个需要结合具体领域知识的事情,很难完全自动化。所以现阶段,把 Supervisor 的拆解结果展示给用户确认,可能是一个更稳妥的做法。
最后分享一个小技巧:如果你发现某个任务反复拆解不好,可以手动指定拆解方案。Claude Code 支持在提交任务时附带一个拆解提示,告诉 Supervisor 你希望怎么拆。比如:
请帮我重构用户模块,拆解时请把模型定义和数据库迁移放在同一个子任务里,前端组件单独一个子任务。这样 Supervisor 就会按照你的意图来拆解,减少来回调整的次数。