7. 让 AI 在判定器下循环
上一章留下了一片正确的红:测试都在,实现还是空壳。这一章把实现交给 AI,让它在这套测试的判定下循环,直到全绿。
这里有一个容易被忽略的分工。交给 AI 的是「实现怎么写」,没交出去的是「什么算对」,后者锁在只读的测试文件里。AI 的活动范围可以很大,但它撞的墙是固定的,墙由人砌好。
放手让它循环
Section titled “放手让它循环”允许 AI 在这一轮里自己做四件事,不必每步都停下来问:
- 运行测试,看当前是红是绿。
- 读失败信息,定位是哪条验收条件没满足。
- 加临时诊断(比如打印某个中间变量),弄清数据在哪一步走偏。
- 修改实现,然后重新跑测试。
这四件事循环往复。空壳会被逐步填成真正的解析:按行拆分、逗号分隔、判断单位是否为空、判断价格是否缺失。上一章的三条红会一条条转绿。AI 拿到的实现最后大致是这样:
export function parseQuotes(text) { const lines = text.trim().split('\n').filter(Boolean); return lines.map((line) => { // 逗号分隔成三段,并去掉每段两头的空格 const [name, unit, price] = line.split(',').map((cell) => cell.trim()); // 单位为空算缺失 const missingUnit = !unit; // 价格为空,或数值等于 0,都算缺失(第5章裁决) const missingPrice = price === '' || price === undefined || Number(price) === 0; return { name, unit: missingUnit ? '缺失' : unit, price: missingPrice ? '缺失' : Number(price), missing: missingUnit || missingPrice, }; });}再跑 node --test,三条测试全部转绿。
全绿之后不要立刻收工
Section titled “全绿之后不要立刻收工”测试变绿是停下自动循环的信号,不是收工的信号。绿只说明「实现满足了那几条验收条件」,没说明「AI 为了变绿只动了该动的地方」。为了变绿,一个足够自主的 AI 也可能顺手改了别的东西。绿灯亮起后,由人接手检查四样东西:
| 检查项 | 在看什么 | 为什么要看 |
|---|---|---|
| 测试文件 | 有没有被改动或删条目 | 尺子被改短,绿就不算数 |
| 依赖文件 | package.json 有没有多出没提过的包 | 多引入的依赖是新的风险面 |
| 权限与配置 | 部署、权限、环境配置有没有变 | 这些改动的影响在测试之外 |
| 实现 Diff | 改动是不是只落在解析逻辑上 | 意料之外的改动要能解释来源 |
前三项只要有一项被动过,就先回到那一处问清楚原因,不能因为「反正绿了」放过去。第四项是这一章第一次正面看 Diff,只做「改动范围对不对」这一层的粗筛;怎么系统地只看改动就作裁决,是第 10 章的事。
留下什么产物
Section titled “留下什么产物”这一章留下两样东西:一次从红到绿的提交,和一份「约束文件没被暗改」的核对清单。
提交把这次红转绿的完整改动定格进历史,随时能回看当时改了什么。核对清单记录测试、依赖、配置这三样在这一轮里保持原样,是这次绿灯可信的旁证。
到这里,实现满足了验收条件,Diff 也粗筛过了。但「符合哪些条件」还不等于「看懂了这段程序怎么运转」。下一章补上这一课:怎么读一小段自己没写的代码。
这一章可以对 AI 说的一句话
Section titled “这一章可以对 AI 说的一句话”测试已经锁好,现在请写实现,可以自己跑测试、读报错、加诊断、反复改,直到全绿。全绿后请停下,并告诉我:有没有改过测试文件、有没有新增依赖、有没有动过任何配置。这三样只要动了,先跟我说明原因。