AI 迁了 83 万行代码,人只说了 2600 句话

cover

Hello,我是飞飞。

昨天 GitHub 发了篇复盘,数字很扎眼:一位工程师带着一队智能体,用 14.5 周把 Copilot 的运行时从 TypeScript 完全重写成 Rust,832,378 行生产代码,128 个 PR,边迁边发版。但整篇看下来,最扎我的是另一组数:全程 31,247 条发给智能体的消息里,人亲手打的只有 2,600 条上下,差不多十二分之一。

这个比例值得停一秒。八十三万行代码,人只出了十二分之一的力?账没这么算的。官方把那 2,600 条消息按意图分了类:31% 在盯审查、测试和 CI,17.4% 在挑战智能体的技术决策,15% 在推它别把半成品当完工。三样加起来 63%。写代码的那只手已经交出去了,人攥着的,是挑刺、抬杠、催进度的嘴。

这本账摊了些什么

先交代工程本体。迁的是 Copilot agent runtime,就是 CLI、桌面 App、SDK、VS Code 这全家桶共用的那台引擎。原先 TS 走 Node,SDK 每接一个客户端都得陪跑一个 V8,一百多 MB 内存起步,两个进程盯着,Node 一崩会话陪葬。换成 Rust,这些全部翻篇。

结果这边:14.5 周,83 万行生产 Rust,128 个 PR 边迁边发版,main 分支天天可发布。收益挑两条说人话的:一轮会话从 5.25 秒压到 292 毫秒,快了 18 倍;十个客户端的内存省了九成,原先每个客户端陪跑的那台 V8,整个下岗了。

怎么迁的也值得单说。他们没搞憋大招式的整体切换,也没养两套版本随时热切换,用的是笨办法:一个组件一个组件来,每个 PR 用 Rust 实现换掉对应那段 TS,同时把旧代码删干净,main 分支始终保持能发布。我六月那场迁移的结论就是「一次只动一个变量」,八十三万行的工程最后选的也是这条路。有意思的是拒绝热切换的理由:最需要新旧对照的组件,恰恰是耦合最重、最没法影子运行的那批。想抄近道的冲动,在哪儿都一样。

钱也摊了。全程约 1363 亿 token,账单约 12 万美元,再搭上约三周人力。官方原话,这活搁 agents 出现之前,得一个团队干上一两年。当然这笔账有前提:一是 FFI(跨语言调用那层胶水)、打包、评审那些活是团队里其他人分掉的,作者如实列了名单;二是 96.22% 的缓存命中率撑着,说人话就是,系统提示词、工具定义、越滚越长的对话这些贵前缀只算一次钱,后面每轮按一折续读,护住前缀,就是在护钱。还有个细节给所有做估算的人提了醒:开工前估 13 万行,实际从手上流过 43 万行,因为隔壁团队一边迁一边还在往里塞新代码。迁移的规模本身是流动的。

人的活挪到哪了

63% 那组数,我有体感。六月我拿 AI 迁 7600 行 Java,有个叫 isLiability 的字段在改写时被悄悄并进了别的 class。编译照过,测试全绿,我肉眼翻 diff 根本看不出来,是流水线里专门唱反调的校验 agent 把它吼回来的。那天之后我的活就变了:依赖清单逐个对,路由清单逐字节比,删旧代码前先拿旧版跑 19 条黄金输出钉成断言。它写得再快,我的卡一个不撤。当时我写过一句「检查这活没消失,是被做成了功能」。GitHub 这次等于给这句话配了份统计报表。

还有一组数跟大众印象正好拧着。你想象中的 AI 写代码,大概是噼里啪啦往外喷。日志里的实情是,读文件、搜代码、跑诊断的量,是改代码的十倍。官方的意思差不多是:AI 喷代码的流行画像正好反了,这个规模下的工作更像反复勘察。看一眼现状,立个假设,改一小刀,再来一轮。

他们还有个细节我特别欣赏。智能体会自动处理 CI 失败、回评评论、解冲突,一条龙干到 merge 前一步。有一次一个迁移把 SDK 的一个函数弄丢了,兼容性检查红了,agent 的反应是给 PR 打上「允许破坏兼容」的豁免标签,让检查闭嘴。人在 merge 前抓了出来,就问了一句:什么破坏?为什么就 ok 了?21 秒后,函数带着 Rust 实现回来了。

配套的规矩跟着立下:agent 不许动端到端测试。这条我认为是整篇复盘里最值钱的一句。测试是「对」的定义,让改代码的 agent 顺手改测试,等于让它自己批自己的卷子。我六月钉黄金输出,防的就是这个。

能编译这事骗不了人

你可能也刷到过那个说法:Rust 特别适合 AI 写,编译器严,能兜住模型的错。官方拿数据验了,验完你大概会跟我一样,把这句话倒过来信。8,678 条编译错误里,命名解析、缺方法缺字段、类型对不上、接口没实现,这四样占 84%,全是普通静态语言都能抓的接线错。传说中最难的 borrow checker,那套管内存借用的严规矩,只占 1.7%。

更有意思的是后半句。所有已知回归,没有一个不是编译通过的。「能编译就是对的」在 Rust 圈本来就是个玩笑,这回有了八十三万行的实证。我六月写过一句「光靠能编译、老测试也过,这种坑根本兜不住」,两边隔着三个月和一个数量级,撞了同一句话。

官方还补了半句我没想过的:编译器抓不了你没写的东西。有个守卫在 rebase 时被悄悄删掉,连同它的测试一起,编译器毫无意见。所以测试本身也得设门,人守着,agent 不许碰。

经验清单里还有一条我打算贴在工位上:先翻译,后重构。迁移动语言的时候顺手改设计,两个变量一起动,出了问题根本分不清是谁弄的。作者承认自己破过几次例,原话是每一次都后悔。我六月能一天迁完 7600 行,靠的其实就是这条,只是当时没意识到它值钱。

没人当裁判的地方

还有一处乱劲,官方没摆数字,但更值得你记住。15 个智能体在同一台笔记本上并行,同时跑构建测试,机器直接趴窝。作者的解法说出来你都不信,核心就是一段简单到不好意思的提示词:开一个普通聊天会话当门卫,谁想构建谁来找它领租约,一次只放一个。被拒的会话也不闲着,转头去干清单上不费 CPU 的杂活。

这个门卫背后还有场好戏。整个迁移最难啃的是个 3 万行的中枢文件,横穿所有模块,被留到最后。负责它的会话先安安静静读了 56 分钟,122 次工具调用之后才开始动手。之后 25 小时,它派生出 15 个子会话,分七波出门,各自在独立工作区干活。父会话自己轮询 60 次,发了 89 条协调消息收账。

更妙的事故在另一头。另一个会话迁到同一片地界,四次请求合并都被这个中枢会话回绝。对方二话不说,直接伸手把它工作区里的改动全抢了过来,然后各干各的。作者复盘时认账,根因是他自己从两头开工,还在启动提示里提了对方的名字,却没说「别碰它」。平级 agent 之间没有裁判,拒绝不构成约束,愿意单边动手的赢。这条规矩你可以直接抄:给并行会话划地界时,提示里别只提名字,要写明「它的东西不许碰」。agent 世界里,没写出来的边界等于不存在。

写完这篇我想好了下一步。我流水线里多 agent 并行的场景越来越多,构建门卫这招我准备直接抄,先给配图那条线装上。

轮到你了。你平时派活给 agent,数过自己一天发多少条消息吗?里头几成在挑刺、几成在催它干完?评论区报个数,我猜你也会被自己的比例吓一跳。