我把 AI 工作流写成了代码,然后亲手拆掉了它
7 月 25 日,我发了一篇文章,叫《我把自己的 AI 工作流变成了代码》。
一天后,我把它的核心前提推翻了。
这事发生得很快,甚至有点好笑。那篇文章刚写完的时候,我还觉得 BiliKit 迫使我形成的工程工作流总算稳定下来了:风险分级、质量门、独立审查、复杂度预算,再把这些经验整理成可以复用的 Codex Skills。
结果第二天,BiliKit 自己就给了我一个反例。
一个被越做越完整的滚动位置
当时正在做 M5.0,需求本身并不复杂:
从浏览页进入播放页,再返回时,尽量回到进去前看到的位置。
BiliKit 当时使用一套自定义路由。进入播放页时,原来的浏览 View 会离开视图树;返回后重新创建,所以浏览状态和滚动位置都需要手动恢复。
最开始只保存选中的视频 ID。后来发现这只能把对应卡片滚回屏幕,不能还原原来的 viewport。
于是又开始记录原始 offset。
接下来出现的问题都很合理:
- 内容高度变化后,旧 offset 可能已经不可达;
- 旧
ScrollView的回调可能污染新的 snapshot; - 异步请求返回时需要检查 request identity 和 generation;
- 用户主动滚动后,要解除程序恢复状态;
- 还要补确定性测试和 XCUI,确认返回后确实是进去时的样子。
每解决一个问题,这套实现就完整一点。
而且它真的能通过测试。
回头看,最麻烦的地方也正在这里。测试、审查和质量门都在证明这套恢复机制有没有写对,没有人继续问:
为什么浏览页一定要被销毁?
后来我停下来做了一个很小的 SwiftUI probe,把条件替换、自定义恢复和原生 NavigationStack 放在一起比较。
结果很直接。
使用 NavigationStack 时,根 View 没有被重新创建,原来的 state identity 也还在。之前花了大量时间解决的恢复问题,有相当一部分来自我们自己选择的导航结构。
于是 BiliKit 改成了原生 TabView(.sidebarAdaptable),每个 Tab 使用自己的 NavigationStack(path:)。自定义的 AppRoute 和 AppReturnSnapshot 被删掉,浏览工作集继续保留,请求取消和播放器生命周期也继续保留。滚动位置改由 SwiftUI 的语义 ID 管理。
真实滚动验证中,两次热门页往返分别是:
0.195066 → 0.1948670.389877 → 0.389447已经没有必要让 ViewModel 保存一套原始数值 offset,再负责 clamp、恢复 latch 和旧回调隔离。
所有流程都正常工作了
这次没有哪个 Agent 明显偷懒。
项目规范被读取了,风险被识别了,测试层级也选得没什么问题。实现过程中确实抓到了不少真实 bug,像旧回调污染、不可达 offset、生命周期清理和 XCUI 驱动问题。
麻烦在于,所有流程都接受了同一个前提:继续使用现有导航结构,然后把状态恢复做好。
一旦这个前提进入任务契约,后面的 Agent 就很擅长沿着它工作。
Reviewer 会检查边界,red reviewer 会找失败路径,测试会继续增加,文档会越来越完整。每一层都能提高当前方案的正确性,同时也让它看上去越来越像唯一应该继续做下去的方案。
我之前把 project-governance-bootstrap 形容成一个编译器:
仓库事实+ 架构文档+ 测试+ 安全边界→ 项目规则现在觉得这个说法有个很大的问题。
编译器只能处理已经输入的东西。
如果仓库里的架构、任务契约和现有实现共同漏掉了一个更简单的方向,它只会把原来的方向编译得更严格。输入中的路径依赖不会消失,反而会被整理成更正式的文档、更多检查项和更强的执行惯性。
连纠错都差点被我做成另一套流程
发现问题后,我的第一反应仍然是把这次经验总结成规则。
比如要求每个高风险任务先列替代方案,强制安排 challenger,建立 decision ledger,再增加一个专门检查问题定义的 reviewer。
写着写着又不对劲了。
为了防止流程堆得太多,我正在继续增加流程。为了避免 Agent 机械执行任务契约,我又准备写一份更长的任务契约。
这和刚刚推翻的东西其实没差多少。
所以这次我没有做 workflow v2。
BiliKit 的协作规则直接精简了。一个提交删除了 766 行配置和文档,5 个固定 Agent 定义也一起删掉。现在的 AGENTS.md 只保留项目事实、架构边界、安全约束、实际可用的验证命令和提交规范。
质量门还在。
SwiftPM、xcodebuild、生命周期测试、安全边界检查也都还在。它们确实能回答构建有没有坏、取消是否正确、资源有没有释放、真实 App 能不能运行。
固定风险颜色、任务契约格式、reviewer 链和复杂度预算不再是项目的默认仪式。
需要的时候照样可以用,没必要每次都先证明自己遵守了流程。
那两个 Skills 怎么办
apple-dev-loop 目前没有太大问题。
它负责选择和执行 Apple 平台的验证手段:什么时候跑 SwiftPM,什么时候需要 xcodebuild,什么时候应该查看 .xcresult、启动签名 App、跑 XCUI 或 Instruments。
这些工具能提供什么证据,边界相对明确。
project-governance-bootstrap 就不一样了。
它原本试图根据仓库生成治理方式,里面天然带着一种暗示:只要读取的信息足够完整,就能得到适合这个项目的工作流。
BiliKit 这次说明,仓库本身也可能在很认真地描述一条走偏的路线。
这个 Skill 我准备重新看一遍。可能会缩到只整理现有事实和验证入口,也可能直接归档。现在还没决定。
暂时没有新答案
按目前主要源码和测试目录统计,BiliKit 已经有接近 2.6 万行 Swift。
项目继续变大,靠一条 prompt 加一次 diff review 肯定还是不够。之前那些规则也不是完全没用,它们确实帮我抓到过不少问题。
但我不再觉得可以把这些经验整理成一套通用流程,然后让它自动替下一个项目选择正确路线。
至少这次不行。
7 月 25 日那篇文章我会留着。它写的是我当时真的相信的东西,删掉或者悄悄改写都没什么意思。
这篇则记录第二天发生的事。
后面怎么做,等项目再逼我一次再说。