移动端与原生应用智能体。
编码智能体的优势不在于它写的代码比你好——而在于它可以一小时错二十次,然后留下通过的那一次。把这个循环搬到 iOS 或 Android 代码库上,光是一次干净构建就要吃掉几分钟,模拟器得先启动,而本该抓住这次回归的那个测试,渲染出来的是一块屏幕,而不是一个布尔值。答案不是把构建变快。而是把目标改造一遍,让智能体把它的尝试花在编译只要几秒的地方,并把整个应用的构建从内层循环降级为"每个候选跑一次"的关卡。
先给验证步骤做预算——其余一切都从它推出来。
"定位-编辑-验证"循环是按尝试次数计价的,而在原生移动端,验证步骤比在后端服务上贵一到三个数量级。在调提示词之前,先量出决定"你的智能体最好能好到哪儿"的那四个数:
- 被编辑模块的增量构建——你希望这个数以秒计。在一个模块化做得好的工程上它确实如此;在一个带着大片桥接面的单目标应用上则不然,因为动一个文件就作废了半张依赖图。
- 整个应用的干净构建——以分钟计,也是决定智能体究竟能不能负担得起一次完整构建的那个数。任何会强制干净构建的东西(一次依赖变更、一个代码生成步骤、一次 Gradle 配置改动)都属于另一类任务,要用另一份预算。
- 到"测试真正跑起来"的时间。一个 JVM 或 Swift 单元测试几乎立刻就开跑。一个 Android instrumented 测试或 XCUITest 需要一台已启动的模拟器、一次安装、一次应用启动——而启动往往是其中最大的那一项。
- 智能体将被评判的那套测试集的 flake 率。这是团队最常跳过的一项,也恰恰是决定这个循环收不收敛的那一项。见第 3 步。
把这四个数折成一个数字:每小时通过验证的尝试次数。一个后端智能体拿到的是几十次。一个未经改造的移动应用工程常常只给出三四次;而在一小时三次的节奏下,智能体不是在迭代——它是在交草稿。下面每一条建议都是在试图挪动这个数字,而每做完一条,你都该重新量一遍,而不是相信那次改动有用。
把目标劈成一个快内核和一个慢外壳。
回报最高的那一项改动不在智能体身上。它在代码库上:让智能体所编辑的那部分,不构建应用就能编译和测试。这就是移动团队跟自己吵了十年的那场模块化之争,只是如今多了一个新的、异常具体的回报。
- 把逻辑从视图类型里拽出来。网络、解析、持久化、格式化、状态归约与业务规则,都该待在不依赖 UIKit/SwiftUI 或 Android framework 的模块里。那些模块几秒就构建完,并且完全不需要设备、直接在宿主机上测——那才是你想让智能体待的地方。
- 让模块边界成为智能体的边界。把任务限定在一个模块内,并告诉外壳与它对应的构建与测试命令是哪些。一个因为不知道自己改了哪个 target 而把整套测试跑一遍的智能体,等于把你刚花钱买来的模块化又扔了。
- 即使改动落在 UI 里,也保留一条快路。一次 view-model 改动可以对着快内核验证;只有最终候选才需要外壳。要守住的分别是迭代与确认,这两件事并不需要同样的保真度。
- 密闭式构建系统有用,但别等它。如果你已经跑着 Bazel 或同类方案并带远端缓存,就把缓存给智能体——收益很大。如果没有,引入一套是一个季度的平台工作量,而不是这个月上线一个智能体的前提条件。
凡是代码库抵抗这件事的地方——一个单体 target、大量代码生成、一个人人都依赖的桥接头文件——诚实的读法是:智能体在那儿会很弱,而模块化才是真正的活。把这句话写进方案里,而不是在复盘会上才发现它。在一棵大树上做定位是另一个问题;见仓库导航与代码上下文。
把设备钉死,否则每一次失败都是含混的。
智能体分不出真回归与 flake。它只能看出测试红了,而它被训出来对"红"的反应是改代码直到它变绿——所以一套 flaky 的测试不是把智能体拖慢,而是在主动教它写错的代码。在模拟器上,flake 的来源是可以枚举的,而其中每一条都是可修的配置:
- 一份钉死的设备定义——一台具名的 simulator 或 AVD,固定的系统版本、屏幕尺寸与缩放倍率,由一份放在仓库里的脚本创建。不是"当时正好开着的那台"。
- 固定的 locale、地区、时区与时钟。日期格式化与货币渲染是那对经典的"过夜就红",而一次在 23:58 起跑的运行,行为应当与正午起跑的那次一致。
- 关掉动画,并给一个真正的空闲信号。在系统层面禁用 UI 动画,并等待一个明确的空闲条件,而不是 sleep。基于 sleep 的等待是屏幕类测试 flake 的最大单一来源,而在并行智能体运行造成的 CPU 争抢下,它只会更糟。
- 权限、引导与登录态预先授予。直接启动进一个已灌好数据、已登录的状态,通知与定位弹窗都已处理完毕。每一个需要智能体去关掉的模态框,都是你自愿领回来的一种失败模式。
- 确定性的网络。录制好的固定响应或一台本地桩服务器,绝不是 staging 环境。运行途中的一次 staging 部署,会让一套绿的测试因为任何 diff 都解释不了的理由变红。
拿这一条给智能体设闸:在一个未改动的提交上把候选测试集跑二十遍,数一数红了几次。如果不是零,那就先修这个,再谈在这个代码库上给智能体任何自主权;因为在它归零之前,你分不清"智能体弄坏了它"和"这套测试本来就这样"。这与 CI 修复智能体所需的纪律是同一套,只是来得更早、也更要紧。
让快照测试成为契约——并且绝不让智能体更新参考图。
智能体看不见你的应用。你给它的验证手段,就是它关于"这块屏幕对不对"的全部知识;而一个通过的单元测试,对一个如今渲染到刘海底下的视图什么也说明不了。快照(截图)测试是唯一一种能在循环负担得起的代价内补上这道缝的机制:把视图渲染成图像,与一张已提交的参考图比对,像素差异超过阈值就判失败。
- 参考图是经过评审、提交进仓库的产物,你支持的每一类设备与每一种外观模式各一套。它们是"这块屏幕长什么样"的规格说明,这也让它们成为人类评审在 diff 里真正该读的东西。
- 智能体可以提议一张新的参考图;它不可以接受一张。这是补丁生成与测试驱动循环所警告的"删断言"失败在移动端的形态:重录快照能把任何红变成绿,只要一条命令,而且在记录里看起来像进展。用 CI 强制执行——一份改动了参考图、且由智能体署名的 diff,必须由人工批准,靠机制,而不是靠约定。
- 能测视图就别测流程。一个固定状态下的视图快照又快又稳;一个要驱动四块屏幕才走到那个状态的 UI 测试又慢又飘。把状态直接推进去,渲染,比对。
- 端到端测试还是要留一小套,并且把它当关卡而不是循环——它能抓住逐视图渲染看不见的接线错误。十个每候选跑一次的场景,胜过两百个智能体从不去跑的场景。
- 把失败的差异图挂进智能体的上下文。一个有视觉能力的模型,拿到"前/后/差异"三联图,往往能说出原因;只拿到"快照不匹配,0.7%",它就只能猜。这是截图真正对得起它那些 token 的唯一一处,而它与通过GUI 操控去驱动应用截然不同——在你自己的代码库上,后者不该做。
在签名、entitlement 与生成式工程文件处划线。
原生平台有一片"没有任何测试会先失败"的发布面——那里的错误浮出来时,是一次被拒的构建、一条断掉的升级路径,或者某一个系统版本上的线上崩溃。把下面这些放到智能体够不着的地方,并说明理由:
- 签名、描述文件、keystore 与 entitlement。凭据不该出现在智能体沙箱附近,而一次 entitlement 变更改变的是应用在运行时被允许做什么——一个披着配置文件外衣的安全决定。相关:智能体的密钥管理。
- 商店元数据、隐私清单与数据安全声明。这些是关于你产品的法律陈述。智能体可以起草一份;提交的必须是人。
- 生成式工程文件。一个去编辑 Xcode
project.pbxproj或重新生成 Gradle lockfile 的智能体,产出的是没有评审者读得懂的 diff——也就意味着它未经评审就上了线。要么用一个输入可评审的声明式生成器来驱动工程结构,要么把这些文件设成智能体只读。 - 最低系统版本与依赖版本的提升。小 diff,大半径;而编译器不会告诉你在你仍要支持的那个旧系统上,哪个行为变了。把它们走依赖升级智能体的流程——那一类之所以单列,正因为它需要自己的协议。
- 任何只在真机上才会失败的东西。相机、蓝牙、后台执行、推送送达、电量表现。模拟器在这些地方的沉默不是证据。
这一切所运行的那个沙箱值得单独留心——一次移动构建需要一整套庞大的工具链、一块可写缓存,以及访问包仓库的网络权限,这比一个典型智能体任务的授权面更宽。见沙箱与执行。
按"每次尝试的构建代价"来挑活。
把候选任务放在一根轴上排序——一次尝试需要多少次完整构建——待办清单就自己排好了。划算的那些任务,是改动面宽、验证便宜、评审机械的那些:
- 跨大量调用点的废弃 API 迁移。机械、由编译器验证、对人来说无聊,而且 diff 评审起来很快,因为每一块看起来都一样。
- 本地化与字符串抽取。几百处小改动,由一条 lint 规则加一轮快照验证——而伪本地化快照能抓住那处本来要三周后才由译者报回来的截断。
- 在快内核上补测试。完全不需要构建应用,所以尝试很便宜,而产出还会复利。在移动代码库上,这通常是最好的第一个项目;见测试生成智能体。
- 基于符号化崩溃报告的分诊。栈回溯替你完成了定位,也就把循环里贵的那一半拿掉了。与调试与分诊智能体配合。
- 无障碍标签。可用规则审计、在快照差异里不可见,而且长期缺人——这是罕见的、智能体的不知疲倦本身就是全部价值的场合。
目前还不划算的:UI 耦合很重的区域里的新功能开发、任何需要对动效或手感作判断的活,以及那些信号只在真机热负载下才显现的性能工作。去量,别去假设——评估编码智能体讲的就是如何搭出那套告诉你"某类工作属于哪一栏"的任务集。
在你写下第一个提示词之前,先在你真实的仓库上量出"每小时通过验证的尝试次数",并在一个未改动的提交上把候选测试集跑二十遍。这两个数字决定这个项目。如果每小时尝试次数低于五,那么摆在你面前的活是模块化和一台钉死的设备——不是智能体配置——而按这个顺序做,正是"一个能把 PR 合进去的智能体"与"一个产出貌似可信、最后被团队重写的草稿的智能体"之间的差别。然后从快内核上的补测试开始,那里循环本来就便宜,而产出会让后面每一项任务都更便宜。