
实用技巧 — 知道这些就能指挥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 .
或者回到更早的:
"回滚到'待办添加功能完成'那次提交"
什么时候提交
规则很简单:
- 一个功能完成且运行正常时 → 提交
- 所有Hurl测试通过时 → 提交
- 开始下一个功能之前 → 必须提交
不提交就继续,出问题时没有可以回去的地方。就像玩游戏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测试失败 → 拒绝提交 → 修复 → 全部通过 → 提交 → 锁定
规则很简单:
- Hurl测试通过就锁定
- 锁定的测试禁止删除/修改
- 添加新功能时也要追加新的Hurl测试
- 所有现有测试 + 所有新测试都通过才能提交
让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的判定不一致的情况有吗?
总结
本课学到的:
- Hurl — 用纯文本声明"必须这样运行"的契约。验证的是行为不是代码
- Git — 创建"可以回到这个时间点"的存档点
- CI/CD — 安装"每次自动检查"的机械化验证
- 棘轮 — 三者结合就成了"通过就锁定、不会倒退"的齿轮
核心原理:
不要指示AI方法。给它什么必须通过的契约。
“做TDD” → 回归恶化。“这个Hurl必须通过” → 回归减少70%。差异在于指示和契约。
不要换模型,添加契约。
下一课预告
第3课学了用Hurl逐个保护API。但项目变大后需要保护的不只是API。数据库结构、安全策略、UI组件,这些都必须相互一致。
第4课学习yongol。用一个声明式规范管理API、DB、安全、UI,把AI的工作对象从代码转移到规范。突破氛围编程在200个端点时崩溃之墙的方法。
相关文章
- Hurl如何阻止氛围编程的漂移 — Hurl进行API契约验证如何防止氛围编程漂移的详细分析
- Ratchet Pattern — 让AI给527个函数写测试却停在40个的原因,以及用机械验证器让它跑到底的模式
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%),同一模型、完成判定主体不同