终点不是转写稿 书架

第 13 章 / 共 16 章

第十三章 · 一个人和一群代理

一份可以核对的账

2026 年 6 月 18 日,仓库第一条提交是 chore: init voicedrop repo;到 9 月 1 日走到 738 条,如今 740 出头。中间 76 天。按天数一遍:最猛的是 6 月 23 日 70 条(去掉合并提交还有 55 条),其次 6 月 20 日 43 条、7 月 14 日 40 条,其间 43 次合并,以及十几次明确标着 revert 的回滚。产物也不止一个 iOS App:还有 Cloudflare 上的三套服务、一个排序旁车、东京 VPS 上的公众号中继、一套网页书架、一个给别的代理调用的接口服务。

这堆数字容易被读成「AI 让人写得快」。它的真实含义是另一件事——当写代码不再是瓶颈,瓶颈会往上游跑

并行的前提是隔离

提交历史里反复出现 Merge branch 'worktree-swift-jingling-pike',十几次。worktree(工作树)是 git 的一个功能:同一个仓库同时签出到好几个目录,各占一个分支,互不打扰。因为同时开着好几个 AI 会话,共用一个目录就会互相踩脚——一个刚写完文件,另一个的编译还在读旧版本。

并行不是「多请几个人」,是「给每个人一张自己的桌子」。桌子不够,人越多越慢。

代码便宜了,规格反而贵了

740 条提交里有一套反复出现的节奏:docs(spec) 写清楚要什么、边界在哪 → docs(plan) 拆成一串带验收条件的任务 → 一串 feat,每个任务先写一个必然失败的测试,再写代码让它变绿(这就是 TDD)→ 评审打出的 fixdocs(state) 收口。仓库里躺着 27 份 spec、30 份计划与任务报告、41 条 docs(state);算力计费、设备配对登录、持久编辑队列、接受分享、语音指令、追问、提示词注册表、邀请奖励、分享码,九个大功能全走完这套。

代码写得越快,不是越该「直接开干、错了再改」吗?恰恰相反:想清楚的成本没变,把它变成代码的成本掉到了零头,规格于是成了整条流水线上最贵的一段

以前打字慢,含混的想法在敲键盘的过程里会被自己发现。现在打字不要钱了,含混会被原封不动地铸成产品——而且长得很像对的:变量名整齐、注释完整、测试全绿。

洞在计划里,不在代码里

最值钱的一条发现出现在 7 月 13 日。计划文档里照惯例给了一段「参考实现」,约束本身没错:标签不超过 40 字,整棵树不超过 200 个节点。然后对抗性评审上场——它的任务不是确认代码能跑,是想办法把它弄坏。三轮对抗评审连着打穿六处,挑三处说:

关键在于这几刀砍中的位置。三条修复提交的正文写着同一句自白:

这些洞是计划本身写错的(实现者是逐字照抄的)。

实现没走样。你的实现者会逐字照抄你的计划,包括你计划里的错。人照着一份有毛病的设计写代码,往往写着写着「觉得哪里怪怪的」就顺手改了;代理不会。

由此写死进计划两条规矩。校验器必须是全函数——对任何输入都得给出确定答复,「过」或「不过」,不能自己先炸;它一崩,400 就变成 500,而 500 意味着这个洞可以被利用。输出是要落盘的,必须永远满足自己的校验器——恢复默认值居然能产出一份连自己都会拒收的文档。

还有一个动作值得单说:每次修完代码都回头改计划文档,理由只有五个字——「免得再被照抄一遍」。评审的火力要往上游移:审代码只救这一次,审计划才救下一次。

测试从「保险」变成「契约」

CLAUDE.md 里有条铁律,6 月 24 日加的:任何改动前后各跑一遍单测。iOS 第一个测试 target 是 7 月才建的,从 0 涨到 125,如今 214。一批「决策逻辑」被刻意抠成纯函数再钉死:何时推荐升档订阅、何时弹评分请求、要不要问署名、走哪条线路、社区搜索怎么过滤。测的是行为契约,不是实现细节——实现细节明天就会被某个代理重写。

服务端的规矩叫「向下兼容即契约」:永远伺候老数据和老客户端,老契约钉绿才推新版本。最有仪式感的一条是删掉一段遗留代码之前,先删它的测试。顺序是关键:先删测试,你必须写下「我知道我在放弃这种老数据」;反过来就成了「被测试挡了路,把挡路的搬开」。同样两行删除,一个是决策,一个是事故。

「有响应」不等于「能用」

语音改稿换了听写后端,两次上线都通过了冒烟测试(最粗的一层验证:点着火看冒不冒烟),两次都完全不工作,用户反馈是「说话和没说一样」。根因是中转服务把二进制音频帧隐式转成了字符串,每帧都变成 「[object Blob]」 这十三个字符。而冒烟测试之所以说「通过」,是因为它只发配置帧、只检查「有没有字节回来」——回来的正是对这十三个垃圾字符的回应。合格的冒烟测试必须流真实数据、断言解码出来的结果。

当外部世界给你限流

苹果给每个 App 大约 20 次/24 小时的上传额度,高强度迭代日里「每次 push 就发一版」直接烧光,CI 成片返回 409。修法不是加重试,而是把默认值翻过来:默认只跑不签名的编译校验,提交信息带 [tf] 才真打包上传——整个历史里带这标记的只有 51 条。但这条闸只活了 24 天。8 月 2 日它被撤销了——恢复每次推送都自动发 TestFlight,理由写在提交里:苹果那约 20 包/24 小时的限额当然还在,撞上 409 就是额度到了,等窗口刷新就好,但不要为此把默认值改成「默认不发布」。这个反转比原来那条方法论更值得记:限流是外部世界的成本,而「每次推送都能出包」是你自己的节奏——宁可偶尔吃一个 409,也不要让节奏迁就限额。唯一没变的那句是:CI 绿不等于出包。另一条纪律踩了两次才写:某个版本号一过审,那趟「列车」就永久关闭,同号构建再也传不上去;第二次当天就加了守卫——构建时查在售版本,工程版本没高过它就就地加一。

一个新的失效模式:上下文污染

写书引擎接某家模型服务时,请求被风控判成「高风险」直接拒绝。二分了整整一天才定位到元凶:编程助手会自动往上下文里注入项目记忆笔记,而那个目录的笔记里恰好带着某本书的内容——风控读到的是那段内容,不是你写的提示词。它跟文件名、目录名统统无关:搜代码搜不到,看日志看不到。修法是让这条腿跑在一个独立的零记忆工作目录里,每次开跑前清空。

代理的输入不只是你传的参数,还包括系统提示词、项目记忆、工具返回值、上一轮对话。「我传了什么」和「它收到了什么」第一次成了两件事。

人在哪儿

这不是「AI 全自动」的故事。「用户当场指出」「用户拍板」「用户要求还原」是提交历史里的高频词,而且每次都落在关键位置:

共同点:全都不是「代码写错了」,而是「这个方向本身不对」。代理能把任何方向执行得又快又漂亮,包括错的那个。人的角色于是从「写代码的」变成「定义什么是对的」。

给未来的代理留账

长期文档里,一条从来没真跑通过的路被诚实写成「待验证」,后来第一次拿两台真机跑当场挂了两次——所有「待验证」本质上都是「已知会坏,只是还没坏给你看」。一次「合并 → 回滚 → 一模一样重做」的曲折被专门写进文档,理由是「免得 git log 误导后人」。有条提交信息里专设一节叫 Honest scope (not faked):照着设计稿做,但不照着设计稿撒谎。一次整体回滚里还写明「一并撤掉的还有一个独立的真 bug 修复」。留账是这套工作方式的必要成本,不是美德——收益在三周后,下一个会话不管是人还是代理,读到的是「为什么」。

真正变了的三件事

76 天七百多次提交,速度是这故事里最不重要的部分。真正变了的是三件:规格成了最贵的东西——「你想清楚了没有」成了唯一的瓶颈;评审的火力要往上游打——实现者会逐字照抄你的计划,包括错的那部分;人必须留在「什么是对的」这个位置上——方向、取舍、什么时候该停、什么该整体删掉,没有一条能从提交数里读出来,也没有一条能外包。

终点不是转写稿 · 王建硕
费曼式写法 · 由多个 AI 代理撰写与互相审校