让 Claude Code 自己跑完验证闭环

每次改完代码都手动跑测试、贴报错、等 Claude 修,反反复复直到绿。后来我学会用 `/goal` + `/loop` 把验证闭环交给 Claude 自己跑,人只需要最后看一眼结果。

我在项目里踩过一个反复出现的坑。

某天要改一个检索接口的分页逻辑。我让 Claude 去改,它改完我跑测试,五个 case 挂了三个。我把报错贴回去,它修了两个,我又跑,还剩一个空指针。来回三次,半个下午过去了。

那时候我才意识到,我花最多时间的不是写代码,而是"改代码 → 跑测试 → 复制报错 → 等 Claude 修"这个循环。


后来我才知道 Claude Code 有 /goal/loop 这两个命令,专门就是干这个的。

本质上它们把"你当监工"变成了"你定标准,Claude 自己跑圈"。

/goal 定义"什么叫做完",/loop 让 Claude 按节奏反复执行,直到满足条件自动停。

我第一次用的时候,只用了三分钟配置,然后去喝了杯咖啡。回来的时候,所有测试已经绿了,Claude 还顺带把两个相关的边缘 case 一起修了。


具体怎么用

先定义一个可验证的目标,再开循环。

我在项目根目录打开 Claude Code,输入:

/goal 所有单元测试通过,无 skip,无 warn

这句话很重要。目标必须是 Claude 能自动判断的,不能说"代码质量好一点"这种虚的。

目标设好之后,启动循环:

/loop every 2m until: tests pass

every 2m 是检查间隔,until: tests pass 是停止条件。

Claude 会在后台每两分钟跑一次测试,如果挂了就自己修代码,修完再跑,直到全部通过才停下来。

如果你想让它跑得更激进一点,可以缩短间隔:

/loop every 1m until: tests pass

如果你在跑一个比较重的集成测试套件,两分钟可能刚跑完一轮。这时候间隔设太短反而会重叠,Claude 可能会在上一轮还没跑完的时候就启动下一轮。


一个实际例子

上周我在改 RAG 检索的混合排序逻辑。改动很小,但牵连的测试很多。

我先设了目标:

/goal npm test 全部通过,coverage 不低于 92%

然后启动循环,自己去喝咖啡了。

回来之后 terminal 里的输出大概是这样的:

[loop] 第 1 轮: 47 passed, 3 failed, coverage 89%
[loop] 第 2 轮: 50 passed, 1 failed, coverage 91%
[loop] 第 3 轮: 51 passed, 0 failed, coverage 93%
[loop] 目标达成,循环停止

它第一轮修了两个断言不匹配的 case,第二轮发现还有一个边界条件没处理,第三轮全部通过。

如果我手动来,至少也要贴三次报错、等三次回复。


Tricks

把目标写进 CLAUDE.md 的顶部

我把常用的验证目标直接写在 CLAUDE.md 最上面,每次新对话一进来就能看到。

这样不用每次都重新输入 /goal,Claude 启动时就带着标准工作。

/loop max: N 兜底

无限循环有个风险:如果测试一直失败,Claude 会一直修一直跑,烧token。

加上最大迭代次数:

/loop every 2m until: tests pass max: 20

这样最多跑二十轮,达不到就停下来告诉你哪里有问题,不会无休止地转。

组合使用 /goal + hooks

如果你想要更强的验证,可以在 .claude/settings.json 里加一个 stop hook。

这个 hook 会在 Claude 每次想结束对话的时候触发,自动跑一遍 lint + test,不通过就不让停。

{
  "hooks": {
    "Stop": [
      {
        "matcher": "Stop",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/verify-before-stop.sh"
          }
        ]
      }
    ]
  }
}

脚本内容很简单:

#!/bin/bash
npm run lint 2>&1 | tail -5
npm test 2>&1 | tail -10

这样即使你忘了开 /loop,Claude 每次想收工的时候也会先跑一遍验证。过不了就不让停,逼着它自己修完。

针对长任务的延迟检查

有些测试跑一次要三五分钟。这时候 every 1m 就没意义了,反而造成资源浪费。

我习惯把间隔设成测试平均耗时的 1.5 倍:

/loop every 8m until: e2e tests pass

给 Claude 留够时间跑完一轮,再开始下一轮。


踩坑

目标写模糊了,Claude 会自己宣布胜利

我第一次用的时候目标写的是"代码质量达标",结果第二轮它直接告诉我"代码质量已达标"就停了。根本没有跑测试。

/goal 后面的条件必须是 Claude 能通过命令输出判断真假的。

"所有测试通过"可以,"代码看起来不错"不行。

长循环会吃上下文

每轮循环 Claude 都要读一遍测试输出、分析失败原因、改代码、再读一遍测试输出。

跑十轮下来,上下文窗口会吃得很满。

这时候 /compact 就有用了。如果循环超过五轮,我手动发一次 /compact 把历史压缩掉,让 Claude 轻装上阵继续跑。

不要在循环里插话

如果你在 /loop 运行的时候突然插入一条消息,循环会被打断。

这时候有两种选择:要么重新发一遍 /loop,要么就接管过来手动修。

我一般选择后者——既然我插手了,说明情况有变,让 Claude 继续自动跑反而容易出问题。


说实话,这个功能改掉了我一个很大的习惯问题。

以前我总觉得"AI 写代码,人负责验收"。现在我更愿意把"跑测试、看报错、判断修没修好"这种重复劳动也交给 Claude,我只需要在开始前把标准说清楚。

人应该做判断,不应该做监工。

下次遇到"改完代码要跑很多测试"的场景,不妨试试把目标定好,开个 /loop,然后干点别的。

不过话说回来,Claude 自己跑圈的时候,你真的敢完全不管吗?

评论

此博客中的热门博文

我写了半年 prompt,这是我发现的最好的技巧