Claude Code Projects 开始并行派发 Agent:先把预算管住

·阅读约11分钟·Evergreen Tools Team

2026 年 9 月 17 日,Anthropic 重构了 Claude Code 的 Projects 功能。你不再需要自己手动切分工作,而是描述一个目标,由一个 coordinator 把它拆成若干并行「thread」,每个 thread 都作为一次独立的 Claude Code 云 session 运行,拥有自己的分支和仓库副本。每个 thread 可以运行测试、迁移调用方、直接开 Pull Request;多条线程之间还会随时间共享记忆。首批开放给部分 Pro 与 Max 订阅者在云 session 中使用,Team 与 Enterprise 稍后跟进,本地执行也会陆续支持。这是把「多会话协作」这件事从人工编排、交给 Agent 自己编排的一步。但同一批报道也给出了那句最该被记住的提醒:这个功能可能在你午饭前就把套餐额度烧光。并行不是免费的,它只是把你的账单提前了。

一、这次到底发布了什么

据 The Decoder、DevOps.com 与 The New Stack 的报道:Projects 现在从主对话出发,coordinator 把一个开发目标拆成多个 thread;面向仓库的任务里,每个 thread 都是一次独立的云 session,带有自己的分支与仓库副本,因此多个部分可以同时推进而互相不覆盖彼此的改动。你可以在主项目对话里跟踪整体进度,也可以点进单条 thread 观察并随时纠偏。thread 能迁移调用方、跑测试、开 PR;coordinator 会判断哪条链路应该先合并。共享记忆让多个 thread 之间逐步形成共同上下文,一个资料库统一收集上传的文件与结果。

// Fan out a goal, but cap concurrency and give every thread a budget.
type Thread = { id: string; branch: string; budgetUsd: number; spent: number };

const MAX_THREADS = 3;            // matches your plan's headroom, not your ambition
const PER_THREAD_BUDGET = 0.80;   // USD - a thread that exceeds this is stopped

async function fanOut(goal: string, subtasks: string[]): Promise<Thread[]> {
  const queue = [...subtasks];
  const running: Thread[] = [];
  const done: Thread[] = [];

  while (queue.length || running.length) {
    while (running.length < MAX_THREADS && queue.length) {
      const task = queue.shift()!;
      const thread = await startCloudSession({
        task,
        branch: branchFor(task),
        budgetUsd: PER_THREAD_BUDGET,
      });
      running.push(thread);
    }
    const finished = await Promise.race(running.map((t) => waitFor(t)));
    running.splice(running.indexOf(finished), 1);
    done.push(finished);
  }
  return done;
}
并行运行的编码 Agent

一个目标,多条线程,多个分支

二、为什么并行会改写成本模型

单会话时,成本大致是「一个人、一段时间」。并行之后,成本变成「并发数 × 时长」,而每一条 thread 都是一次完整 session,消耗的是同一份额度。The New Stack 的标题说得很直白:这个功能可能在午饭前掏空你的套餐。原因不难理解——如果你一次开五条 thread,协调器就会以你开的线程数那样的速率为你计费。许多团队第一反应是「那我多开几条更快」,但真正被放大的不是速度,而是花费。更隐蔽的是时长:thread 在你合上笔记本之后仍然继续跑,这在体验上是优点,在账单上是一种持续计费。

// Each thread is a full session: meter tokens and dollars per thread,
// never per project, or you will not see which thread ran away.
function meter(
  threadId: string,
  usage: { input: number; output: number },
  prices: { in: number; out: number },
) {
  const cost = (usage.input / 1e6) * prices.in + (usage.output / 1e6) * prices.out;
  db.threads.update(threadId, { $inc: { tokens: usage.input + usage.output, spent: cost } });
  const row = db.threads.get(threadId);
  if (row.spent > row.budgetUsd) return stopThread(threadId, "budget");
  return "ok";
}

三、并行的账,最后都记在合并上

fan-out 只是前半句,fan-in 才是成本真正落地的地方。五条分支各自成功、各自开 PR,听起来很美;但如果它们的改动相互交叠,等待你的是一场合并战。每一条冲突都要人去看、去判断,而人的时间往往比 token 更贵。更麻烦的是顺序:先合并哪一条,会直接决定后面几条要不要重跑。这正是 coordinator 的价值所在——它知道哪条链路应该先合并——但顺序决策仍然是人的职责。一个诚实的问题应该在开跑前就问:这个任务真的能被拆成互不干扰的子任务吗?如果不能,并行只是把同一个问题复制了五份。

# Before the coordinator merges anything, find the threads that will collide.
# A fan-out of five branches with overlapping edits is a merge war, not speed.
for b in $(git branch --list 'agent/*' --format='%(refname:short)'); do
  git merge-tree --write-tree main "$b" >/dev/null 2>&1 || echo "CONFLICT likely: $b"
done

# Merge the smallest, least entangled branch first, then re-run the check.
# Let the coordinator merge; you decide the order. That is a human call.
合并多个 Agent 的分支

并行的代价最终都会出现在合并那一步

四、给 fan-out 定价:并发上限与预算护栏

把并行当成本控制问题来做,思路就清楚了。第一,设并发上限——不是设成你想要的野心值,而是设成你套餐余量能承受的值;三条稳过七条翻车。第二,给每条 thread 一个预算上限,超了就停,而不是让它跑到天荒地老。第三,设一个「整轮上限」,因为 coordinator 并不知道你的发票长什么样,护栏必须建在它之外。第四,开跑前先用 git 的合并预检找出会冲突的分支,优先合并最不纠缠的那条。第五,把每一轮 run 的线程数、token、花费、是否干净合并记下来,然后去看唯一真正重要的指标:每次合并 PR 花了多少钱。

// A run-level ceiling beats per-thread limits. The coordinator does not know
// what your invoice looks like, so the guard has to live outside it.
const RUN_CEILING = 6.00;

async function guard(runId: string) {
  const run = db.runs.get(runId);
  if (run.spent >= RUN_CEILING) {
    await stopCoordinator(runId);
    await stopAllThreads(runId);
    notify({ runId, reason: "run ceiling reached", spent: run.spent });
  }
}
setInterval(() => guard(runId), 30_000);

五、落地代码:从并发闸门到预算哨兵

第一段是带并发上限的 fan-out:最多同时开三条,每条带预算,谁先结束谁让位。第二段是按 thread 计量的成本表——一定要按线程算,按项目算你会看不出是哪条 thread 失控。第三段是合并预检:用 git merge-tree 在合并前找出大概率冲突的分支,并提示你按纠缠程度排顺序。第四段是整轮预算哨兵:每 30 秒检查一次,一旦超过整轮上限就同时停掉 coordinator 和所有 thread。第五段是 run 日志,把线程、token、花费和合并结果落库,供你画出「每次合并 PR 的成本」这条线。

// Log every run: threads, tokens, dollars, and whether the merge was clean.
type RunLog = {
  runId: string;
  threads: { id: string; branch: string; tokens: number; usd: number; pr?: string }[];
  mergedClean: boolean;
  wallClockMinutes: number;
};

export function logRun(run: RunLog) {
  db.runs.insert({ ...run, ts: new Date().toISOString() });
}
// Then chart cost per merged pull request. One number tells you whether
// parallelism is paying for itself or merely spending faster.
每次合并 PR 的成本

唯一该看的指标:每次合并 PR 花了多少钱

六、清单:并行之前先回答五个问题

第一,这个目标真的能拆成互不干扰的子任务吗?第二,我的套餐余量能承受几条并发?第三,每条 thread 的预算是多少、超了谁负责停?第四,有没有一个覆盖整轮的硬上限?第五,我能不能把「每次合并 PR 的成本」算出来?这五个问题里,最容易跳过的是第一个——很多任务表面可拆、实则强耦合,强行并行只会制造冲突和返工。并行的正确姿势不是「能开几条就开几条」,而是「开几条能让合并之后的代码仍然值得信任」。速度是可以买的,可维护性是买不到的。

📌 常见问题 FAQ

Claude Code Projects 的这次改动是什么时候上线的?

据 The Decoder 与 DevOps.com 报道,Anthropic 于 2026 年 9 月 17 日宣布重构 Projects,首批向使用云 session 的部分 Pro 与 Max 订阅者开放 beta,随后逐步扩大到更多 Pro/Max 用户,Team 与 Enterprise 与本地执行在后续路线图中。

每个 thread 是共享一次 session 还是各开一次?

据 DevOps.com 与 The New Stack 的报道,面向仓库的任务中每条 thread 都是一次独立的 Claude Code 云 session,拥有独立的分支与仓库副本,因此可以同时推进而不会彼此覆盖改动。

为什么说这个功能可能「在午饭前烧光套餐」?

因为每开一条 thread 都是一次完整 session,消耗的是同一份额度。The New Stack 指出,协调器会以你打开的线程数那样的速率为你计费,因此并发越多,花费增长越快,而 thread 在你关闭笔记本后仍会继续运行。

多线程并行有哪些成本风险?

主要有三类:并发数直接放大花费;thread 在后台持续运行带来持续计费;以及 fan-in 阶段的合并冲突,会消耗远比 token 更贵的人力,且合并顺序会影响是否需要重跑。

团队应当怎样治理这种并行的成本?

设并发上限、给每条 thread 设预算、为整轮 run 设硬上限并在外部监看、用 git 做合并预检并按纠缠程度排序、并把每轮 run 的线程/ token /花费/合并结果记录下来,长期跟踪每次合并 PR 的成本。