开发 AI DevFlow Studio 的三个多月里,我在 Codex 中先后用 GPT‑5.5、GPT‑5.6 和 GPT‑6 推进这个项目。回看记录,最常出现的一幕并不是某个功能写不出来,而是 Agent 汇报“已经从头到尾跑通”,我多问一句,事情就露出了另一面。
这样的追问至少发生过三次。每次暴露出来的都不是模型偷懒,而是“完成”的范围被悄悄缩小了:换成了 fake 引擎,跳过了首次配置,或者复用了一个早已配好的项目。这篇文章按时间复盘这几次追问,以及最后一轮从空白仓库出发、一直走到 Draft PR 的验证。
文中会反复出现三层角色,先说清楚:
- 我:确定目标和验收范围,处理登录与授权;
- 外层 Agent:Codex 中的 GPT‑5.5/5.6/6,负责开发 DevFlow、操作它的界面、提交 Issue 和修复问题;
- DevFlow 内部的 Agent:产品自己调用的 DeepSeek,以及通过 OpenCode 或 Native 执行器写代码的模型。
GPT‑5.5:每一块都完成了,拼起来却走不通
DevFlow 想做的是一个本地优先的研发工作台:把需求澄清、方案设计、编码、测试、PR 和验收串成一条带 Gate 的流程,让开发者看清 Coding Agent 做了什么、产出了什么、哪里需要人来判断。
最早的版本走的是“先有界面、再接真实能力”的路线。v0.1 是基于 fixture 的团队工作台,v0.2 接入真实的 Electron、本地测试命令和 SQLite。初始路线图(在新标签页打开来源) 之后每个里程碑的范围都写得很清楚:v0.6 做好了 fake coding harness、权限面板和变更产物,把“在同一条 Electron IPC 上接入真实 opencode-http”留给后续加固;v1.3 叫 Delivery Flow Completion,实际交付的是 PR 草稿和验收证据包,明确不创建真实的 GitHub PR。v0.6 计划(在新标签页打开来源) · v1.3 范围(在新标签页打开来源)
单看每份文档都没有问题,问题出在它们叠加之后:功能清单越来越长,一个真实用户却很难把流程完整走一遍。
6 月下旬,我刚创建的一个 Run,点了“同步团队”就从界面上消失了。原因是界面用远端列表整个替换了本地列表,而那条 Run 其实还好好地保存在本地 SQLite 里。6 月 26 日的走查更矛盾:Run 显示已完成,Build 节点还是 ready,PR 仍在 waiting,摘要里的“当前卡点”却写着测试证据。那次走查用的还是 fake、零成本的 Coding 和 Review;到 7 月 25 日的走查,同步覆盖本地状态的问题仍未修复。6 月 26 日走查(在新标签页打开来源) · 7 月 25 日走查(在新标签页打开来源)
几天后,我换了一个新项目,发现界面里仍混着另一个示例项目的测试命令和记录。我给 Agent 定了一条规则:数据必须来自当前的真实项目;没有数据就显示中性的空状态;绝不能展示别的项目的数据或没有意义的假数据。按所选项目限定数据(在新标签页打开来源)
再往后我意识到,demo 数据已经不只是观感问题,它挡住了真实功能的开发。于是让 Agent 先盘点所有桩代码,再给出移除方案。7 月初的提交把 fake 和 demo 从默认路径里拿掉,改成显式开关:
// apps/desktop/electron/coding-engine.ts(3a995ff)
const selection = resolveDevFlowCodingEngineSelection(env)
if (!selection.engine) {
return createUnconfiguredCodingEngineAdapter()
}
if (selection.engine === 'fake') {
return createFakeCodingEngineAdapter()
}
Coding Engine 的默认值从 fake 变成“未配置”,fake 必须设置 DEVFLOW_ENABLE_FAKE_RUNTIME=true 才能启用;API 没有数据库连接时直接报错,除非显式打开 demo 数据;db:setup 也不再顺带写入种子数据。清理与运行时修复(在新标签页打开来源)
这一步方向是对的,也说明 GPT‑5.5 并不是一遇到困难就去造假数据。但它埋下了一个两个月后才暴露的缺口:默认引擎变成了“未配置”,界面上却还没有配置真实引擎的入口;而冒烟测试一直显式设置 DEVFLOW_CODING_ENGINE=fake。测试始终是绿的,因为它从来不走那条没有配置的路。
GPT‑5.6:应用做出来了,DevFlow 却停在半路
8 月下旬,我在一个新环境里打开 DevFlow,界面提示 Coding Engine 未配置。我先问这是什么意思,接着追问:我要的是完整跑一遍,为什么这个问题之前没有被发现?
回查的结论很直接。此前的自动化验收全部显式启用了 fake 引擎,从没有在不加额外环境变量的情况下点过一次 Coding Agent。单元测试分别锁定了“默认引擎为空”和“Coding Agent 按钮仍然显示”,却没有一条接近生产环境的端到端测试把两者连起来。界面还把引擎身份硬编码成 opencode-http,保存下来的 DeepSeek Provider 也没有真正配置给 OpenCode 或 Native 执行器。我让 Agent 按“现状、缺口、目标体验”把这些写进 Issue #37(在新标签页打开来源);在 v2.2 的冒烟脚本(在新标签页打开来源)里,还能看到当时显式启用 fake 的那两行配置。
几天后,我提了一个更严格的要求:付费、完全真实、任何环节都不许有 mock,在已有的一个 Mini Agent 项目上做一个真实需求,从澄清一直走到发布。
这次 DevFlow 内部由 DeepSeek 完成澄清、设计和审查,Native Coding 执行器再调用 DeepSeek 修改代码。过程并不顺利:修复轮返回了一个不在已批准 Change Set 中的文件路径,被运行时拒绝;有一版提案把只应由服务端生成的 requestId 和 generatedAt 塞进了模型输出契约;解析器遇到任何问题都只报同一句“提案无效”。前 11 次尝试失败或中断,第 12 次才成功。
对应的修复在 6c2ab51(在新标签页打开来源) 中:对 DeepSeek 端点关闭 thinking、强制 response_format: json_object;解析器容忍多余的顶层元数据、丢弃无实际改动的替换,并为每种失败给出单独的错误;修复轮的提示词直接列出允许修改的路径:
Every path must exactly equal one entry in this allowed path list: [...].
Do not use any other path.
Do not move server-, runtime-, or agent-owned fields into a provider/model result.
最终的 PR 改动 7 个文件(其中 2 个是测试),给接口加上了可追踪的 requestId 和 generatedAt,应用也发布了。可 DevFlow 自己停在了 PR 节点:GitHub App 的安装只覆盖旧的沙箱仓库,新仓库的绑定被 GitHub 正确拒绝。最后是外层 Agent 用已登录的 gh 命令行创建 PR、合并并发布 Release。
于是同一次运行给出了两个答案:
- 应用做出来了吗? 做出来了,真实模型、真实代码、真实测试和发布。
- DevFlow 的产品流程走完了吗? 没有。交付这一段,是产品之外的命令行替它完成的。
把这两个问题分开问,是这一阶段对我最有用的收获。
GPT‑6:什么才算“从空白开始”
9 月 10 日至 11 日,GPT‑6 在 DevFlow 上跑了一个很小的需求:修改首页的一句文案。过程中它发现并修复了几个问题(其中关于测试诊断回传的一个,写在另一篇笔记里),最后由产品创建 Draft PR,报告写的是“完整端到端验证”。
我注意到报告里有一个词:“复用”。项目、仓库绑定、Desktop 配对、Mini Agent 的已有代码,全都是之前配好的。当晚我直接问:我们有没有真正从一个完全空白的项目、一个全新的需求开始跑过?回答是没有,此前的说法夸大了。
于是我重新定义了验收终点:云端和本地都从零开始,不从流程中间的某一步切入,一直走到最终结果。我给了足够的权限,只在它自己做不到的地方再叫我。外层 Agent 据此列出具体步骤:新建空仓库,在 Web 上建项目并提交需求,用全新的 Desktop 档案首次配对,再依次走完澄清、设计、编码、测试、交付和验收。
需求故意定得很简单:
从空白仓库创建一个中文任务清单网页,支持新增、完成、删除任务,刷新后保留内容,并提供自动化测试和运行说明。
业务上一眼就能检查,产品却必须经过每一个真实环节。第一个问题几乎立刻出现:空目录继承了上层目录的 Git 仓库(#104(在新标签页打开来源))。整轮里我亲自做的事情只有几件:重新登录 GitHub CLI、给 GitHub App 授权新仓库、重新保存 DeepSeek Key(系统钥匙串拒绝了旧凭据)。其余操作都由 GPT‑6 通过界面完成。
编码阶段一共尝试了三次:
- 失败。 OpenCode 会话正在正常工作,却被过短的权限发现窗口判定为异常并终止(#111(在新标签页打开来源))。底层原始错误当时没有持久化,原因只能推断。
- 取消。 一个零依赖的应用,却一直在等待“无 lockfile 安装依赖”的审批,而待审批请求数为 0(#113(在新标签页打开来源));同时发现会话尚未空闲时 diff 就可能被提前截取(#114(在新标签页打开来源))。
- 完成。 从同一个空白基线重新开始,生成 11 个文件。验收前重新计算 Git diff 的 SHA,与 Diff Artifact 一致。
同一组 17 项测试分别在编码阶段、Workflow 的测试节点和最终交付的提交上运行,都通过;外层 Agent 另外用真实的 Chrome 检查了新增、完成、删除、刷新后保留和几个边界情况。这一轮总共暴露并关闭了 12 个 DevFlow Issue。空白项目完整验证记录(在新标签页打开来源)
测试全绿之后,卡在一条排序规则上
代码和测试完成后,交付仍然推进不下去。每次 Resume,Desktop 只得到 recovery_required 或 service_unavailable,远端的交付请求始终没有建立。
追下去,问题出在 API 和数据库对“有序”的理解不一致。API 用 localeCompare 给改动路径排序并计算摘要;数据库的 CHECK 约束则要求数组中每个路径都严格大于前一个,比较时用的是数据库自己的排序规则(collation):
-- 0011_github_delivery.sql(旧规则,节选)
OR previous_item.value #>> '{}' >= current_item.value #>> '{}'
这个新项目恰好同时有 README.md 和一批小写字母开头的文件。同一个数组,两套规则排出来的顺序不同:
localeCompare : .gitignore < index.html < package.json < README.md < src/app.js
按字符码点 : .gitignore < README.md < index.html < package.json < src/app.js
localeCompare 在第一层比较中忽略大小写;而从实际被拒的数组看,数据库这里的比较等同于按字符码点,大写 R 排在所有小写字母之前。于是 package.json >= README.md 成立,约束失败,INSERT 被拒绝。此前的交付验证只改动过单个 README,从没碰到过这种组合。Issue #117(在新标签页打开来源)
修复没有让数据库换一套排序去迁就 API,而是把职责分开:规范顺序和摘要只由 API 校验;数据库不再对一个已签名的数组施加自己的顺序要求,只检查类型、长度、数量、非法路径和重复项。新迁移 0028 用 count(DISTINCT ...) 判断重复,顺带能发现不相邻的重复项,而旧规则只能发现相邻的。已有的 0011 迁移没有被改写。修复提交(在新标签页打开来源) · 新迁移(在新标签页打开来源)
回归测试(在新标签页打开来源)在真实 Postgres 上运行 15 个用例,覆盖混合大小写、中文路径、['README.md', 'readme.md'] 这种只差大小写的合法组合,以及重复、空数组、../secret、反斜杠、超长路径等非法输入。它先在旧函数上失败,再在新迁移上通过。
修复合入后,流程恢复的是原来那条 Delivery Intent:提交没有被改写,也没有为了得到成功状态另造一条交付记录。随后产品推送提交、创建真实的 Draft PR;最终验收由 GPT‑6 在我的授权下审阅 diff 和浏览器证据后提交。Run 以 8 个节点全部成功结束。
我现在怎样验收“完成”
这几个月改变的,是我给 Agent 布置任务和验收的方式。
先写清楚终点,再允许拆分。 可以先做接口、再做持久化和界面,但验收终点要写成一句具体的话:从什么状态开始,到什么结果结束,中间不允许绕过哪些环节。“从完全空白的云端和本地项目开始,到产品创建的 Draft PR 和业务验收”,比“端到端跑通”有用得多。
要求报告写明替身和绕行。 fake 引擎、复用的配对、产品之外的 gh 命令,本身都不是错误,但它们改变了结论的范围。我现在会专门留意报告里的“复用”“显式启用”“手动完成”这类词。
让失败留在原来的任务里。 这一轮的三次编码尝试、12 个 Issue 和那次排序故障,都保留在同一个 Run 的记录中。修复之后接着走原来的流程,而不是新开一个 Run 换一个干净的结果。
也要说清楚这段经历证明不了什么。它不是三代模型在同一份代码、同一道题上的对照实验:代码、工具、授权和我的验收要求一直在同时变化。最后一轮也仍有人和工具参与:登录、授权和 Key 由我处理,建仓和最终合并由外层 Agent 通过命令行完成,各个 Gate 的批准也是它在我委托下提交的,并不是独立的人工评审。DevFlow 的服务运行在本机,真实的外部服务只有 DeepSeek 和 GitHub。按记录中的估算,这一轮 8 次 DeepSeek 直连调用约 0.027 美元;OpenCode 一侧的调用成本无法从适配器看到。
这些修复后来随 v2.3.0(在新标签页打开来源) 在 9 月 14 日发布。9 月 17 日,同一个任务清单应用上又验证了一个“清除已完成”的新需求,这次由 Native 执行器调用 DeepSeek,8 个节点同样全部完成,Draft PR 留待审阅。后续验证记录(在新标签页打开来源)
对我来说,最重要的变化是:当 Agent 说“完成”时,我会先问它是从哪里开始的。
本文依据 DevFlow 的公开提交、Issue、计划文档和验证记录整理,版本相关的链接固定到当时的提交;历史会话只用于回顾我当时的提问和决定,不公开原始对话、凭据、私有仓库或运行标识。文中测试数量属于各自的应用和阶段,同一组用例的多次复验不重复计数;本文写作时没有重新执行付费模型任务。