这篇能做出什么
先看结果。做完之后你会有一个这样的工作台:
一个 Git 仓库,外面挂着 2~4 个隔离的工作副本(git worktree)。每个副本里跑着一个不同的编码 Agent——Claude Code 跑一份,Codex 跑一份,如果有第三个也照此办理。同一张任务卡,一次性分派给所有 Agent,它们并行开工互不干扰。跑完之后,产物统一落到 runs/ 目录下:每个 Agent 一份 diff.patch、一份运行日志、一份验收测试输出。你在一张界面上并排看这些 diff,挑出最靠谱的那份合回主干,其余的工作区和分支清理掉。
换句话说,把一个"人跟一个 Agent 来回聊"的流程,改造成"一个人开一个班次,多个 Agent 同时上交作业,你来当评审"。
需要先说明一点:Offrun 的界面布局、配置字段名、子命令会随版本变化,本文涉及的 Offrun 专属命令与字段都写成示意形式,实际使用时以官方文档当前版本为准。真正可以直接照抄的部分是 Git worktree、diff、测试这些通用动作——它们不依赖任何具体版本。
---
前置条件清单
动手前把这些确认好,能省掉后面一大半排错时间:
1. 一个 Git 仓库,能 clone、能建分支,主干是干净的(git status 无输出)。脏主干会让后面的 diff 比对变成一团乱麻。
2. 各个编码 Agent 的 CLI 已经装好,并且能单独跑通。 Claude Code、Codex CLI 这类工具的安装方式通常是 npm 包或独立二进制,以官方文档当前版本为准。
3. 各家的凭据都已配置(登录态或 API Key)。这是后面并行时最常见的翻车点——单跑没问题,一起跑就撞配额。
4. Offrun 已经安装并能启动。
5. 一条能验证改动的命令。 比如 npm test、pytest -q、go test ./...。没有这条命令,你只能靠肉眼看 diff 判优劣,风险很高。
6. 足够的磁盘空间。 每个 worktree 是一份完整工作副本,如果再各自装一遍依赖,占用会翻好几倍。
---
分步骤实操
第 1 步:确认每个 Agent 能单独跑通
先别急着上 Offrun。一个一个来,把凭据和网络问题提前暴露掉:
```bash
逐个检查 CLI 是否在 PATH 里(具体命令名以官方文档为准)
which claude
which codex
```
然后在项目里让每个 Agent 干一件小事,比如"读一下 README,用一句话总结这个项目是做什么的"。确认每个都能返回结果、都能读写文件。
这一步的意义在于:如果并行阶段某个 Agent 卡住了,你能立刻判断是"它本身有问题"还是"并行编排有问题"。全混在一起排查会非常痛苦。
第 2 步:给每个 Agent 准备隔离的工作区
这是整套流程里最关键的一步。不要让多个 Agent 在同一个目录里同时改代码——它们会互相覆盖文件,最后你拿到的 diff 是一锅粥,根本分不清谁的功劳。
git worktree 是原生解决方案:同一个仓库,多个独立的工作目录,各自挂在自己的分支上。
```bash
cd ~/projects/myapp
确保主干是新的
git switch main
git pull --ff-only
git status --short # 应该没有输出
建一个放工作副本的目录,放在仓库外面,避免被 git 跟踪
mkdir -p ../myapp-runs
给每个 Agent 建一个隔离工作区
git worktree add ../myapp-runs/run-a -b run/agent-a main
git worktree add ../myapp-runs/run-b -b run/agent-b main
确认
git worktree list
```
如果要反复用,把它写成一个脚本 make-runs.sh:
```bash
#!/usr/bin/env bash
set -euo pipefail
REPO=~/projects/myapp
RUNS=~/projects/myapp-runs
STAMP=$(date +%Y%m%d-%H%M)
cd "$REPO"
git switch main
git pull --ff-only
mkdir -p "$RUNS"
for name in a b; do
path="$RUNS/run-$name-$STAMP"
branch="run/$name-$STAMP"
git worktree add "$path" -b "$branch" main
echo "created $path on $branch"
done
```
建好之后,把不进版本库的东西同步过去——.env、本地配置文件、证书等。依赖目录可以直接软链,省磁盘也能省安装时间:
```bash
ln -s ~/projects/myapp/node_modules ~/projects/myapp-runs/run-a/node_modules
```
(如果 Agent 可能升级依赖,就别软链,老老实实各装一份。)
第 3 步:在 Offrun 里登记这些 Agent
打开 Offrun,把每个 Agent 登记成一个"执行器"。核心要填三样东西:启动命令、工作目录、产物输出位置。
下面是一份示意配置,文件名、字段名、命令参数都以官方文档当前版本为准,这里只表达结构:
```yaml
示意配置:字段名请以 Offrun 官方文档为准
agents:
- id: agent-a
cmd: ["<agent-a-cli>", "<非交互参数>", "{{task_file}}"]
workdir: /home/you/projects/myapp-runs/run-a
artifacts: /home/you/projects/myapp/runs/agent-a
timeout: 1800
- id: agent-b
cmd: ["<agent-b-cli>", "<非交互参数>", "{{task_file}}"]
workdir: /home/you/projects/myapp-runs/run-b
artifacts: /home/you/projects/myapp/runs/agent-b
timeout: 1800
```
两个要点:
cmd里放的是非交互模式的调用方式。你在第 1 步已经验证过哪条命令能让它一次性把活干完、不需要人工点确认。如果它半路停下来等你按回车,并行就废了。timeout一定要设。Agent 卡死时,没有超时的任务会一直占着工作台。
如果 Offrun 的操作方式是界面点选而不是配置文件,那就把上面这些信息在界面里一一填入,逻辑是一样的。
第 4 步:写一张任务卡
多个 Agent 各自是一个独立会话,互相看不见对方在做什么,也看不见你脑子里的上下文。所以任务卡必须自包含——把背景、目标、边界、验收标准全写进去。
建一个 tasks/fix-login-timeout.md:
```markdown
任务:修复登录接口偶发超时
背景
src/api/login.ts 里的登录请求在并发较高时会超时,
错误日志中可见连接池耗尽的提示。相关代码集中在 src/api/ 目录。
目标
让登录接口在 50 并发下稳定返回,不出现超时错误。
验收标准
1. npm test -- login 全部通过
2. 新增至少一个覆盖并发场景的测试用例
3. 不修改 src/api/login.ts 以外的公开接口签名
允许改动的范围
- src/api/login.ts
- src/api/__tests__/
不要做的事
- 不要跑全量格式化工具(prettier/eslint --fix),会产生大量无关 diff
- 不要重构无关模块
- 不要升级任何依赖版本
交付物
- 代码改动
- 在
NOTES.md中写:你做了什么、为什么这么做、有哪些不确定的地方
```
最后那条"写 NOTES.md"很值得保留。它逼着 Agent 把思路说出来,你在比对两份 diff 时,这份说明往往比 diff 本身更好判断。
第 5 步:分派并并行推进
一份任务卡,同时发给所有已登记的 Agent。示意命令:
```bash
具体子命令以 Offrun 官方文档为准
offrun run --task tasks/fix-login-timeout.md --agents agent-a,agent-b
```
或者在 Offrun 界面里选中任务卡、勾选多个 Agent、点运行。
并行度建议:第一次只开两路,跑顺了再加到三到四路。 原因很实际——并行意味着同一时间有多个 Agent 在消耗你的 API 配额、CPU、磁盘和注意力。开十路并不会让产出变成五倍,反而会让评审环节变成瓶颈。
跑起来之后,Offrun 的界面里应该能看到每路的状态:运行中、已完成、失败、超时。
第 6 步:回收产物并比对
先统一产物目录结构,后面写脚本、做归档都方便:
```
runs/
20250601-1030/
agent-a/
diff.patch
agent.log
tests.txt
NOTES.md
agent-b/
diff.patch
agent.log
tests.txt
NOTES.md
```
如果 Offrun 会自动收集,检查一下是否完整;如果没有,用一段脚本兜底:
```bash
#!/usr/bin/env bash
set -uo pipefail
RUNS=~/projects/myapp-runs
OUT=~/projects/myapp/runs/$(date +%Y%m%d-%H%M)
for pair in "agent-a:run-a" "agent-b:run-b"; do
name="${pair%%:*}"
dir="${pair##*:}"
workdir=$(ls -d "$RUNS/$dir"-* 2>/dev/null | tail -n 1)
[ -z "$workdir" ] && { echo "skip $name: no workdir"; continue; }
mkdir -p "$OUT/$name"
1) 完整的改动快照
git -C "$workdir" add -A
git -C "$workdir" diff --cached > "$OUT/$name/diff.patch"
git -C "$workdir" diff --cached --stat > "$OUT/$name/stat.txt"
2) 跑验收测试,把结果和退出码都记下来
( cd "$workdir" && npm test ) > "$OUT/$name/tests.txt" 2>&1
echo "exit=$?" >> "$OUT/$name/tests.txt"
3) 把 Agent 自己写的说明捞出来
cp "$workdir/NOTES.md" "$OUT/$name/NOTES.md" 2>/dev/null || true
done
echo "artifacts at $OUT"
```
回收阶段最容易被忽略的是测试结果。 只看 diff 是不够的:一份改得很漂亮但跑不过测试的方案,价值是零。先看 tests.txt 的最后几行和退出码,把不通过的直接划掉,剩下的才值得细看。
比对两路 diff 的几种方式:
```bash
看改动规模对比
cat runs/*/stat.txt
直接看两份 patch 的差异
diff -u runs/agent-a/diff.patch runs/agent-b/diff.patch | less
快速扫一遍所有测试结论
for d in runs/*/; do echo "===== $d"; tail -n 5 "$d/tests.txt"; done
```
有一个特别好用的技巧:给两份 diff 各起一个临时分支,用 git range-diff 看它们的差异,或者干脆在 Offrun 界面里左右并排看。重点比对四件事:
1. 改动范围——有没有人顺手改了无关文件
2. 测试是否通过
3. 思路差异——同样的问题,一个改连接池配置,一个加重试,这是两种取舍
4. NOTES 里的不确定项——这是最值得你花时间读的部分
第 7 步:合回主干并清理
选定之后,把胜出的那份合进来:
```bash
cd ~/projects/myapp
git switch main
取出胜出工作区里的改动,squash 成一个提交
git merge --squash run/agent-a-20250601-1030
git commit -m "fix(api): 修复登录接口并发超时"
清理工作区
git worktree remove ~/projects/myapp-runs/run-a-20250601-1030
git worktree remove ~/projects/myapp-runs/run-b-20250601-1030
git worktree prune
删掉实验分支
git branch -D run/agent-a-20250601-1030
git branch -D run/agent-b-20250601-1030
```
另外,把产物目录排除在版本控制之外——日志里可能带 token:
```bash
echo "runs/" >> .gitignore
```
---
常见坑与排错
多个 Agent 改同一个目录,产物互相覆盖。
症状:diff 里出现你没见过的改动,或者某个 Agent 说"文件已被修改"。
处理:必须用 worktree 隔离,一个 Agent 一个目录。这是硬性要求,没有折中方案。
Agent 半路停下来等确认。
症状:任务一直"运行中",日志最后一行是权限询问。
处理:改用非交互模式启动;或者在配置里预先放开必要的权限范围。更稳妥的做法是把 Agent 放在容器或临时目录里跑,降低风险敞口。
产物目录是空的。
检查三件事:workdir 路径对不对;Agent 是否真的动了文件(git -C <workdir> status --short);.gitignore 是否把改动文件排除了,导致 git add -A 什么都没抓到。
diff 大得离谱,几千行。
多数是格式化工具全量重排,或者锁文件被重写。在任务卡的"不要做的事"里明确禁止跑全量格式化,并且把 package-lock.json、poetry.lock 这类文件列进禁止改动清单。
某一路失败,其他路正常。
先单独到那个 workdir 里手跑一遍 Agent 命令。九成是凭据问题——比如某个 Agent 用的是环境变量,而 Offrun 启动的子进程没继承到。把变量显式写进配置里。
worktree 删不掉,提示有未提交改动。
用 git worktree remove --force;如果目录已经被手动删了,用 git worktree prune 清掉记录。
跑完发现 API 配额被吃光。
并行是配额放大器。先从小任务、两路并行开始,摸清楚每次跑的消耗量再放量。
---
下一步建议
把任务卡模板化。 建一个 tasks/_template.md,固定写上背景、目标、验收标准、允许范围、禁止事项、交付物这六段。第一次写会嫌麻烦,写到第五次你会发现它决定了整批产物的质量下限。
加一个"评审 Agent"。 让第三个 Agent 读两份 diff,输出一份对比报告:各自的取舍、潜在风险、推荐哪一个。你自己还是要拍板,但它能帮你省掉第一轮筛选。记得评审 Agent 不要跑在任何一个参赛 workdir 里,单独开一个干净目录。
把验收命令固化进 CI。 回收阶段的 npm test 应该和 CI 跑的是同一条命令。两边不一致时,你在本地判定的"通过"到了合并后可能翻车。
给每个 Agent 建一张成绩单。 记录它在不同类型任务(修 bug、加测试、改文档、重构)上的表现:通过率、改动范围是否克制、需要人工返工几次。攒上一两个月,你就有了自己的选型依据,任务来了知道该派给谁。
最后,别急着扩大并行度。 这套工作台真正的瓶颈从来不是 Agent 跑得不够快,而是你能认真评审的产物数量有限。两路并行的稳定产出,比五路并行加一堆没人细看的 diff 更值钱。
