从 vibe coding 到 Agent Orchestrator:一个使用者的视角

17 阅读25分钟

前两天看到Agent Orchestrator 介绍的帖子, 声称可以多 Agent 并行编码,都在自己的 Git worktree中运行。自主修复 CI 、处理代码 review 并提交 PR——您只需在一个控制面板上进行监督即可。而且没有炫酷的 GUI,只是 tmux 中运行,很对我胃口, 所以我研究了一下这个项目的整个工作流程和原理,分享一下:

HGQT6Xca4AATcEy.jpeg

先说一下你现在的工作方式

你打开 Claude(或者 ChatGPT、Cursor、Windsurf、随便什么),描述一个需求:“帮我做一个能同步 Notion 到 Linear 的小工具”。AI 给你一段代码。你复制到本地,跑一下,报错。把错误粘回去。AI 改一版。再跑,再错。改 README 里的某一行。又错。你来回二十轮,终于跑通了,但中间漏掉了写测试这件事。再开一个新会话补测试,发现 AI 已经不记得之前的代码长什么样,你又得把整个项目结构粘一遍。

这就是 vibe coding:和一个 AI 串行聊天,自己当中转站。

它有几个绕不开的痛点:

  • 你的脑子是单核。 一次只能跟进一个对话窗口、一个修改、一个 issue。
  • 上下文是易碎的。 关掉窗口,下次聊天就要从头解释项目;同一个项目的两个独立任务往往要两个独立会话,互相不知道对方的存在。
  • PR / CI / 评审你自己跟。 AI 不知道你的 PR 现在卡在 CI 红灯上,也不知道有人写了个评审让你改三行。
  • 没有”项目状态”这个概念。 五个改动并行进行时,你只能在脑子里维护”哦这个分支等评审,那个分支 CI 在跑,第三个分支我忘了在干嘛”。
  • 卡住了你不知道是为什么卡住。 AI 在等你回复一个权限询问?还是它崩了?还是 CI 跑了三十分钟还没回?你只能猜。

vibe coding 在写一段隔离的脚本时很爽。一旦你想用同样的方式做”完整的应用开发”——多个并行任务、多次代码评审、CI 反馈环、长周期协作——就开始累。

Agent Orchestrator(下文简称 AO)想解决的就是这一段累。它不是又一个 AI 编码工具——它假定你已经有一个或多个 AI 编码工具——它是把这些工具编排成一个并行流水线的层。

AO 总览:八个槽位 + 一个状态机 + 一个仪表板

把 AO 想象成一个生产线主管。它本身不写代码(写代码的事交给 Claude Code、Codex、Aider、OpenCode、Cursor 中的任意一个),但它知道:

  1. 谁在干活(哪些 agent 会话还活着)
  2. 干到哪一步了(每个会话现在的状态:在写代码、在等 CI、在等评审、还是卡住了)
  3. 哪个需要你(哪个会话此刻需要人类介入:批准权限、答复评审、合并 PR)
  4. 后台跑得怎么样(CI 状态、PR 状态、issue 状态——所有外部系统的最新视图)

它通过八个插件槽位做到这一点:

  • Runtime(运行时):agent 跑在哪里。tmux 还是裸进程。
  • Agent(智能体):用哪个 AI 工具。Claude Code / Codex / Aider / OpenCode / Cursor。
  • Workspace(工作区):代码怎么隔离。Git worktree 还是完整 clone。
  • Tracker(任务追踪器):issue 在哪里。GitHub Issues / Linear / GitLab。
  • SCM:PR 在哪里。GitHub / GitLab。
  • Notifier(通知器):有事如何告诉你。桌面通知 / Slack / Discord / webhook / Composio / OpenClaw。
  • Terminal(终端):你想看实时输出时,怎么接进去。iTerm2 标签页 / 浏览器 xterm.js。
  • Lifecycle(生命周期):把上面七个串起来的状态机。这一个不是插件,是 AO 的核心。

加一个 Web 仪表板,看板(Kanban)布局,每张卡片代表一个会话。

image.png

你的工作变成:扫一眼仪表板,处理”respond”列里需要你的卡片,剩下的让 AO 推着往前走。

下面我们走一遍完整的开发流程,看每一步背后 AO 在做什么。

第 0 步:装上、跑起来

npm install -g @aoagents/ao
cd ~/projects/my-app
ao start

你看到的: 浏览器自动打开 http://localhost:3000,看板出现,但所有列都是空的。

AO 在后台做了什么:

  1. 配置加载。 AO 找到(或自动生成)当前目录的 agent-orchestrator.yaml。这个文件描述:默认用哪个 agent、哪个运行时、哪个 tracker / SCM、通知去哪里。第一次跑会进入一个交互式向导,让你选 agent、选 tracker、填几个必填项。
  2. 预检(preflight)。 检查 tmux 装了没、gh auth status 通不通、3000 端口空不空、Web 资源构建过没。任何缺的都给你提示和安装命令。
  3. 插件注册。 根据配置实例化对应的插件:tmux runtime、worktree workspace、claude-code agent、github tracker、github SCM、desktop notifier……
  4. Fork 仪表板进程。 AO 不把仪表板作为它自己的子进程跑,而是 fork 出一个同级进程(sibling),让它脱离当前 shell 独立活下去。这意味着 ao start 跑完之后,ao 命令本身就退出了,但仪表板继续在后台跑。
  5. Fork 生命周期 worker。 同样的”fork 后退出”模式:另一个后台进程,专门跑那个 30 秒的轮询循环。
  6. 写 running.json。 AO 在 ~/.agent-orchestrator/ 下用一个建议锁文件(O_EXCL 创建,5 秒超时回收陈旧锁)维护”当前哪些编排器实例在跑”的注册表。

这一步背后的设计: AO 把”启动器”和”运行时”分开。你的 shell 不需要一直挂着;你重启电脑,下次 ao start 会发现旧进程还在(或者已经死了,自动清理)然后接管。

vibe coding 对比: 你用 Claude 时,关掉浏览器标签页 = 上下文蒸发。AO 的会话和编排器的存活是解耦的:编排器死了,会话还在 tmux 里活着;会话死了,编排器继续管别的会话。

第 1 步:第一次孵化一个 agent

你打开 Linear 看了眼自己的 backlog,挑了一个 issue:INT-123 — 给设置页加深色模式开关。

ao spawn INT-123

或者直接在仪表板的 sidebar 里点这个 issue。

你看到的: 几秒后,看板的 working 列多了一张卡 int-1,标题是从 Linear 拉下来的”给设置页加深色模式开关”,活动指示器(一个绿点)开始闪。点开这张卡,能看到一个内嵌的终端,里面就是 Claude Code 在你的代码上工作的实时画面。

AO 在后台做了什么(这里是最密集的一段):

  1. 生成会话 ID。 AO 用一个确定性算法(项目前缀 + 序号)给会话起名,叫 int-1。下一个就叫 int-2。这样你在 tmux ls 里看到的名字是人能读懂的。
  2. 拉 issue 上下文。 Tracker 插件(这里是 Linear)通过 LINEAR_API_KEY 调 GraphQL,把 issue 标题、描述、标签拉下来。
  3. 三层 prompt 组装。 Prompt 不是直接喂给 agent 一个字符串那么简单:
  • 基础层:核心指令(“你在一个被管理的会话里、按照 Git 工作流推进、PR 标题清晰、评审及时回应”),写死在 core 里
  • 配置层:项目名、默认分支、仓库地址、tracker 信息、issue 上下文(用 tracker.generatePrompt(issueId) 生成)
  • 用户规则层:你项目里的 agent-rules.md(如果有)
  1. 创建工作树。 Workspace 插件跑 git worktree add -b feat/INT-123 ~/.worktrees/my-app/int-1。新分支基于 origin/main 拉,工作树是物理上的另一个目录,和主仓库共享 .git/objects(节省磁盘)。
  2. 跑 postCreate 钩子。 如果你的项目配置了 npm install / pnpm i 这种创建后命令,会在工作树里跑一次。也可以配置软链接(symlink)把主仓库的 node_modules 链过去,避免每个会话装一遍依赖。
  3. 安装钩子机制。 这是 AO 的一个关键设计。它要让 agent 跑 gh pr create 时,能”无感地”把生成的 PR URL 记到会话元数据里。两种做法:
  • Claude Code:在工作树写 .claude/settings.json,注册一个 PostToolUse 钩子,agent 每次执行 Bash 工具后这个钩子被调用。
  • 其他 agent(Codex / Aider / OpenCode / Cursor):在 ~/.ao/bin/ 装两个 shell 脚本 gh 和 git,把它放在 PATH 最前面。Agent 跑 gh pr create 时,实际跑的是这个垫片,垫片 tee 一份输出,正则提取 PR URL,写到元数据,再把真正的 gh 的输出原样转发。
  1. 创建 tmux 会话。 Runtime 插件跑 tmux new-session -d -s int-1 -c -e AO_SESSION_ID=int-1 -e AO_ISSUE_ID=INT-123 ...。这是个 detached 会话,预先把环境变量塞进去。
  2. 启动 agent。 Runtime 通过 tmux send-keys 把启动命令打进 tmux 会话。命令长度 < 200 字符直接发;> 200 字符写到一个自删除的临时 shell 脚本,然后 send-keys 一句 bash /tmp/ao-launch-xxx.sh,避免 send-keys 对长字符串的限制。
  3. 写元数据文件。 ~/.agent-orchestrator/{hash}-my-app/sessions/int-1 是一个文本文件,每行 key=value:branch=feat/INT-123、worktree=...、status=spawning、tmuxName=...。原子写:临时文件 + rename。
  4. 返回。 ao spawn 命令打印 SESSION=int-1 然后退出。仪表板的下一次 SSE 快照(5 秒内)会发现新会话,刷出卡片。

整个过程通常 5-10 秒。

vibe coding 对比: 你启动一个新对话要做:打开新窗口、把项目结构粘进去、描述这个 issue 在做什么、提醒 AI”按照我们的代码风格”、提醒 AI”要写测试”、提醒 AI”用 feat 分支不要直接改 main”、提醒 AI”做完发 PR 别忘了”。AO 把这套”提醒”全部固化到 prompt 组装和钩子里,每个 agent 自动遵守。

第 2 步:agent 在干活,你在干别的

Agent 启动了,你不需要盯着它。你可以:

ao spawn INT-124
ao spawn INT-125
ao spawn INT-126
ao spawn INT-127

或者一句:

ao batch-spawn INT-124 INT-125 INT-126 INT-127

仪表板的 working 列瞬间多出五张卡,每张卡里都有一个 Claude Code 在并行干活。

AO 在后台做了什么:

每个会话独立的 tmux + 工作树 + agent 进程。它们互不知道对方存在。生命周期 worker 的 30 秒轮询会同时检查这五个会话:

poll cycle (every 30s)
 ├── [int-1] runtime alive? ✓ activity? active. status: working
 ├── [int-2] runtime alive? ✓ activity? active. status: working
 ├── [int-3] runtime alive? ✓ activity? waiting_input. status: needs_input ← 通知!
 ├── [int-4] runtime alive? ✓ activity? ready (idle 1m). status: working
 └── [int-5] runtime alive? ✗ status: killed

int-3 撞到了一个权限询问(“是否允许 npm install 一个新依赖?”),活动状态是 waiting_input,会话状态变成 needs_input,仪表板把这张卡从 working 列移到 respond 列,同时桌面通知器弹出一个系统通知:“int-3 需要你的输入”。

int-5 死了——也许是 Claude Code 内部出错,也许是 tmux 会话被外部 kill。状态变成 killed,卡片移到 done 列(带”已死亡”标注,可点 restore 重启)。

轮询循环里 AO 做的事:

  1. 拉所有会话列表(从扁平文件,几乎不耗时)。
  2. 过滤掉终态会话(merged / killed / done 这些不再变了,跳过减少负担)。
  3. 批量查询所有 PR:把所有未关闭 PR 的信息一次性用 GraphQL alias 批量取回(最多 25 个一批)。这个一次能省几十次 API 调用。
  4. 2 道 ETag 守门:
  • Guard 1:GET /repos/{owner}/{repo}/pulls?state=open&sort=updated,带上次拉到的 ETag。如果返回 304 Not Modified,意味着这个仓库的所有 PR 都没动,整个 GraphQL 批量查询都跳过,直接用缓存。
  • Guard 2:对每个有 pending CI 的 PR,单独查 GET /commits/{sha}/status 的 ETag。304 = CI 没翻转,跳过。
  1. 对每个会话跑 6 步级联:runtime 还活着吗(tmux has-session)→ 活动状态是什么(agent 插件的 4 层活动检测级联)→ PR 检测到了吗(按分支名查 PR)→ PR 子状态机(PR 开着?合并了?关闭了?CI 失败?变更被请求?可合并?)→ Post-PR 停滞检测(idle 超过阈值 → stuck)→ 默认 fallback。
  2. 状态变了 → 发事件 → 触发反应:CI 失败 → reaction ci-failed → 把失败 detail 通过 tmux send-keys 喂给 agent:“你的 CI 失败了,这是 detail:……请修复。” 评审请求变更 → reaction changes-requested → 把评审评论发给 agent 让它修。评审通过 + 可合并 → reaction mergeable → 通知人类(或者你配置成自动合并)。

关于成本控制的一个有趣数字: 10 个会话,30 秒轮询:不优化的话 10 × 20 个 API 点 × 120 次/小时 ≈ 24,000 点/天;用批量 + 2 道 ETag 之后,满缓存命中时 2 × 120 = 288 点/天。GitHub 的 GraphQL 限流是每小时 10,000 点。这意味着默认 vibe coding 式的”全量轮询”很快就被限流,AO 的设计让你能开几十个会话不撞限流。

vibe coding 对比: 你跑 5 个并行任务的版本是开 5 个浏览器标签页,自己在中间切换。你脑子里要记住”哦标签 3 在等评审”、“标签 5 在跑 npm test”、“标签 4 卡了我得回去看看”。AO 把这套切换给了机器,你只看仪表板。

第 3 步:PR 出来之后

int-1 干了 8 分钟,agent 发现自己写完了,跑:

gh pr create --title "feat: 设置页深色模式开关" --body "..."

钩子拦截: PATH 里的 ~/.ao/bin/gh 垫片接到这个调用,把它转发给真正的 gh,但同时 tee 一份 stdout。gh pr create 成功后,stdout 里会有一行 PR URL 像

github.com/me/my-app/p…

。垫片用正则把这个 URL 抓出来,调用元数据辅助函数:

update_ao_metadata pr "

github.com/me/my-app/p…

" update_ao_metadata status "pr_open"

这两行原子地(写临时文件 + rename)把 pr= 和 status=pr_open 写到会话元数据里。

下一次轮询(最长 30 秒后): 生命周期 worker 读到 pr=https://...,调 scm.detectPR(session) 确认 PR 存在,开始跑 PR 子状态机。PR 状态 open / CI 状态 pending / 评审决定 none / 可合并 false → 状态 pr_open。仪表板把卡片从 working 移到 pending。

15 分钟后 CI 跑完,假设过了。下一次轮询:PR 状态 open / CI 状态 passed / 评审决定 none / 可合并 true → 状态 approved(如果配置了”无评审者也算通过”)或 review_pending(如果有指定评审者还没回)。

如果是 review_pending,AO 不主动催评审,等。如果是 approved 且 mergeable,状态变 mergeable,卡片移到 merge 列,桌面通知”int-1 可以合并了”。

评审来了,假设 reviewer 写了三条评论:“这个变量名改一下”、“这里漏了空状态”、“别忘了文档”。 下一次轮询:评审决定变成 changes_requested → 触发 reaction changes-requested → AO 拉所有评审评论 → 用指纹去重(fingerprint = 评论 ID 的集合):如果指纹和上次一样就跳过(已经发给 agent 过了);如果指纹变了就发给 agent。

把评论拼成一段话,通过 tmux 喂给 agent:“你的 PR 收到了变更请求,评审者说:1. xxx 2. xxx 3. xxx。请处理后推一个新提交。”

Agent 改完,git push。下一次轮询发现 head SHA 变了,CI 重新跑,状态又回到 pr_open。

这里的关键设计:你完全没动手。 你做的全部事情就是:偶尔扫一眼仪表板,对那些卡在 merge 列的 PR 点一下”merge”。

Review 走向 + 人类决策点

上面这一节”你完全没动手”容易让人以为 AO 在 CI 通过后就自动合并了。这是误读。展开讲。

Review 走的是 GitHub / GitLab 原生评审

AO 没有发明自己的评审系统。评审者用 gh pr review --approve、在 GitHub 网页上点 Approve、或者在代码行上留 comment——这些动作通过 GitHub 本身发生,和没用 AO 时一模一样。AO 在评审这件事上只做两件翻译工作:

翻译 1:把评审结果变成状态机事件。 评审者提交评审之后,下一次 30 秒轮询(或者通过 ETag 守门免费发现的变化)拉到新的 reviewDecision,AO 把会话状态从 review_pending 迁到 changes_requested 或 approved,对应的卡片在仪表板上换列。

翻译 2:把评审评论自动喂回 agent。 当状态变成 changes_requested 时,changes-requested reaction 被触发。AO 跑:

  1. scm.getPendingComments(pr) 把所有未解决的行内评论 + 整体 review body 拉下来
  2. 用指纹(评论 ID 集合)和上次 dispatch 的指纹比,变了才发;没变就跳过(避免每 30 秒反复骚扰 agent)
  3. 把新评论拼成一段消息,通过 tmux send-keys 打进 agent 的会话:

Your PR got a change request. The reviewer said: 1. [src/api/auth.ts:42] "This variable name is misleading — rename to expiresAt" 2. [src/api/auth.ts:78] "Missing null check before dereferencing" 3. [overall] "Please also add a test for the expired-token path" Please address and push a new commit.

Agent 读到这段,开始修。修完推,CI 重跑,回到 pr_open 状态,评审者审下一轮(或者 GitHub 的”push 后自动重新请求评审”接手)。

你没做的事: 把评论复制粘贴到和 AI 的聊天窗口里。你做的事: 点 Approve 或者 Request changes。

评审者可以是人,也可以是 agent

AO 支持把一个 agent 注册为 GitHub 的指定 reviewer。社区里有 review agent 的配置模板:它在 PR 打开时被触发,自己 checkout 代码、用 Claude 对 diff 跑一次代码评审、通过 gh pr review --comment/--approve/--request-changes 把结果发回去。从 AO 的视角看,这个 review agent 和一个人类评审者没区别——都是发 PR review 的第三方。

组合起来最有意思的用法:

  • 第一级评审 = review agent,负责抓显而易见的问题(风格、未处理 edge case、missing test)
  • 第二级评审 = 人类,只看 review agent 放过的 PR

于是 20 个 PR 里人类只看 2-3 个真正需要判断的,剩下的在 agent 级被挡掉或自动修掉。这不是 AO 强制的模式,但它的插件 / 反应架构支持你这么搭。

哪些决策 AO 不替你做

即使你打开了所有自动化、配好了 review agent、装了 auto-merge reaction,有几件事 AO 默认不替你决定:

  • 合并 PR 到 main —— 人类点 merge。合并是写入主干,错了不可逆(git revert 不算真的逆)。AO 的默认立场:CI 和评审是必要条件不是充分条件。你点 merge 的那一下是”我看过了 / 我负责”的显式签名。
  • 关闭一个 issue 不做 —— 人类。Agent 不应该有权判断”这个需求不做了”。
  • Approve 自己 agent 的 PR —— 不允许 agent 自审自 approve。GitHub 本身就禁止,AO 顺着这个。如果你要用 review agent,它必须是一个独立的 GitHub 身份。
  • 改生产配置 / 动密钥 —— 人类(通过工作树外的通道)。Agent 的工作树是隔离的,它看不到你没主动放进去的 secrets。AO 没有”把 .env 自动传给 agent”这种功能,是有意的。
  • 修改 agent-orchestrator.yaml 自己 —— 人类。Agent 不应该有权重写编排器的配置。这是一个隐性边界:agent 跑在工作树里,工作树里没有 AO 配置。
  • 决定停掉一个 stuck 的会话 —— 人类。AO 会通知你 stuck,但不会自动 kill。原因:stuck 有时是 agent 在思考困难的 edge case,kill 掉等于丢掉它已经做的工作。
  • 跨项目的优先级排序 —— 人类。哪个 issue 先做、哪个先合,AO 不管。它只管每个 issue 被正确推进。

如果你真的想让 AO 自动合并

这是一个明确的 opt-in。在 agent-orchestrator.yaml 里加一条 reaction:

reactions: approved-and-green: action: auto-merge requires: - ci: passing - reviewDecision: approved - mergeable: true # 可选的更严格条件: - approvedBy: [senior-dev] # 必须是这个账号批准 - minApprovals: 2 # 至少两人批准 - noUnresolvedThreads: true # 所有评审线程都已 resolve

满足所有 requires 条件时,AO 调 gh pr merge --squash --delete-branch(合并方式可配置),完成后卡片直接进 done 列。

什么时候开 auto-merge 是合理的: 有高覆盖率 CI(测试真的能抓到回归);有严肃的代码评审文化(review agent + 人类二审);改动范围受限(比如这个 repo 只有 docs/ 下的改动允许 auto-merge,代码路径不行);有快速 rollback 能力(git revert + CI 重跑 + 重新部署在一小时内能完成)。

什么时候绝对不要开: 任何写入生产数据库 schema 的 migration PR;任何涉及金额计算 / 权限 / 加密的代码路径;你对 CI 覆盖率没信心时;你的 agent 被配置成 --dangerously-skip-permissions(Claude Code)/ --yes(Aider)这类”跳过所有确认”的模式时——这种配置加上 auto-merge 是一把可以单键毁掉 main 分支的枪。

我自己用 AO 时只对文档 PR 开 auto-merge。代码路径一律人点 merge,哪怕我 99% 信任 agent。这一秒的手动确认,换来的是一个”我看过”的心理负担,它会让我偶尔发现”哦等一下,这个 diff 有点意外”。那种偶尔的发现值这一秒。

第 4 步:当事情出错

并行流水线里事情一定会出错。AO 的设计假设这一点。

情况 A:agent 卡在权限询问。 Claude Code 想跑 rm -rf node_modules,弹出”是否允许?“。Activity 检测器看到这个提示,标记 waiting_input。卡片移到 respond 列,桌面通知 + Slack 通知一起发。你点开卡片,看到内嵌终端的实时画面,里面就是那个询问。你直接在终端里敲 y(仪表板的终端通过 WebSocket 把按键发回 PTY)。Agent 继续跑。

情况 B:agent 跑了 30 分钟没动静。 活动检测器持续看到”无新输出”,最后一次活动时间戳超过 5 分钟(stuck 阈值)。状态变 stuck。通知触发。你点开终端,发现 agent 在死循环——它一直在改一个文件,每次都说”现在我重新尝试”。你 ao send int-3 "停下来,先告诉我你目前的理解" 给它一个外部干预。或者直接 ao session kill int-3,从头再来一次。

情况 C:agent 进程死了(killed)。 Tmux 会话还在但 Claude Code 进程退出了。状态 killed,卡片移到 done。你可以 ao session restore int-3 让它重启。Restore 利用 agent 插件的 getRestoreCommand:Claude Code 用 claude --resume 接回它原来的 JSONL 历史;Codex 用 codex resume 。Aider 和 Cursor 不支持 resume,restore 就是重新启一次(agent 看到的工作树里已经有它之前的提交)。

情况 D:编排器进程崩了。 这是最有意思的一种。什么都不会立即发生。Tmux 会话还在跑(tmux 是独立进程,编排器死了不影响);Agent 还在干活(它通过 PATH 垫片更新元数据,根本不需要编排器);PR 状态变化、CI 跑通——agent 的钩子继续正确写 pr=... status=merged;仪表板的 SSE 流断了,浏览器显示”重连中”。你 ao start 重新启动。新编排器从扁平文件读所有会话,第一次轮询把所有外部状态重新查一遍(这是冷启动成本,但只这一次),把世界状态对齐,仪表板 SSE 接回,浏览器自动恢复。

核心设计:每个插件都是跨运行无状态的。 没有缓存依赖跨进程一致性。崩溃恢复就是冷启动。代价是第一次轮询的 API 成本,收益是不需要任何持久化协议。

第 5 步:合并、归档、清理

PR int-1 终于点了 merge。Agent 的钩子捕获 gh pr merge 调用,把 status=merged 写进元数据。下一次轮询:状态 merged → 触发 reaction none(merged 是终态,agent 任务结束)→ 卡片移到 done 列。

自动清理(如果你配置了): ao session cleanup 会扫所有终态会话,对每个:把元数据归档到 ~/.agent-orchestrator/{hash}-my-app/archive/int-1_2026-04-14T...;销毁 tmux 会话;不删除分支(这是个深思熟虑的决定:万一你想回头看历史;万一某个分支名碰巧和你已有的分支重了,自动删除会误伤);销毁工作树(git worktree remove --force)。

工作树没了,但分支留在仓库里。git branch --merged 之后可以单独清理。

vibe coding 对比: 你合并 PR 之后还要切回 main 并 pull、删除本地分支、删除远程分支、关闭 issue、可能还要在 Linear 里手动改状态。AO 的清理是命令一句、配置自动。如果你的 tracker 配置了”merged 时自动关闭 issue”,连 issue 都不用动。

Vibe coding vs AO

  • 并行度:vibe coding 是 1(你的脑子是单核);AO 是任意(你看仪表板,agent 自己跑)。
  • 上下文持久化:vibe coding 关窗口就没;AO 用扁平文件 + tmux,跨重启幸存。
  • Prompt 一致性:vibe coding 每次手动重新交代;AO 三层组装,规则在配置里。
  • PR 跟踪:vibe coding 你自己刷 GitHub;AO 30 秒轮询 + ETag 守门。
  • CI 失败处理:vibe coding 是你看到→复制 log→粘给 AI→改→推;AO 的反应自动把 detail 喂给 agent。
  • 评审处理:vibe coding 是你看→复制评论→粘给 AI→改;AO 的反应自动把评论喂给 agent。
  • 卡住 / 出错:vibe coding 你回过头才发现;AO 通知主动来找你。
  • 多 issue 切换:vibe coding 是多浏览器标签 + 头脑切换;AO 看板 6 列,扫一眼就知道哪个需要你。
  • 实时介入:vibe coding 仅在当前对话窗;AO 任何会话点开就有实时终端。
  • 失败隔离:vibe coding 一个挂全员挂(你脑子崩);AO 用 Promise.allSettled,单个挂不影响其他。

心智模型的转变

最关键的不是工具,是你的角色。

Vibe coding:你是操作员。 AI 是工具。你说一句、AI 回一段、你判断、你复制、你粘贴、你跑、你看结果。AI 不会主动找你。

AO:你是主管。 AI 是员工。你分派任务(spawn)、看报表(仪表板)、处理升级(respond 列)、签字放行(merge)。员工自己跑流程:开 PR、跟 CI、回评审。只有真正需要决策的事才升级到你。

这个转变带来一些不直观的副作用:

  1. 你会”看见”得更多。 之前 vibe coding 时你不知道一个 PR 卡了多久;现在仪表板的 pending 列里那张卡的时间戳告诉你”卡了 2 小时”。
  2. 你会”做”得更少。 你从”复制粘贴胶水机器”退化成偶尔点几下 lgtm 的人。这一开始会让你不安。
  3. 你会更关心系统设计。 既然写代码的是十个并行 agent,你的精力会从”如何把这一段 React 写对”转向”如何把任务分解成十个独立 PR”。这其实是一种更有杠杆的思考方式。

什么时候不要用 AO

诚实地讲,AO 不是万能的:

  • 修一个错别字: gh issue list && vim && gh pr create 比启动一个 agent 快。
  • 探索式编程: “我不知道我要什么,我边写边想”。这种场景 vibe coding 更自然,AO 的批量孵化和 PR 流水线开销大于收益。
  • 强一致性协调: 两个 agent 改同一个文件、需要锁。AO 通过 git 分支隔离,本身不做细粒度协调。
  • 多租户 SaaS: AO 假设你是单个用户在自己的机器上跑十几个会话。一千个会话 / 一百个租户,AO 的扁平文件、桌面通知、单进程编排器都会撑不住。
  • 极短周期: 工作单元是毫秒级的事情,30 秒轮询是灾难。

AO 的最佳工况:单人或小团队,每天 5-30 个并行 issue,长周期任务(一个会话存活数分钟到数小时),外部系统(GitHub / Linear)是真相之源。如果你的工作长得像这种形状,AO 会让你的并行度从 1 跳到 10+,并且把 80% 的杂活给消掉。

一个完整开发日的样子

最后给一个具象的例子。

早上 9:30: 打开电脑,ao status 看一眼昨晚的进度。三个 PR 已经合并自动归档了。两个还在 pending(CI 在重跑)。一个 respond 列:int-7 收到了一个评审评论,agent 已经修了重推,但需要你点 lgtm 触发自动合并。

9:35: 看一眼 Linear backlog,挑了 5 个 issue:

ao batch-spawn INT-130 INT-131 INT-132 INT-133 INT-134

working 列瞬间多了 5 张卡。

10:00: 桌面通知响:“int-2 需要输入”。点开仪表板,看到内嵌终端:agent 在问”是否允许覆盖现有的 package-lock.json?“。你敲 y。

10:30: Slack 弹通知:“int-130 的 CI 失败”。AO 已经把失败 detail 喂给 agent 了,状态显示 agent 在重新工作。你不用做任何事。

11:15: 仪表板 merge 列出现两张卡。点 merge。AO 调 gh pr merge --squash --delete-branch,下一次轮询发现合并,把卡片移到 done,标记自动清理。

12:00: 午饭。

14:00: 早上五个 issue 现在状态:1 个 merged;2 个 pending(评审中);1 个 changes_requested(agent 在重工作);1 个 stuck(你点开发现 agent 在思考一个边缘 case,写了三个版本都不对,你 kill 它,写一句更具体的描述重新 spawn)。

你下午又添 4 个新 issue。一天结束时你合并了 8 个 PR,处理了大概 12 个升级,自己写代码的总时间大约 40 分钟(修改 2 个 agent 没搞定的边缘 case)。

这种工作日在 vibe coding 模式下不可能存在。你的瓶颈是上下文切换。AO 把切换的代价从”打开新标签 + 重述项目”压缩到”扫一眼仪表板”。

收尾

vibe coding 是一段对话;AO 是一条流水线。

如果你的日常是写一段一段的隔离脚本,vibe coding 够了。如果你在维护一个真正的应用——多个 issue 并行、每天有评审和 CI、希望自己的注意力用在决策而不是搬运——AO 把那一摊机械操作收编成一台机器,让你回到主管位置。

最重要的不是”用了 AO 就能 10x”。最重要的是它改变了 AI 编码的极限是什么。Vibe coding 的极限是你的脑容量;AO 的极限是你的判断力。前者很快撞墙;后者要慢得多。