基于OpenCode的多Agent代码开发工作流:实践与效果

基于OpenCode的多Agent代码开发工作流:实践与效果

这是系列第二篇。第一篇《基于OpenCode的多Agent代码开发工作流:设计与搭建》讲了架构和机制,这一篇讲它跑起来的真实表现:pulse-pet V1 的完整开发复盘、四个压力案例、质量与成本数据、以及实践如何反过来改了设计。

相关地址:pulse-pet 项目(lab 仓库 pulse-pet/ 目录)| 设计与测试用例:DESIGN.mdTEST-CASES.md| 8 个任务的完整检查点存档:.opencode/workflows/

1. 实验设定

被开发的东西:pulse-pet

一个 macOS 桌面宠物(Tauri 2 + React + TypeScript):桌面上有一只像素小猫,实时反映 opencode 编码 agent 的状态(thinking/editing/testing/waiting-permission/error…);控制面板展示 token 用量统计(直读 opencode 的 SQLite);定时提醒(喝水/休息)带全屏烟花庆祝;有 Todo 插件和双语界面。技术栈 Rust + TS 双侧,GUI 应用的验收天然带大量”要看要点的”环节——选它当工作流的试验场,就是故意的:GUI E2E 是对自动化验证最不友好的品类,能跑通它,跑通别的只是工作量问题

前置条件:设计与测试用例先行

工作流不负责”想做什么”。开工前仓库里已经有:

  • DESIGN.md:完整设计(窗口配置、事件链路、调度器语义、数据模型、里程碑切分 M1-M8)
  • TEST-CASES.md:验收用例,编号到具体步骤 + 预期(TC-EV-01 ~ TC-TD-09 共上百条)

这两个文档在工作流里的身份是验收依据,等同需求:tester 按它逐条勾验,coder 禁改,口径冲突走 CASE_BUG 裁定。这套工作流消费的是”想清楚的输入”,它自己不生产需求——边界划在这里,评价才公平。

范围

M1 骨架 → M2 事件链路 → M3 token 统计 → M4 提醒/烟花 → M5 atlas 加载器 → M6 拖拽/穿透/热键 → M7 Todo 插件 → M8 收尾(i18n/CI/安全回溯)。2026-08-14 深夜到 08-19 下午,约 5 天。

2. 全景数据

里程碑 日期 轮次 改动规模 测试基线(npm / cargo) PR
M1 骨架 08-14/15 4 轮(触发收敛保护,用户批准超轮) 55 文件 +9154 12 / 3 PR #1
M2 事件链路 08-15/16 1 轮双通过 28 文件 +2882 53 / 34 PR #2
M3 token 统计 08-16 1 轮双通过 28 文件 +2904 85 / 58+1 PR #3
M4 提醒/烟花 08-16 1 轮(内含 3 次用户补充需求提交) 23 文件 +3457 123 / 79+1 PR #4
M5 atlas 08-16 1 轮(内含 6 次 R1 提交,5 次用户美术反馈) 25 文件 +2509 154 / 99+1 PR #5
M6 交互精致化 08-17 3 轮(用户报 bug + committer NEEDS_CHANGES) 25 文件 +1461 184 / 115+1 PR #6
M7 Todo 插件 08-17/19 1 轮双通过 25 文件 +2924 204 / 140+1 PR #7
M8 收尾 08-19 2 轮(committer NEEDS_CHANGES) 35 文件 +2105 221 / 159+1 PR #8

8 个 PR 全部合入 develop,约 30 个提交(22 个 coder 实现/修复提交 + 8 个回 spec 文档提交)。

成本(opencode.db 里的真实账单,24 个子会话 = 8 任务 × 3 角色):

指标 数值
input tokens ~7.15M
output tokens ~1.23M
cache read ~541M
API 计费 约 $2.5(coder 走订阅套餐基本 $0;tester $0.95、committer $0.58、M1/M2 早期 coder 计费 $0.97)

不含编排者主会话和我的交互开销,但量级不会翻一个数量级。一个完整桌面应用 V1 的多 agent 开发,模型直接成本个位数美元——主要成本其实是我的人工注意力(见 §7.3)。

一次通过率:8 个任务里 5 个一轮双通过;3 个多轮(M1 的 4 轮里有两轮 tester FAIL + 一次 blocked;M6/M8 各一次 committer NEEDS_CHANGES)。tester 全程只误判过一次(M1 假阴性,下详),committer 的两次 NEEDS_CHANGES 事后都被证明是对的。

3. 案例一(M1):当自动化验证说了谎

最有价值的一次翻车。M1 的验收标准里有一条:pet 窗口完全透明(只显示猫,无窗口框感)。

R1:coder 交付,tester 全项 PASS(含”透明”——但只核对了配置字段),committer APPROVED(附 6 条 P2 和 2 条需求边界问题)。双通过后我亲自启动 App 一看:猫被一个白色窗口框着。自动化全绿,用户肉眼可见地不达标。

打回 R2:coder 加上 macOSPrivateApi: true(macOS 透明窗口必需),像素级量化证明透明生效。tester 回归 FAIL——透明确实修好了,但窗口内容完全不渲染了(显示/隐藏截屏 diff=0,亮像素=0,accessibility 树无内容)。证据链非常扎实,coder 打回。

R3:coder 定位到 wry 源码级根因,补 backgroundColor: "#00000000",并给出量化证据:内容 diff 59.7%、亮像素 59716。tester 复测 FAIL——全部独立测量依然是 0 内容,报告里写了一句关键的话:”coder 报告与 tester 实测完全矛盾,两个方向只能一个对,建议用户人工目验“。

收敛保护触发status=blocked(超 3 轮 + 内容渲染问题往返 2 次)。我从应用程序实际启动 pulse-pet:猫在,透明也对。R3 修复在真实环境是好的,tester 的自动化测量是假阴性。

R4(我批准突破 maxRounds):coder 书面分析假阴性根因——① 无头会话里 WebContent 渲染进程被系统挂起(CPU=0,rAF 不跑,DOM 空);② screencapture -l 捕获的是窗口 backing store,而 WKWebView 内容经独立进程用 IOSurface 合成、不写入 backing store,透明窗口恒得全透明图。产出”可信的运行时视觉验证 5 步法”写进 README:真实 GUI 会话启动 → screencapture -x 全屏合成截屏(不用 -l)→ 前置自检(WebContent CPU>0、AXWebArea 有内容)→ 量化指标 → 人工目验兜底。tester 按新方法复验:自检通过(CPU 4.7%、AXLayoutCount=4),量化结果与 coder 数字吻合,PASS。committer 增量审查 APPROVED,交付。

这个案例暴露并最终修复了三件事:

  1. “验收标准含视觉”时,配置核对不算验证——TC-APP-01 的口径当场升级为”视觉透明需量化实证”;
  2. 无头 GUI 测量有系统性盲区,验证方法论本身要作为资产沉淀(之后的 M3-M8 再没在这个坑里跌过);
  3. 矛盾上报机制工作正常:tester 没有屈从 coder 的数字,coder 没有迎合 tester 的 FAIL,双方各自给出可复核的证据,仲裁权干净地交回用户。收敛保护也没有把流程卡死成官僚主义——用户批准就能突破。

4. 案例二(M5):美术迭代的工程化

M5 是 atlas 加载器(webp 解码、9 状态映射、宠物选择)。功能本体一轮写完,真正的戏在美术上——我一晚上提了 5 次反馈:

  1. 21:09 补充需求:小猫定名 blinking-kitty,新增一只内置小狗 wagging-doggy(像素风、摇尾巴);
  2. 21:31 造型反馈:不写实、参考小猫画风、正面不要侧面、尾巴别摇得刻意 → coder 重画(尾摆帧差异从 1216px 收敛到 224px,全部有像素断言);
  3. 21:54 反馈:小猫 idle 没在眨眼 + waving 手臂画到猫头上了;
  4. 22:08 反馈:上一轮的眨眼改法(4 格宽横条)不喜欢,就要单只眼睛变一条缝
  5. 22:20 反馈:还是看不到——要求跟 App 图标一模一样

注意第 3、5 条的处理方式,这是工作流的价值所在:我只会说”没在眨眼”,supervised-coding(编排者)自己动手解码 spritesheet 做像素级定位——第 3 次定位到 waving 手臂坐标 x23-24/y12-17 落在猫头正中且与身体同色(实锤 bug);第 5 次定位到真正的根因:资产生成脚本里有一行把右眼强制覆盖为睁眼(”atlas 基线两眼睁开”),而 App 图标的右眼本来是一条缝——所以无论怎么改眨眼帧,idle 永远是双眼对称的,用户永远看不到图标效果。修复方向由此精确:idle 行恢复原始单眼缝造型,与 App 图标逐像素 0 mismatch。

流程机制上,这 5 次反馈全部走”补充轮“:落笔 DESIGN/TEST-CASES(编排者改文档,coder 禁改)→ coder 在上一提交基础上改 → 重新验证——不新增 round。正式轮次计数保持 R1,用户主观反馈不消耗收敛预算。6 个提交(5fcf8fc → d9bc811)每个都带完整的测试基线 + 解码断言(”408 非透明格 0 mismatch”这种级别)。

复盘:主观验收(美术观感)也能进工程化流程,前提是把”用户说不好看”翻译成”哪个像素不对”——这个翻译活,编排者 + 像素分析工具干得比人肉猜快得多。

5. 案例三(M6):用户报 bug → 意见闭环的真实运转

M6 收尾后我日常使用中发现:拖拽宠物之后,明明没有单击,宠物的状态却轮换了一次(单击轮换状态是 M1 的既有交互)。原话反馈给编排者。

R2:coder 排查出根因——启动 OS 拖拽时清空了”本轮按下”的记录,而 macOS 拖拽结束后 WKWebView 不发 pointercancel、浏览器补发完整的 pointerup+click,点击处理器无从区分拖拽尾巴和真实单击。修复:DragClickGuard 状态机(一次性消费的点击抑制)。用”锁死显示状态 → 合成拖拽 → 截帧断言毛色不变”复现修复前后差异,与我的投诉逐字吻合。

tester 复验 PASS 后,committer 审查给出 NEEDS_CHANGES:一条 P1——它发现同族的另一个时序竞态:右键菜单的 document 级 pointerdown 监听用了 capture 阶段,先于 React 把菜单关掉,导致”菜单开着时点画布”这条防线的判断失效,每次关菜单都会误轮换一次(有提醒气泡时还会误确认)。意见原文写得很硬:”与用户 R2 投诉的附加单击效果同族,故不放过。”——修复成本”一行级”,但这是正确性缺陷。

R3:coder 修复(去 capture + pointerdown 快照菜单开态 + shouldRotateOnClick 纯函数统一判定 + 3 条防回归单测)。有个值得记录的细节:coder 在自测报告里如实报告了自己的验证是弱证据——它的合成输入环境出现 App 激活竞态,”菜单开 + 点击送达”的完整时序无法稳定复现,只拿到弱证据,完整的 GUI 手测移交 tester 真实交互环境。tester 在真实环境把三项手测全部跑绿。

这个案例展示了意见闭环的设计意图如何落地:用户 bug(拖拽误轮换)和审查发现(菜单误轮换)是同族缺陷的两个实例,普通”报一个修一个”的流程大概率漏掉第二个——检查点把用户反馈逐字留档,committer 对照检查点审查时把同族问题挖了出来。以及:弱证据如实上报,验证责任移交到有真实环境的角色,流程没有强迫 coder 谎报”已验证”。

6. 案例四(M8):committer 拦下文档级 P1

M8 收尾轮,committer 给出第二次 NEEDS_CHANGES,P1 是个纯文档问题:README 里的社区素材行序表写错了 3 行(idle/editing/testing/success… 的行序与实际渲染语义不符,jumping 预留行的位置也错了)。

为什么文档错误算 P1(阻断交付)?因为这份 README 是给社区用户自制宠物素材的规格说明——照错误的行序表做素材,9 个动作状态会整体错乱。用户不读代码,README 就是他们的 API。

R2 修复后复审,committer 做了”R1 意见闭环对照 9/9 全 ✅”的逐项核对才 APPROVED。这个案例补上了质量门禁的最后一块认知:语义审查的对象不止代码,一切用户会照着执行的东西都是审查对象

7. 效果评估

7.1 质量拦截

committer 每轮输出的 finding 统计(全部有留痕,可在检查点复核):

任务 P1 P2 P3 需求边界问题(回 spec)
M1 0 8 0 2
M2 0 2 6 2(含”success 无事件驱动”的实证发现)
M3 0 0 5 2
M4 0 7 0 6
M5 0 7 0 0
M6 1 3 1 1
M7 0 4 7 1
M8 1 3 5 1

两个 P1(M6 菜单竞态、M8 行序表)都是真缺陷且都被修复验证。P2 里的典型货色:SQLite 没开 PRAGMA foreign_keys 导致级联删除静默失效(M4/M7 的定时炸弹)、token 文件先写后 chmod 的权限短窗口、PNG 解压炸弹无防护、迁移非事务化……这些都不是”跑不过测试”的问题,是只有读代码的人才能发现的问题——这正是 committer 跨模型族独立审查的存在意义。

“需求边界问题”通路(D27)的价值单独说:它拦下的是验收口径与现实的漂移。比如 M2 实测发现 opencode 根本没有 success 事件(设计假设有)、DESIGN 里的 SQL 列名与真实 schema 不符、ignoreCursorEvents 不是 Tauri 2 的配置字段——这些都不是代码 bug,是文档错了。流程把它们回 spec 由编排者落笔勘误(全程 20+ 处),文档始终是活的真相源而不是开工即过期的标本。

7.2 遗留事项流转

P2/P3 不阻断交付,但会进检查点的”遗留事项”小节,下一个任务开工时被强制扫描(D29)。真实流转链:

  • M1 的 8 条 P2 → M2 开工并入,逐条清偿(含 FK pragma、⌘W 防护、dpr 竞态…)
  • M2 新增 P2-9/P2-10 + 测试缺口 4 条 → M3 清偿
  • M4 的 7 条 P2 → 分流:todo 相关去 M7、多屏实机去 M8、其余顺带
  • M7 开工我一句话”能并入都并入” → M4 P2 ②③④ + M5 P2 ⑤⑥ + M6 P2 ② 六项全清
  • M8 收尾清偿 A1-A9 九项,剩余的 B 类(需要多屏/Windows 实机硬件)显式记录去向

没有一条 P2 蒸发。对比我过往”TODO 写在便签上”的开发方式,这是可核查的债务账本。

7.3 人工介入点的真实成本

诚实盘点我要做的事:每任务 1 次需求确认(含遗留清单)、每次 subagent 调用前的”调用预告”确认(D28,每任务约 5-15 次)、美术/体验类主观反馈(M5 一晚 5 次)、M1 的一次关键仲裁(真实 GUI 目验)、每任务交付确认 + 合入确认。8 个任务合计我的显式交互约 100 次出头的量级,多数是”看一眼预告→回车”。

这个成本是 D28 主动选择的:编排全自动跑通后我反而心里没底,收了半步。它买到的东西很具体——每次角色介入前我能改输入、改范围、跳过;M1 假阴性仲裁那种”只有用户能做的判断”有一个自然的落点;以及全程对模型行为的一手观察(这些观察直接变成了第一篇里的 D28-D37)。

7.4 测试资产的复利

测试基线 12 → 221(npm)、3 → 159+1(cargo),且每轮全量回归(”全部实际复跑,非引用”是 tester 报告的固定声明)。tester 不只跑新用例:M4 验证时顺带回归了 M1 的窗口配置、M2 的 23 条 TC-EV、M3 的 13 条 TC-TK。六个里程碑的返工没有一次以”改好了但把旧的改坏”收场——双 SHA + 全量回归把这个通道堵死了。

8. 实践反哺设计

第一篇提到九轮修订里 D28 之后的每条都来自实跑。对应关系:

实跑事件 设计修订
M1 编排全自动跑通,用户要求收紧观察粒度 D28 调用预告
M1 “P2 移交 M2”靠人记,险些丢失 D29 遗留事项强制清偿
M1 交付时的分支混乱 D30 固定 develop_opencode + 提交前同步
项目跑完复盘发现全员在默认推理档跑 D31 →(切 agent 报错)→ D36 reasoningEffort
M1-M3 tester 一轮触发十数次权限确认 D32 扩容 →(M4 依然不够)→ D37 权限反转
检查点漏写 task_id / filesChanged D33 三条铁律
supervisorSessionId 三任务全 null D34 删死字段
过程中问 coder”为什么这样做”要编排者传话 D35 直连通道(方案定稿)
检查点追加内容时锚点旧文本被覆盖 追加写入铁律(prompt 级热修,M7 期间)

一个值得强调的模式:修订全部是”实跑证据 → 设计文档 → prompt/配置”的闭环,设计文档 v10 与最终配置严格同步。没有一条修订是”感觉应该”——这也是为什么这份设计文档本身值得当博客写。

9. 教训清单

给想复刻这套流程的人排雷:

  1. 无头 GUI 验证会系统性说谎。渲染进程挂起、backing store 不含合成内容、合成输入被激活语义吃掉——M1/M6 都付过学费。GUI 项目的 tester 需要”可信验证方法”作为一等资产沉淀(真实会话自检 + 合成截屏 + 量化指标 + 人工兜底),并在 README 里固化。
  2. 合成输入 ≠ 真实输入。CGEvent 合成点击在 App 非 active 时不可靠(macOS 激活语义消费首个 plain click);coder 环境测不稳的场景,要设计”移交 tester 真实环境复验”的通道,而不是逼着某个角色谎报绿。
  3. 模型摘要会计数虚标。”token-stats 12 测”实际 9 测,出现过两三次。committer 的”静态计数逐文件复核”流程就是为这个长出来的——对模型的产出做机械交叉核对,永远值得
  4. 检查点协议要防 edit 的替换语义。追加内容时锚点文本必须完整出现在 newString 里,否则等于静默删除既有记录。LLM 用编辑工具改状态文件,这类事故是概率问题,只能用铁律压频率。
  5. 单人仓库的 gh 不能 approve 自己的 PR。终态评审留痕改用 COMMENT 型 review,正文承载全部审查结论——留痕目的达成,形式无所谓。
  6. 验收口径的漂移比代码 bug 更隐蔽。”需求边界问题”单独通路(不经 coder、回用户)是全套机制里性价比最高的一条,强烈建议保留。
  7. 便宜模型当编排者完全够用。supervised-coding 用 flash 档模型跑了 8 个任务零调度事故,它做的是确定性工作,贵模型花在判断层(committer 的 pro 档拦截了全部两个 P1)。

10. 结论

这套工作流值得,但有前提。

适合的场景:单人/小团队;需求能先写成结构化验收用例(这条是硬门槛——工作流放大的是”想清楚的输入”);迭代周期以天计的项目;愿意付每任务十次级确认交互的用户。

真实的收益构成:committer 独立审查拦下的正确性缺陷(2 个 P1 + 一串 P2 定时炸弹)、跨 8 个里程碑零蒸发的债务账本、全量回归垫底的返工安全感、$2.5 的模型成本,以及一份始终与代码一致的设计文档。

真实的成本构成:我的交互注意力(比纯手写代码省,但远不是”完全托管”);流程本身的搭建与九轮修订(好在这是一次性投入,配置随仓库可复用);以及 GUI 验证方法论的学费。

下一步:把四个 agent 的 know-how 拆成 skill、用 SDK 把跑定的循环固化成脚本进 CI、迁移到全局配置让其他仓库复用。到时的经验,也许会有第三篇。


基于OpenCode的多Agent代码开发工作流:实践与效果
https://yq3.github.io/codelife/2026082001/
作者
yq3
发布于
2026年8月20日
许可协议