【Agents】自定义子代理进阶:沙盒隔离

📅 发布时间:2026/7/8 4:54:38 👁️ 浏览次数:
【Agents】自定义子代理进阶:沙盒隔离
基础篇【Agents】Claude Code 多 Agent 入门从一问一答到并行协作实践篇1:【Agents】Claude Code 自定义子代理内置的不够用就自己造​ 上一篇你已经会造子代理了。但用过几次就会发现一个问题子代理直接改你的工作区改崩了没有后悔药。​ 你让一个子代理试试用策略模式重构这段 switch-case它要是改崩了你的代码也崩了。你得盯着它每一步操作随时准备撤销。想让它放手大胆试你不敢。​isolation: worktree解决的就是这件事给子代理一个沙盒改坏了扔掉就行你的主工作区纹丝不动。​ 上图是整篇文章的核心对比。左边是基础模式子代理直接在你的工作区里改文件你得盯着改崩了就是你的事。右边是进阶模式子代理在隔离的沙盒里工作你不用等也不用担心完成后收到通知再决定要不要合并。一、沙盒模式isolation: worktree1.1 为什么需要沙盒​ 先看一个真实场景。你在做一次性能优化数据库查询层有两个方案可选方案 A 是给现有查询加缓存方案 B 是换用批量查询重写整个模块。你不确定哪个更好想让 Claude 两个都试一下跑跑基准测试再决定。​ 如果没有沙盒你只能串行试先让 Claude 改成方案 A跑测试看结果然后git stash或git checkout恢复现场再试方案 B。更糟的情况是方案 A 改到一半你发现思路不对现在代码处于一个半成品状态回退都不利索。​沙盒模式让每个子代理在独立的 Git Worktree 中工作。它有自己的工作目录、自己的分支和你当前的代码物理隔离。子代理可以大刀阔斧地改改成什么样都不影响你的主工作区。满意了就合并不满意就丢弃。就像在草稿纸上画方案不满意揉了扔掉正稿纹丝不动。​ 上图展示了沙盒隔离的核心结构。上层是你的主工作区main 分支下层是子代理的沙盒worktree 分支。两者拥有相同的文件结构但物理上是不同的目录子代理在下层怎么改都不影响上层。它们共享同一个.git对象库所以磁盘开销很小不是完整 clone。1.2 怎么配置​ 在项目根目录的.claude/agents目录下创建refactor-explorer.md文件只需要在子代理的 frontmatter 中加一行--- name: refactor-explorer description: 探索性重构方案在沙盒中独立尝试不改动主工作区 model: sonnet isolation: worktree tools: - Read - Glob - Grep - Edit - Bash --- 你是一个重构探索员。收到重构需求后 1. 先分析当前代码结构和依赖关系 2. 提出重构方案并直接实施 3. 修改完成后运行现有测试确保不破坏功能 4. 如果测试失败分析原因并修复 5. 最后给出改动摘要改了哪些文件、为什么这么改、测试结果 大胆尝试不需要保守——你在沙盒中工作不会影响主工作区。​ 关键就是isolation: worktree这一行。有了它这个子代理每次被调用时都会自动在独立的 Git Worktree 中运行。前置条件项目必须是 Git 仓库。沙盒模式基于git worktree如果你的项目还没有初始化 Git没有.git目录子代理启动时会直接报错。确保至少执行过git init并有一次提交。另外worktree 要求当前不能处于 rebase 或 merge 冲突状态Git 需要一个干净的基点来创建新的工作树。二、生命周期从创建到清理​ 当主对话调用一个 worktree 隔离的子代理时背后会经历五个阶段​ 上图展示了 worktree 隔离子代理从创建到清理的完整过程。2.1 创建 Worktree​ 子代理启动时Claude Code 自动执行git worktree add在.claude/worktrees/下创建一个新的工作目录并从当前分支通常是main拉出一个新分支。整个过程对你完全透明——你不需要手动操作任何 Git 命令。​ 新创建的 worktree 是当前仓库的完整副本但和主工作区物理隔离。它们共享同一个.git对象库所以磁盘开销很小不是git clone不会复制历史记录。2.2 子代理在沙盒中工作​ 子代理的所有文件操作例如Read、Edit、Bash都发生在 worktree 目录中。它看到的文件树和你的主工作区一模一样因为是从同一个 commit checkout 出来的但它做的任何修改都不会影响你的主目录。​ 这意味着你可以在提示词里写大胆尝试因为试错成本为零。子代理可以删文件、重写模块、改配置怎么折腾都行。2.3 提交改动​ 子代理完成工作后把改动提交到 worktree 分支上。这个提交只存在于 worktree 的分支里不会出现在你的主分支上。2.4 你来决定合并还是丢弃​ 这是整个流程中唯一需要你决策的环节。子代理完成后会返回一个摘要告诉你它做了什么改动、测试结果如何。同时你会拿到 worktree 的路径和分支名。​ 你有三个选择# 方案满意合并到主分支gitmerge worktree-refactor-explorer-abc123# 想看看具体改了什么再决定gitdiffmain...worktree-refactor-explorer-abc123# 方案不行直接丢弃gitbranch-Dworktree-refactor-explorer-abc1232.5 自动清理​ 如果子代理工作过程中没有产生任何改动比如它分析完发现不需要改worktree 会被自动清理目录删除、分支移除不留任何痕迹。​ 如果有改动worktree 会保留等你 merge 或手动删除后再清理。三、实战A/B 方案对比​ 回到开头的性能优化场景。有了沙盒模式你可以同时让两个子代理探索两个方案同时启动两个 refactor-explorer 方案 Arefactor-explorer 给 UserRepository 的 findByIds 方法加 Redis 缓存 缓存 key 用 user:{id}TTL 5 分钟。改完跑 benchmark。 方案 Brefactor-explorer 把 UserRepository 里所有逐条查询改成批量查询 用 WHERE id IN (...) 替代循环中的 WHERE id ?。改完跑 benchmark。​ 两个子代理各自在独立的 worktree 中动手改代码、跑测试。你不用等也不用担心它们互相干扰。等两个都完成后对比 benchmark 结果选一个合并丢掉另一个。3.1 基线代码​ 演示用的基线文件是test-repo/user_repository.py核心结构如下classUserRepository:deffind_by_id(self,user_id):单条查询——每次都新建连接、执行一条 SELECT。connself._get_conn()try:rowconn.execute(SELECT id, name, email, department FROM users WHERE id ?,(user_id,),).fetchone()...finally:conn.close()deffind_by_ids(self,user_ids):⚠️ N1 问题循环调用 find_by_id200 个 ID 200 次建连 200 次查询。results[]foruidinuser_ids:userself.find_by_id(uid)# 每个 ID 独立建连 查询ifuser:results.append(user)returnresults​ 文件还包含一个init_db()函数初始化 1000 条测试数据和benchmark_find_by_ids()函数多轮计时 统计两个子代理都会复用这套基础设施来跑 benchmark。​ 两个子代理拿到的是同一份基线文件但各自在独立的 worktree 中修改互不可见。下面是实际跑出来的结果基线查询 200 个用户 ID平均耗时约 18ms。3.2 方案 A 结果Redis 缓存​ 子代理 A 的 worktree 路径为.claude/worktrees/agent-ac47fc19/分支名worktree-agent-ac47fc19。它对user_repository.py做了以下改动新增FakeRedis/FakePipeline类用 dict 模拟 Redis接口兼容redis-py生产环境可直接替换为真实 Redis给UserRepository.__init__新增可选参数redis_client为None时退化为原始 N1向后兼容重写find_by_idsMGET 批量查缓存 → 未命中的查 DB → pipeline 批量写缓存keyuser:{id}TTL 300s更新__main__块分别跑基线、冷启动、热缓存三组 benchmarkBenchmark: BASELINE (N1 逐条查询无缓存) Avg time : 20.28 ms Benchmark: 方案A 冷启动首次缓存未命中 Avg time : 23.42 ms ← 比基线略慢额外做了 MGET JSON 序列化 pipeline SET Benchmark: 方案A 热缓存后续轮次全命中 Avg time : 0.70 ms ← 28.9x 加速3.3 方案 B 结果批量查询 WHERE id IN​ 子代理 B 的 worktree 路径为.claude/worktrees/agent-a4f8ac9d/分支名worktree-agent-a4f8ac9d。它对user_repository.py做了以下改动重写find_by_ids用一条SELECT ... WHERE id IN (?, ?, ...)替代循环单连接单查询新增分批逻辑IDs 超过 500 个时按_BATCH_SIZE 500分批防止 SQL 参数超限提取_rows_to_dicts辅助方法消除find_by_department中的重复转换逻辑更新__main__块先用原始 N1 跑基线再用新实现跑对比Benchmark: BASELINE (N1 逐条查询) Avg time : 16.87 ms Benchmark: 方案B (WHERE id IN 批量查询) Avg time : 0.65 ms ← 25.8x 加速3.4 对比总结指标基线 (N1)方案A 冷启动方案A 热缓存方案B 批量 IN平均耗时~18 ms~23 ms0.70 ms0.65 ms加速比1x0.8x28.9x25.8x额外依赖无RedisRedis无首次请求—比基线更慢—始终快​ 两个方案的热路径性能接近但方案 B 零依赖且首次请求就快适合大多数场景方案 A 更适合跨服务共享缓存或读写比极端的场景。​ 最终选方案 B 的话gitmerge worktree-agent-a002bc72# 合入方案 Bgitbranch-Dworktree-agent-a6e1b7# 丢弃方案 A​ 这就是沙盒模式的核心价值把要不要试试的心理负担降到零。四、和手动 --worktree 的区别​ 区别在于谁来管理 worktree 的生命周期手动--worktreeisolation: worktree创建方式你手动在终端执行claude --worktree name子代理被调用时自动创建使用场景你知道要并行做什么主动开多个终端写进 agent 定义每次调用自动隔离生命周期你管理手动 merge、手动清理半自动无改动时自动清理有改动时等你决定适合谁开发者主动规划并行任务探索性、破坏性任务或团队标准化流程​ 简单说--worktree是你主动开沙盒isolation: worktree是让子代理自带沙盒。后者的好处是你不需要每次都想着这个任务要不要隔离定义好的子代理天生就是隔离的。五、总结​ 沙盒隔离解决的是信任问题你不用再盯着子代理的每一步操作不用担心它改坏你的代码。isolation: worktree一行配置子代理就有了自己的独立工作空间。核心要点沙盒基于 Git Worktree物理隔离文件系统共享.git对象库磁盘开销小子代理在沙盒里可以大胆试错改崩了扔掉就行生命周期半自动无改动自动清理有改动等你决定合并或丢弃特别适合 A/B 方案对比、探索性重构这类试试看的场景但你可能还有另一个痛点子代理跑着的时候你只能干等没法继续对话。之后会介绍background: true让子代理在后台跑你该干嘛干嘛再加上沙盒 后台的组合玩法。