第3课

实用技巧 — 知道这些就能指挥AI

四句话就够了。

对智能体说:“创建Hurl测试” AI会编写一份验证功能是否正常运行的契约书。不懂代码也能读懂的纯文本。

对智能体说:“添加这个功能。但现有Hurl测试必须通过” 这一句话就能阻止漂移。AI添加新功能时如果破坏了现有功能,Hurl会用红字告诉你。

对智能体说:“提交” 保存正常运行的状态。跟游戏存档一样。下次搞砸了就回到这里。

对智能体说:“回滚” 恢复到上一个存档点。撤销AI破坏的东西。

这四句话的模式

功能完成 → “创建Hurl测试” → 确认通过 → “提交” → 下一个功能 → “现有Hurl必须通过” → 出问题就"回滚"

这就是棘轮。只进不退的齿轮。不管5个还是50个功能,现有的不会坏。

为什么这样有效?

第2课学过了。给AI意见就谄媚,给事实就修正。Hurl返回的不是意见,而是事实。“test failed: status 401, expected 200”——这没有什么好谄媚的。

快速体验

在第2课实操的待办应用上做一个Hurl测试。3分钟搞定。

对智能体说:“编写一个Hurl测试来验证当前待办添加功能是否正常运行”

AI会创建一个.hurl文件。

对智能体说:“运行Hurl测试”

通过就是绿色。现在故意破坏一下。

对智能体说:“把待办添加API响应中的id改成todo_id”

对智能体说:“运行Hurl测试”

红字显示失败。这就是漂移检测。

对智能体说:“回滚”

又变绿了。这就是棘轮的核心。


为什么要这样指挥

第2课看到了问题。逻辑漂移、上下文消失、谄媚偏差。超过5个功能现有的就坏了,AI还虚假宣称"没问题"。

这一课学习防止这些问题的三个工具。全部是软件工程师用了几十年的东西。不需要会读代码。AI编写、AI运行。你只需确认"通过了吗?"

三个工具的角色:

工具类比做什么
Hurl契约书声明"这个功能必须这样运行"
Git存档点保证"可以回到这个时间点"
CI/CD自动监控摄像头机械化"每次自动检查"

Hurl — 用纯文本声明API契约

Hurl是什么

Hurl是记录"这个API应该怎样运行"的文件。

用游戏来比喻:在RPG里向NPC买药有规则"1瓶药 → 金币-50,体力+100"。检查这个规则在补丁后是否变了。这就是Hurl做的事。

来看一个实际的Hurl文件:

# 添加待办
POST http://localhost:8080/api/todos
{
  "title": "买牛奶",
  "priority": "high"
}
HTTP 201
[Asserts]
jsonpath "$.id" exists
jsonpath "$.title" == "买牛奶"
jsonpath "$.priority" == "high"
jsonpath "$.completed" == false

不懂代码的人也能读:

  • POST — 向服务器发出"添加"请求
  • http://localhost:8080/api/todos — 待办列表地址
  • { “title”: “买牛奶” } — 发送这样的数据
  • HTTP 201 — 成功应返回201响应
  • jsonpath “$.title” == “买牛奶” — 返回的数据中必须有"买牛奶"

这就是契约。“添加待办时应返回201,标题和优先级原样返回。“这个契约被打破时Hurl会用红字通知你。

再来一个:

# 未认证访问应被拒绝
GET http://localhost:8080/api/todos
HTTP 401

“不登录直接访问待办列表应返回401(需要认证)。“这也是契约。AI在"整理"认证代码时如果打破了这个规则,Hurl会立刻抓到。

为什么选Hurl — 与单元测试的区别

“测试工具那么多,为什么选Hurl?“对氛围编程者有特殊的理由。

单元测试(unit test)检查代码内部的函数。用汽车来比喻,单元测试是拆开发动机零件逐个检查,Hurl是在真正的道路上试驾。函数名变了测试也会坏,AI重构时测试也得一起改。**如果同时给AI修改代码和测试的权限,AI会把测试改成和代码一致。**测试通过了,但原来的规则消失了。

Hurl不同。它在服务器的入口检查。发送请求、确认响应。它不知道代码内部结构。AI怎么改代码都行,外部可观测的行为相同就通过,不同就失败。

单元测试Hurl
汽车类比拆发动机零件检查上路试驾
AI改代码时测试可能一起变测试不变,只判定结果
阅读难度需要懂代码像普通文字一样可读
漂移检测AI也改测试就漏掉独立于代码,自然检测

Hurl验证的不是代码而是行为。 AI可以自由改代码。但行为不能变。这个区分是抓住漂移的关键。

为什么这种方式有效 — 研究证明

第2课学了谄媚偏差。“写测试"这个建议也取决于怎么给,结果完全不同。

TDAD(Test-Driven AI Development)研究(2026)精确实验了这一点。让AI修Bug时改变测试条件:

条件回归率(现有功能崩坏比率)
基准(无测试指示)6.08%
“做TDD"过程指示9.94%(恶化!)
将受影响的测试文件作为上下文提供1.82%(减少70%)

结果惊人。指示"做TDD"反而更糟。 AI为了遵循过程指示反而偏离了本来的工作。但给出"这个测试文件必须通过"的具体上下文后,回归减少了70%。

差异很明确:

  • “边写测试边开发” → 过程指示 → AI混乱
  • “这个Hurl文件必须通过” → 具体契约 → 回归减少70%

不是指示方法,而是给出什么必须通过的契约。上面学到的"第三句话"就是这个。

Git — 可回滚的存档点

Git是什么

玩游戏会存档。在Boss战前存档,输了就读档。

Git是代码的存档功能。“当前这个状态挺好” → 存档(提交)。下一个任务搞砸了 → 恢复到上一个存档。

氛围编程没有Git时:

添加功能1 → 正常
添加功能2 → 正常
添加功能3 → 功能1坏了!
→ 想回滚但... 功能2的状态是什么来着?
→ 让AI"恢复到之前" → AI不知道"之前"是什么
→ 从头开始

有Git时:

添加功能1 → 正常 → 提交(存档1)
添加功能2 → 正常 → 提交(存档2)
添加功能3 → 功能1坏了!
→ "回到存档2" → 恢复到功能1、2正常的状态
→ 用另一种方法重试功能3

Git用法:两个词就够

不需要学Git的几十个命令。氛围编程者需要的只有两个。

“提交” — 保存当前状态

"把现在的状态提交。消息写'待办添加功能完成'"

AI执行的命令:

git add .
git commit -m "待办添加功能完成"

“回滚” — 恢复到之前的状态

"回滚到上一次提交"

AI执行的命令:

git checkout .

或者回到更早的:

"回滚到'待办添加功能完成'那次提交"

什么时候提交

规则很简单:

  1. 一个功能完成且运行正常时 → 提交
  2. 所有Hurl测试通过时 → 提交
  3. 开始下一个功能之前 → 必须提交

不提交就继续,出问题时没有可以回去的地方。就像玩游戏3小时不存档一样。

好的模式:
功能完成 → Hurl通过 → 提交 → 下一个功能

坏的模式:
功能1 → 功能2 → 功能3 → ... → 什么东西坏了 → 无处可回

Git的类比:登山营地

攀登珠穆朗玛峰不是一次性冲到顶。大本营 → 营地1 → 营地2 → … → 顶峰。在每个营地扎帐篷、囤物资。天气变坏就下撤到上一个营地。没有营地的话暴风雪来了就完了。

Git提交就是营地。每完成一个功能就建营地。即使AI在下一个功能中搞砸了,你也能回到上一个营地。

CI/CD — 机器自动守护

CI/CD是什么

CI(持续集成)是"每次上传代码时自动跑测试”。 CD(持续部署)是"测试通过后自动部署”。

现在只需要了解CI。CD以后再说。

没有CI时:

你:  "添加功能"
AI:  "完成了!"
你:  (只在界面上看了新功能)"不错嘛!"
→ 不知道现有功能已经坏了就过去了

有CI时:

你:  "添加功能"
AI:  (写代码)
机器:(自动运行所有Hurl测试)
机器:"现有登录测试失败!"
你:  "登录坏了。修一下。"
AI:  (修复)
机器:"所有测试通过"
你:  "提交"

你不需要每次手动跑Hurl测试。机器每次自动帮你跑。

用GitHub Actions创建CI

把代码上传到GitHub后,GitHub Actions自动运行测试。一个配置文件搞定。

让AI来做:

"用GitHub Actions设置CI。
 - 每次push代码时自动运行Hurl测试
 - 需要先启动服务器再跑Hurl测试
 - 测试失败时阻止合并
   (PR是'这段代码可以合并吗?'的请求,merge是实际合并)"

AI会创建.github/workflows/ci.yml文件。不需要精确理解下面的内容。AI来创建,你只需知道要点:

  • 每次push代码时自动执行
  • 启动服务器并运行Hurl测试
  • 任何一个失败就亮红灯

大概是这样的内容:

name: CI                          # 这个自动化的名称
on: [push, pull_request]          # 每次上传代码时执行

jobs:
  test:
    runs-on: ubuntu-latest        # 在云服务器上运行
    steps:
      - uses: actions/checkout@v4 # 获取代码

      - name: 启动服务器            # 启动应用服务器
        run: |
          docker compose up -d
          sleep 5

      - name: 运行Hurl测试          # 运行所有测试
        run: |
          hurl --test tests/*.hurl

      - name: 关闭服务器            # 关闭服务器
        run: docker compose down

CI的类比:大楼的火灾报警器

大楼有火灾报警器。起火自动响。不需要保安24小时巡逻。

CI是代码的火灾报警器。Hurl测试失败时自动通知你。不需要你每次手动检查。

区别是这样的:

手动检查CI
检查时机想起来的时候每次自动
检查范围只看新功能全部
遗漏经常没有
成本时间和精力免费(GitHub Actions免费方案)

三个工具结合:棘轮锁定

Hurl + Git + CI结合就成了棘轮(ratchet)。棘轮是只能单向转动的齿轮。转就往前走,松手就停住但不会倒转。

功能1完成 → 写Hurl测试 → 全部通过 → 提交 → 锁定
功能2完成 → 追加Hurl测试 → 现有+新测试全部通过 → 提交 → 锁定
功能3进行中 → 现有Hurl测试失败 → 拒绝提交 → 修复 → 全部通过 → 提交 → 锁定

规则很简单:

  1. Hurl测试通过就锁定
  2. 锁定的测试禁止删除/修改
  3. 添加新功能时也要追加新的Hurl测试
  4. 所有现有测试 + 所有新测试都通过才能提交

让AI"重构代码”,AI自由地改代码。但Hurl测试失败就拒绝提交。AI必须在保留所有现有行为的前提下工作。

这与前面看到的TDAD研究结果完全一致。不是"写测试"这样的过程指示,而是"这个Hurl文件必须通过"这样的具体契约。智能体可以选择方法,但不能违反契约。

第2课的问题如何解决

第2课的问题第3课的解决
逻辑漂移Hurl保护现有行为。AI改代码但行为变了就失败
上下文消失Hurl文件永久保存决策。会话换了契约还在
谄媚偏差(“全做好了”)CI机械判定。不是AI自我报告而是pass/fail
决策-实现混杂Hurl在独立于代码的文件中声明决策(行为)
乘法衰减每步棘轮锁定,衰减就被重置

再看第2课的关键实验结果:

自主智能体:  40 / 527  (7.6%)  — 智能体宣布"完成"
棘轮CLI:    527 / 527 (100%)  — 机器宣布"还剩487个"

同一个模型。区别是谁判定"结束”。AI判定就停在40,机器判定就跑到527。Hurl + CI正是扮演那个"机器"的角色。

给已有应用做事后加固

还没做过应用的话可以跳过这一节。 需要的时候再回来看。

“我已经用氛围编程做了应用正在运营,要从头来过吗?”

不用。不需要从头再来。这不是打地基,而是做抗震加固。 不关店门、在营业的同时加固建筑。

第1步:用Hurl捕获当前行为

用Hurl记下当前应用是怎么运行的。有API文档就直接搬,没有就让AI来:

"分析当前应用的所有API端点并写Hurl测试。
 必须按照当前实际运行的方式来捕获测试。"

目标:用纯文本声明"这就是目前的运行方式”。

不需要一次全做完。从最重要的开始一个个来:

  • 登录/注册 — 这个坏了什么都干不了
  • 支付 — 涉及金钱的最优先
  • 核心业务逻辑 — 你的应用做的主要事情
优先级:
1. 登录API → 写Hurl测试 → 确认通过
2. 支付API → 写Hurl测试 → 确认通过
3. 核心CRUD → 写Hurl测试 → 确认通过
... (有空再做其余的)

第2步:用Git保存当前状态

如果还没用Git:

"把这个项目初始化为Git仓库,提交当前状态。
 消息写'保存现有应用状态'"

已经在用Git的话,在所有Hurl测试通过的状态下提交。

第3步:设置CI

如果代码在GitHub上:

"用GitHub Actions设置CI。每次push时自动运行Hurl测试。"

第4步:从现在起安全了

从此不管让AI做什么,Hurl都在保护现有行为:

"添加这个功能。但所有现有Hurl测试必须通过。"

漂移发生时CI立刻捕获。在到达生产环境之前。

反馈的力量:意见 vs 事实

记住第2课学的谄媚偏差。给AI意见就谄媚,给事实就修正。

Hurl返回给AI的不是意见而是事实:

意见:"登录功能好像有点不对劲"
→ AI:"我检查了一下没问题!"(谄媚)

事实:"test failed: status 401 ≠ expected 200"
→ AI:(精确到行级别修正)

问"你确定吗?“AI会推翻正确答案。但告诉它"line 41: expected user_id, got userId"就没有可以谄媚的对象。数字和位置不是情感。

这就是Hurl、Git、CI这样的确定性工具(相同输入总是产生相同结果的工具)有效的根本原因。这些工具不谄媚。pass就是pass,fail就是fail。

常见问题

Q:怎么知道Hurl文件本身是对的?AI可能写错了吧。

Hurl文件首次编写后运行通过了,那个时点的行为就被捕获了。之后代码变了导致Hurl失败,那是行为变了的信号。不是Hurl本身错了,而是在检测行为是否变了。

如果初次编写与预期不符:运行一下,用眼睛确认结果。“添加待办应该返回201但返回了200”——这种你自己能判断。

Q:Hurl测试太多了管理起来不麻烦吗?

开始3-5个就够了。登录、核心功能、最重要的业务规则。以后需要时一个个加。不必一次完美。

Q:需要背Git命令吗?

不用。“提交"和"回滚"两个词就够了。AI会执行适当的Git命令。

Q:GitHub Actions要花钱吗?

公开仓库免费。私有仓库也有每月2,000分钟免费额度(Free plan)。小型项目够用。

Q:已有应用应用Hurl后,现有代码会变吗?

不会。Hurl不碰代码。只是在代码旁边添加.hurl文件。只是捕获当前行为,不修改代码。

实操:构建Hurl + Git + CI流水线

使用第2课实操的待办应用。

前提准备:安装Hurl

让Claude Code来装就行:

"帮我安装Hurl"

或者手动安装:

# Ubuntu/WSL
curl --proto '=https' --tlsv1.2 -sSf https://hurl.dev/install.sh | bash

确认安装:

hurl --version

显示版本号就成功了。

Step 1:用Hurl捕获当前状态(15分钟)

让AI来做:

"为当前待办应用的API编写Hurl测试。
 至少包含以下场景:

 1. 添加待办 → 201响应,标题原样返回
 2. 查询待办列表 → 200响应,返回数组
 3. 标记待办完成 → 200响应,completed变为true
 4. 删除待办 → 200或204响应
 5. 未认证访问 → 401响应(如果有认证的话)"

运行测试:

"运行Hurl测试"

确认全部通过。有失败的让AI修。

Step 2:Git提交(5分钟)

"把当前状态Git提交。消息写'添加Hurl测试 — 保护基本CRUD'"

这是第一个存档点。

Step 3:添加功能 + 棘轮确认(20分钟)

还记得第2课实操时坏掉的功能吗?这次在Hurl保护下添加。

"给待办添加优先级(高/中/低)功能。
 但所有现有Hurl测试必须通过。
 新功能也要追加Hurl测试。"

检查要点:

  • 所有现有Hurl测试通过了吗?
  • 新的Hurl测试也通过了吗?
  • 第2课中坏掉的东西这次被保护住了吗?

通过就提交:

"提交。消息写'添加优先级功能 + Hurl测试'"

再加一个:

"添加截止日期功能。
 所有现有Hurl测试通过 + 添加新功能的Hurl测试。"

通过就提交。这就是棘轮。只进不退。

Step 4:GitHub Actions CI设置(10分钟,可选)

没有GitHub账号就跳过这一步。 只做Step 1-3也能体验棘轮的核心。GitHub以后再注册也行。

如果有GitHub仓库:

"用GitHub Actions设置CI。
 - 每次push时自动启动服务器并运行Hurl测试
 - 测试失败时阻止代码合并"

Push到GitHub,在Actions标签确认测试自动运行。

Step 5:故意制造漂移实验(10分钟)

确认CI正常工作后,故意破坏一下:

"修改待办添加API的响应格式。
 把待办编号的字段名从id改成todo_id。"

确认Hurl测试失败。确认CI亮红灯。这就是漂移检测。

"回滚。恢复原样。"

确认绿灯回来了。

要记录的:

  • 第2课实操 vs 第3课实操:添加相同功能时现有功能坏了吗?
  • Hurl捕获了几次漂移?
  • AI说的"完成了"和Hurl的判定不一致的情况有吗?

总结

本课学到的:

  1. Hurl — 用纯文本声明"必须这样运行"的契约。验证的是行为不是代码
  2. Git — 创建"可以回到这个时间点"的存档点
  3. CI/CD — 安装"每次自动检查"的机械化验证
  4. 棘轮 — 三者结合就成了"通过就锁定、不会倒退"的齿轮

核心原理:

不要指示AI方法。给它什么必须通过的契约。

“做TDD” → 回归恶化。“这个Hurl必须通过” → 回归减少70%。差异在于指示和契约。

不要换模型,添加契约。


下一课预告

第3课学了用Hurl逐个保护API。但项目变大后需要保护的不只是API。数据库结构、安全策略、UI组件,这些都必须相互一致。

第4课学习yongol。用一个声明式规范管理API、DB、安全、UI,把AI的工作对象从代码转移到规范。突破氛围编程在200个端点时崩溃之墙的方法。


相关文章


Reins Engineering 全部课程

课程标题
第1课如何指挥AI
第2课如何不信任AI
第3课不会崩溃的应用
第4课将决策移出代码
第5课有缰绳的AI
第6课通过就锁定
第7课翻转谄媚
第8课智能体的工厂
第9课代码之外的自动化
第10课数据的法则

参考资料

  • TDAD(Test-Driven AI Development)2026 — “做TDD"过程指示使回归率恶化至9.94%,将测试文件作为上下文提供使回归率降至1.82%(减少70%)
  • Ratchet Pattern实验 — 自主智能体40/527(7.6%) vs 棘轮CLI 527/527(100%),同一模型、完成判定主体不同