Worktree + Task Isolation 任务隔离配置改到 TaoToken)
1. 多任务并行时为什么你的 ClaudeCode 会话总在互相踩脚如果你正在跟着 learn-claude-code 项目一路写下来到 s11 的时候应该已经有一个能自主认领任务的智能体了。任务板能创建、能更新、能标记完成看起来挺像回事。但只要你真的开两个会话同时干活问题立刻暴露两个任务都在改config.py一个把超时改成 30 秒另一个把重试次数改成 5 次未提交的改动在同一个工作目录里搅在一起git diff出来一堆不属于自己的东西想回滚都不知道该回滚到哪一步。这就是 s12 要解决的核心痛点。任务板管的是「做什么」但它管不了「在哪做」。所有任务共享一个目录等于所有人挤在同一张桌子上改同一份稿子谁的手肘碰到谁都不奇怪。Worktree Task Isolation 的思路很直接给每个任务分配一个独立的 git worktree 目录用任务 ID 把「目标」和「环境」绑在一起。任务管目标worktree 管目录按 ID 绑定各干各的目录互不干扰。这篇文章面向的是正在做多任务并行开发的场景。你可能同时要重构认证模块、修登录页的 bug、还要试一个新的缓存策略。如果全在主工作区里切来切去git stash会变成你的日常而且 stash 多了自己都记不清哪个是哪个。Worktree 任务隔离要做的就是让每个任务有自己的分支、自己的目录、自己的会话上下文做完一个收尾一个干净利落。我会按 learn-claude-code 第 12 篇的节奏先讲清楚双平面架构的设计再给出可复制的 worktree 创建脚本和任务目录映射配置然后把 endpoint 和 auth.json 改到 TaoToken 统一通道最后做连通性验证和常见报错排查。你跟着敲一遍应该能把这套隔离机制跑起来。2. TaoToken 前置把 endpoint 与 auth.json 统一到一条 API 通道在动手改 worktree 之前先把模型调用这条链路理顺。learn-claude-code 的 s12 代码里用的是 Anthropic SDK通过环境变量读取ANTHROPIC_BASE_URL和MODEL_ID。如果你之前每个会话都手动改 base_url或者把 key 散落在不同的 shell 配置里多任务并行时会非常乱。统一到 TaoToken 的好处是一个 Key、一个 Base URL、一个模型 ID所有 worktree 里的会话都走同一条通道排查问题时不用怀疑「是不是这个目录的配置不一样」。先拿到 API Key。打开 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentworktree_task_isolationutm_campaignrewrite 在控制台里创建一个新的 Key。建议按项目或按用途命名比如learn-claude-code-s12这样后面在事件日志里看到调用记录时能对得上。创建完复制出来注意它只显示一次。Base URL 用https://taotoken.net/api这个地址不加任何查询参数。模型 ID 根据你在控制台里看到的可用模型填比如claude-sonnet-4-20250514这类。如果你不确定该用哪个可以先到模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentworktree_task_isolationutm_campaignrewrite 试一下确认模型能正常响应再写进配置。接下来是 auth.json 的处理。learn-claude-code 的 s12 代码里有一段关键逻辑load_dotenv(overrideTrue) if os.getenv(ANTHROPIC_BASE_URL): os.environ.pop(ANTHROPIC_AUTH_TOKEN, None)这段的意思是如果设置了自定义的ANTHROPIC_BASE_URL就把ANTHROPIC_AUTH_TOKEN从环境变量里移除避免 SDK 同时拿到两套认证信息导致冲突。所以你的.env文件应该长这样ANTHROPIC_BASE_URLhttps://taotoken.net/api ANTHROPIC_API_KEYsk-你的TaoToken密钥 MODEL_IDclaude-sonnet-4-20250514注意这里用的是ANTHROPIC_API_KEY而不是ANTHROPIC_AUTH_TOKEN。Anthropic SDK 在初始化时会优先读ANTHROPIC_API_KEY如果你同时设置了ANTHROPIC_AUTH_TOKEN在某些版本里会覆盖掉 API Key 的读取导致 401。所以最稳妥的做法是只保留ANTHROPIC_API_KEY把ANTHROPIC_AUTH_TOKEN留空或者不写。如果你用的是 Claude Code 的 settings 文件而不是环境变量配置片段是这样的{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的TaoToken密钥, MODEL_ID: claude-sonnet-4-20250514 } }这个 settings 文件放在项目根目录或者用户目录下都行关键是保证每个 worktree 里的会话都能读到同一份配置。因为 worktree 是独立的目录如果你把.env放在主工作区worktree 里是看不到的。解决办法有两个一是把.env放在仓库根目录的上一级用绝对路径加载二是在每个 worktree 创建后把.env软链接过去。我推荐第二种后面在创建脚本里会体现。关于 Coding Plan如果你打算长期跑多任务并行的 Agent可以了解一下 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentworktree_task_isolationutm_campaignrewrite 它更适合这种持续编码的场景。不过 s12 这一篇的重点是隔离机制先用按量 Key 把流程跑通就行。3. 可复制配置worktree 创建脚本与任务目录映射现在进入实操部分。learn-claude-code 的 s12 代码里WorktreeManager.create方法执行的核心 Git 命令是git worktree add -b wt/{name} .worktrees/{name} HEAD这条命令做了三件事创建新分支wt/{name}在.worktrees/{name}目录下建立工作树基于当前 HEAD 提交。wt/前缀是为了避免污染主分支.worktrees/目录则是执行平面的根。但直接敲这条命令还不够因为你需要把任务 ID 和 worktree 绑定起来。s12 的TaskManager.bind_worktree会在绑定时自动把任务状态从pending改成in_progress这个副作用很关键——它意味着「开始工作」这个动作被显式记录了。下面是一个可复制的创建脚本我把它放在仓库根目录的scripts/create_worktree.sh#!/usr/bin/env bash set -euo pipefail # 用法: ./scripts/create_worktree.sh worktree_name task_id [base_ref] WT_NAME${1:?worktree name required} TASK_ID${2:?task id required} BASE_REF${3:-HEAD} REPO_ROOT$(git rev-parse --show-toplevel) WT_DIR$REPO_ROOT/.worktrees/$WT_NAME BRANCHwt/$WT_NAME # 校验名称格式和 s12 的 _validate_name 保持一致 if ! [[ $WT_NAME ~ ^[A-Za-z0-9._-]{1,40}$ ]]; then echo Invalid worktree name. Use 1-40 chars: letters, numbers, ., _, - 2 exit 1 fi # 检查是否已存在 if [ -d $WT_DIR ]; then echo Worktree directory already exists: $WT_DIR 2 exit 1 fi # 创建 worktree git worktree add -b $BRANCH $WT_DIR $BASE_REF # 把主工作区的 .env 软链接到 worktree保证 API 配置一致 if [ -f $REPO_ROOT/.env ]; then ln -sf $REPO_ROOT/.env $WT_DIR/.env fi # 更新 .worktrees/index.json INDEX$REPO_ROOT/.worktrees/index.json mkdir -p $REPO_ROOT/.worktrees if [ ! -f $INDEX ]; then echo {worktrees: []} $INDEX fi python3 - $INDEX $WT_NAME $WT_DIR $BRANCH $TASK_ID PY import json, sys, time index_path, name, path, branch, task_id sys.argv[1:6] with open(index_path) as f: idx json.load(f) idx[worktrees].append({ name: name, path: path, branch: branch, task_id: int(task_id), status: active, created_at: time.time(), }) with open(index_path, w) as f: json.dump(idx, f, indent2) PY # 更新任务文件把状态改为 in_progress 并绑定 worktree TASK_FILE$REPO_ROOT/.tasks/task_${TASK_ID}.json if [ -f $TASK_FILE ]; then python3 - $TASK_FILE $WT_NAME PY import json, sys, time task_path, wt_name sys.argv[1:3] with open(task_path) as f: task json.load(f) task[worktree] wt_name if task[status] pending: task[status] in_progress task[updated_at] time.time() with open(task_path, w) as f: json.dump(task, f, indent2) PY fi echo Created worktree $WT_NAME at $WT_DIR (branch: $BRANCH, task: $TASK_ID)这个脚本做了几件 s12 代码里对应的事校验名称、创建 worktree、软链接.env、更新index.json、更新任务文件。软链接这一步是我额外加的因为 s12 的 Python 代码里load_dotenv是从当前工作目录读.env而 worktree 是独立目录不软链接的话读不到配置。任务目录映射的配置在.worktrees/index.json里结构是这样的{ worktrees: [ { name: auth-refactor, path: /path/to/repo/.worktrees/auth-refactor, branch: wt/auth-refactor, task_id: 1, status: active, created_at: 1730000000 }, { name: ui-login, path: /path/to/repo/.worktrees/ui-login, branch: wt/ui-login, task_id: 2, status: active, created_at: 1730000100 } ] }任务文件在.tasks/task_1.json{ id: 1, subject: Refactor auth module, description: Split token validation into separate module, status: in_progress, owner: alice, worktree: auth-refactor, blockedBy: [], created_at: 1730000000, updated_at: 1730000050 }这两个文件通过task_id和worktree字段互相引用形成双向绑定。控制平面.tasks/管目标执行平面.worktrees/管环境任务 ID 是桥梁。如果你用的是 Claude Code 的 settings 而不是环境变量worktree 里的 settings 也需要能读到。可以在创建脚本里再加一行把主工作区的.claude/settings.json也软链接过去if [ -f $REPO_ROOT/.claude/settings.json ]; then mkdir -p $WT_DIR/.claude ln -sf $REPO_ROOT/.claude/settings.json $WT_DIR/.claude/settings.json fi这样每个 worktree 里的会话都走同一套 Base URL、Key 和 Model ID不会出现「这个目录能调通、那个目录 401」的怪事。4. 验证请求在独立 worktree 中跑通一次完整调用配置写好了接下来验证。先创建两个任务再给它们分配 worktree。启动 s12 的智能体cd learn-claude-code python agents/s12_worktree_task_isolation.py在提示符下输入Create tasks for backend auth and frontend login page, then list tasks.你应该看到类似输出[ ] #1: backend auth [ ] #2: frontend login page两个任务都是pending状态没有 worktree 绑定。接着创建 worktree 并绑定Create worktree auth-refactor for task 1, then bind task 2 to a new worktree ui-login.这一步会触发worktree_create和task_bind_worktree两个工具调用。观察输出任务 1 的状态应该从[ ]变成[]并且带上wtauth-refactor。任务 2 同理。现在验证目录隔离。在 worktree 里跑一条命令Run git status --short in worktree auth-refactor.输出应该是干净的或者只显示 worktree 自己的改动。然后切到主工作区跑同样的命令两边互不影响。这就是目录级隔离的效果——run方法的cwd参数指向 worktree 目录命令在独立目录中执行。接下来验证 API 连通性。在 worktree 里让智能体读一个文件或者写一段代码In worktree auth-refactor, read the file config.py and tell me what the timeout value is.如果配置正确智能体会通过 TaoToken 的通道调用模型返回文件内容。如果返回 401说明 Key 没读到如果返回local proxy failed或者连接超时说明 Base URL 不对。你也可以直接在 worktree 目录下手动验证cd .worktrees/auth-refactor python3 -c import os from anthropic import Anthropic from dotenv import load_dotenv load_dotenv() client Anthropic(base_urlos.getenv(ANTHROPIC_BASE_URL)) resp client.messages.create( modelos.getenv(MODEL_ID), max_tokens100, messages[{role: user, content: Reply with OK only.}] ) print(resp.content[0].text) 如果打印出OK说明这条 worktree 里的 API 通道是通的。同样的命令在另一个 worktree 里跑一遍确认两个目录都能独立调通。最后验证收尾机制。删除一个 worktree 并同时完成任务Remove worktree auth-refactor with complete_tasktrue, then list tasks/worktrees/events.观察三件事任务 1 的状态是否变成[x] completedworktree 索引里auth-refactor的 status 是否变成removed事件日志里是否出现了worktree.remove.after和task.completed两条记录。事件日志在.worktrees/events.jsonl每行一个 JSON{event: worktree.remove.after, ts: 1730000200, task: {id: 1, status: completed}, worktree: {name: auth-refactor, status: removed}} {event: task.completed, ts: 1730000200, task: {id: 1, subject: backend auth, status: completed}, worktree: {name: auth-refactor}}这两条记录就是生命周期可观测性的体现。崩溃之后你可以从.tasks/和.worktrees/index.json重建现场事件流则告诉你「谁在什么时候做了什么」。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错来排查。s12 涉及 Git worktree 和 API 调用两条链路出错的地方比较分散我按报错信息分类。401 Unauthorized最常见的原因是 Key 没读到或者读错了。检查顺序先确认.env文件在 worktree 目录下能访问到ls -la .worktrees/auth-refactor/.env看软链接是否有效。然后确认变量名是ANTHROPIC_API_KEY而不是ANTHROPIC_AUTH_TOKEN。s12 代码里有一行os.environ.pop(ANTHROPIC_AUTH_TOKEN, None)如果你的 shell 里恰好 export 了这个变量它会被移除但如果你在.env里写了ANTHROPIC_AUTH_TOKENload_dotenv又会把它加载回来导致 SDK 拿到两套认证信息。解决办法是.env里只写ANTHROPIC_API_KEY。还有一个容易忽略的点TaoToken 的 Key 是否还有余额。到控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentworktree_task_isolationutm_campaignrewrite 看一下用量如果余额为零返回的也是 401 或 403。local proxy failed / connection refused这个报错通常出现在 Base URL 写错或者网络不通的时候。确认ANTHROPIC_BASE_URL是https://taotoken.net/api注意结尾没有斜杠也没有多余的路径。如果你之前配过其他地址检查 shell 里有没有残留的 exportenv | grep ANTHROPIC把所有相关的变量列出来确认没有冲突。另外worktree 里的.env是软链接如果主工作区的.env被修改了worktree 里读到的也是新值这个行为是符合预期的但如果你在调试时改了主.env忘了同步可能会困惑。reading choices / response parsing error这个报错一般不是网络问题而是模型返回的内容格式和 SDK 预期的不一致。常见原因是MODEL_ID填错了比如填了一个不存在的模型名服务端返回了错误信息SDK 尝试按正常响应解析就失败了。到模型对话页面确认一下可用的模型 ID然后更新.env里的MODEL_ID。还有一种情况是max_tokens设得太小模型返回被截断解析时也会报错。s12 代码里max_tokens8000一般够用但如果你在 worktree 里跑的是长文件分析可能需要调大。OAuth / authentication_error如果你之前用过 Claude Code 的 OAuth 登录本地可能残留了~/.claude/下的凭据文件。Anthropic SDK 在某些版本里会优先读这些凭据而不是环境变量里的 API Key。解决办法是检查~/.claude/目录把旧的凭据文件移走或者重命名然后重新用 API Key 初始化。s12 代码里没有显式处理 OAuth所以如果你遇到这个报错大概率是本地残留导致的。Git worktree 相关报错fatal: wt/auth-refactor is already checked out说明分支已经存在。先跑git worktree list看看有没有残留的工作树如果有用git worktree remove清掉再重新创建。fatal: not a git repository说明当前目录不在 Git 仓库里。s12 的_is_git_repo会检查这一点如果不在仓库里worktree_*工具会返回错误。确认你在 learn-claude-code 的仓库根目录下运行。Invalid worktree name说明名称不符合[A-Za-z0-9._-]{1,40}的规则。检查有没有中文、空格或者特殊字符。排查完这些如果还有问题可以到接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentworktree_task_isolationutm_campaignrewrite 对照一下配置示例或者到 API Keys 页面重新生成一个 Key 试试。6. 把隔离机制用起来从单任务到多任务并行跑通 s12 之后你手里就有了一套可用的任务隔离机制。每个任务有自己的 worktree、自己的分支、自己的会话上下文做完一个收尾一个不会互相污染。这套东西的价值在任务数量上去之后才真正体现出来。我自己的用法是这样的早上到工位先列今天要做的三件事用task_create建三个任务然后给每个任务分配一个 worktree。认证重构开一个登录页 bug 开一个缓存实验开一个。三个会话并行跑每个会话只在自己的 worktree 里操作。中午吃饭前把做完的那个worktree_remove掉complete_tasktrue任务板自动标记完成。没做完的worktree_keep下午接着干。事件日志是个好东西。晚上收工前跑一次worktree_events看看今天所有 worktree 的创建、运行、保留、删除记录哪些任务卡住了、哪些命令超时了一目了然。如果哪天会话崩了从.tasks/和.worktrees/index.json重建现场任务状态和 worktree 绑定关系都还在。如果你打算长期跑这种多任务并行的 AgentCoding Plan 会比按量 Key 更省心至少不用每天盯着余额。不过 s12 这一篇的重点是把隔离机制跑通你先用按量 Key 把流程走一遍确认每个 worktree 都能独立调通、独立收尾再考虑规模化的事。最后提醒一个坑worktree 目录不要提交到主仓库。.worktrees/应该加到.gitignore里否则git status会显示一堆未跟踪文件。.tasks/同理任务文件是本地状态不需要进版本控制。这两个目录是运行时产物不是代码。到这里s12 的 Worktree Task Isolation 就落地了。你可以在 learn-claude-code 的仓库里多试几个任务感受一下目录级隔离带来的清爽。下一章 s13 会在这个基础上继续往前走到时候这套隔离机制就是基础设施了。