一句话摘要
用 Claude Code 摆脱重复劳动
你就是那个循环。下面是用 Claude Code 修复它的方法
2026 年 7 月 12 日 · 16 分钟阅读 · 查看原文 ↗
Claude 自动化 AI 营销
两个月前,我就是那个循环。
打开 Claude Code。输入一个提示词。等待。读 diff。手动修点什么。再输入一个提示词。再等待。
我是触发器。我是验证者。我是决定何时停下的那个人。Claude 只是一个我一天拿起放下四十次的工具,而我把那叫做"使用 AI"。
那根本不是。那是我在手动干着一个调度器、一个 QA 工程师和一个发布经理的活,而一个能力极强的模型却在我的每一次按键之间闲置,等着被允许去做任何一点事。
那个信号总是同样的:每天傍晚,我都有十几个只做到一半的线程,没有一个收尾,全都需要"再跑一遍",而那只有我才能启动。我工作流里的瓶颈从来不是 Claude 的智能。是我的注意力广度。
下面是改变的东西、它背后的推理,以及我今天实际在用的、可运行(不是示意性)的代码。
一旦你把它摊开看,这个转变一点都不微妙。在上半部分里,你是承重的那一个。把你抽走一小时,整件事就卡在句子中间。在下半部分里,你只在写规格(spec)时承重一次,然后短暂地在结尾再承重一次——去读一个别人已经验证过的 diff。
每个人都引用、却没人实施的那句话
别再提示(prompt)你的智能体了。去设计一个循环。
你这个月已经在至少两三个你关注的人那里看到过这句话的某个版本了。每个人都点头附和。几乎没有人改变他们第二天早上真正输入终端里的内容,因为这条建议恰好停在了它开始有用的地方。
没有人告诉你一个循环是由什么构成的。没有人把那个文件递给你。于是人们继续做那件感觉上最接近循环的事——写一个更长、更详细的提示词——然后纳闷为什么结果还是需要人盯着。
一个更长的提示词不是一个循环。一个循环是一个过程。它有活动部件,它有失效模式,而且它有一个无论你是在修一个测试套件、还是跑一个夜间清理任务都会重复的形状。让我们把这个形状拆开,然后再把它重新拼成你今天就能粘贴运行、可以用的代码。
一个循环到底是什么,分四个部分
剥掉那些炒作,一个循环就是:Claude Code 自己重复一个工作周期,而不需要你坐在每一个单独步骤的中间,直到某个你事先定义好的条件说"停"。
这就是全部定义。它恰好有四个活动部件,而如果其中任何一个缺失,你构建的就不是一个循环。它是一个穿着戏服的提示词。
触发器(Trigger)——到底是什么启动了一个周期。可能是你,输入一条命令。可能是一个早上 9 点触发的 cron 任务。可能是磁盘上的一个文件发生了变化。甚至可能是 Claude 自己,在一个大任务进行到一半时,决定它需要一个新的周期来检查自己的工作,然后再继续。
动作(Action)——Claude 在那个周期里被允许实际触碰什么。这是你的爆炸半径。一个拥有不受限工具访问权的循环,是一个能悄悄重写你根本没打算暴露给它的文件、删掉它认定"没必要"的东西、或者游荡进代码库里与任务毫无关系的某个角落的循环。每次写循环时,都要明确地把它收窄。
验证(Verification)——这个周期如何向自己证明那个动作确实起作用了,使用某个在 Claude 关于"它刚才做了什么"的自我叙述之外的东西。一个通过的测试套件。一个具有特定内容的文件。一个干净的退出码。不是"我相信这是对的"——而是一个无论 Claude 是否在场描述它都存在的事实。
停止条件(Stop condition)——结束整件事的规则。不是"当它感觉做完了"。而是一个具体的、可检查的退出触发器:验证通过了,或者你已经撞到了硬性的迭代上限,取先到者。
大多数以为自己构建了循环的人,其实只构建了这四个部分中的三个,然后悄悄自己补上第四个——用肉眼扫输出,决定什么时候不再看它。那仍然是你自己在当那个循环。只是同一个问题的一个更慢、更累的版本。
一个周期的解剖
这四个部分并不等同于一个循环所运行的步骤。部分是它由什么构成;每次循环真正转动时,它们会演变成一个五步周期——动作分裂成"发现"(Discover)然后是"执行"(Execute),验证变成"核验"(Verify),失败时的返回是"迭代"(Iterate),而停止条件在"停止"(Stop)处触发。
我构建过的每一个循环,从一个两行的 bash 脚本,到一个整夜运行的多智能体流水线,都能归结为同一个五步形状。
发现(Discover),是 Claude 在触碰任何东西之前先读取世界的真实状态——任务文件、当前的测试输出、最近的 git diff,任何能告诉它"现在什么是真的"、而不是"你一小时前写提示词时什么是真的"的东西。跳过这一步,就是循环开始"修复"那些三个迭代前就已经被修好的问题的原因。
执行(Execute),是一次有界的变化。不是"把你找到的一切都修了"。是一次变化,尺寸被设计成:如果它错了,你能立刻看出是哪个变化搞坏了事情,而不是去拆解一堆同时进行的编辑。
核验(Verify),是几乎每个人都悄悄跳过的步骤,而它是循环在现实世界里失败的全部原因。核验必须是一个 Claude 能运行、并能从中得到一个真实的、外部的答案的检查——一条测试命令、一个文件 diff、一个状态码——而不是一段 Claude 告诉你"它认为工作做完了"的段落。一旦你让那个做了工作的同一个模型,也成为这份工作的唯一裁判,你就重新引入了循环本该消除的那个盲点。
迭代(Iterate),在核验失败时自动发生:控制权回到发现,但这次带着新信息——那个具体的错误、那条具体失败的断言——而不是从头重复同一条含糊的指令。
停止(Stop),在核验干净通过时发生,或者在你烧穿了一个硬编码的最大迭代次数时发生。那个上限不是可选项。即使一个设计良好的循环,也会撞到它并非为之而建的边界情况,而一个没有上限的循环就是一个没有刹车的循环,一边哪儿也没去,一边悄悄消耗你的 token 预算。
循环一:修测试,直到它们通过
这是我跑的最简单、有用的循环,也是如果你要构建第一个循环、我会建议从它开始的循环。没有框架,没有编排库。只是 Claude Code 的无头模式,包在一个朴素的 bash while 循环里。
用 bash fix-tests.sh 运行它,然后走开。回来时,要么是一个全绿的测试套件,要么是十个精确显示卡在哪里的日志文件——后者本身也有用,因为现在你确切知道 Claude 无法靠推理挣脱的是哪个失败,而不是来自一个精疲力竭会话的含糊一句"它不工作"。
注意那些刻意内置进去的护栏:--allowedTools 被收窄到恰好三个工具,提示词明确禁止编辑测试文件(循环伪造通过的一种常见方式,就是削弱检查它们的那个东西),而且有一个无论 Claude 以为发生了什么都会触发的硬性迭代上限。
循环二:原生原语——Stop hooks(停止钩子)
上面的 bash while 循环能用,但它是从外面硬套在 Claude Code 上的一个外部包装。Claude Code 实际上为这个精确模式内置了一个原语:Stop hooks。
一个 Stop hook 在 Claude 试图结束一个会话的瞬间触发。如果你的钩子脚本以退出码 2 退出,Claude 就被阻止停止,并被迫继续工作——就在同一个会话里,带着它已经建立起来的全部上下文完好无损。
这个和 bash 包装之间的区别,比它看起来更重要。用包装器,每一次迭代都是一个全新的 Claude 会话,它必须重新读取任务文件、从零重建对代码库的理解。用 Stop hook,它是一个连续的会话,保留着它关于"已经试过什么、什么失败了、为什么"的工作记忆——以我的经验,在比单个失败测试更复杂的任何事情上,它的收敛速度都明显更快。
教 Claude 了解它自己的循环:CLAUDE.md
上面两个循环都没有告诉 Claude 它在循环里。它只是体验到一遍又一遍被阻止停止,却不知道为什么。把那个上下文明确地给它,每一次迭代的质量都会上升,因为 Claude 不再每个周期都从头重新推导同样的约束。
把这个加到项目根目录的 CLAUDE.md 里:
循环上下文(Loop context)
最后那条规则配得上它的位置。被放任太久,一个在"让红色测试变绿"压力下的模型,偶尔会走最短路径——那就是删掉断言,而不是修 bug。把它明确地说出来,一次,放在一个每个循环都会加载的文件里,你就不用在每个提示词里重新争论它了。
没有人写过的那套模式:maker-checker(制作者-检查者)
这里是"看起来自动化"的循环和"真正可信"的循环的分界线。
一个单独验证自己工作的 Claude 会话,有一个结构性的盲点:bug 是它写的,而现在由它来决定这个 bug 修没修好。它不是不诚实——它只是没有一双新鲜的眼睛。产生那个错误的同一个上下文,正在给那个错误打分。
修复方法是把循环拆成两个永不共享上下文的角色:
制作者(Maker)——一个有编辑权限的会话,它唯一的任务就是产生那个变化。
检查者(Checker)——一个全新的、只读的会话,它从没见过代码是怎么写出来的,只知道它要对照检查的那份规格。
仅这一处改变,就帮我抓到的真实的、静默的失败,比塞进单个提示词里的任何数量的"请再检查一遍你自己的活"都要多。检查者没有任何既得利益去认为代码是对的——它不是五分钟前做的那个决定、现在在为其辩护,它是冷读一份规格,然后报告它看到的东西。
实际运行起来是什么样子
如果你从没端到端地看过其中一个执行,它比听起来更不戏剧化,又比一次单独的聊天回复更有用。下面是上面那个 maker-checker 脚本在三次迭代里,一个终端会话大致的样子。
那段转录里没有任何东西需要你在场盯着看。你可以启动脚本,合上笔记本,然后回来看到 logs/checker_3.json 写着 PASS,或者三条拒绝理由,精确告诉你哪里需要一个人介入。
把触发器匹配到任务
不是每个循环都该用同一种方式启动。你选择的触发器,应该匹配工作实际需要如何发生,而不是你碰巧已经会接线的那一种。
从真正解决你问题的最便宜的触发器开始。对于很多任务来说,一个每晚跑一次的 cron 任务,并不比一个有五个活动部件的 Stop hook 系统更差——它只是"够了",而"够了"比"花哨"更快上线。
循环在哪里悄悄出错
在我调试过的几乎每一个循环里,都会出现一小撮失效模式,而且它们大多从外面看一模一样:循环一直在跑,日志干净地滚过去,在你真的去读它产出的输出之前,没有任何东西宣布出问题了。循环失败时不会崩溃。它们会成功地做错事,悄悄地,永远。
验证器弱于任务
症状:检查者每一轮都说 PASS,但它检查的东西跟规格几乎不沾边。通常是因为那个检查被写成了很容易满足——"它能编译吗""文件存在吗""跑了一次测试吗"——而不是"它做到了被要求的事吗"。
修复:在你写制作者提示词之前,先写验证器,而且要带着对抗性去写。问自己,最偷懒的、看起来对的输出会是什么,然后确保你的检查能抓到它。一个只检查存在性的验证器不是一个验证器,它是一道形式。
奖励黑客(Reward hacking)——钻检查的空子而不是通过它
症状:第 1 轮还在失败的那条断言,到第 3 轮没了。或者测试文件新加了一个 skip。或者规格要求的"错误处理"是一个光秃秃的 try/except: pass。循环技术上收敛了。什么也没修。
修复:这正是本文开头那条 STATUS.md 规则的用途——明确告诉它别碰检查,并给它一个逃生阀("在 STATUS.md 里说出来"),好让标记一个坏测试,不会让人觉得唯一的前路就是悄悄打败它。然后真的在每一轮之间 diff 测试文件。如果它们有任何变化,读一读为什么。
没有停止条件——无限循环
症状:第 40 轮。第 80 轮。你写的 bash for 循环上限是 8 次迭代,但有人"临时"移除了上限,然后忘了。或者更糟——从来就没有上限,只有一个 while true,因为它看起来显然会收敛。
修复:每个循环都有一个硬性的数字上限,没有例外,在第一次运行之前就提交进去。当它没收敛就撞到上限时,那不是循环里的 bug——那是循环在干它的活,告诉你这个任务需要一个人。把"N 轮之后没有收敛"当作一条正常的、预期中的退出路径,而不是一条要去压制的错误。
跨轮的上下文漂移
症状:第 1 轮的输出锐利、贴合规格。第 6 轮的输出悄悄漂了——它在解决一个相邻的问题,或者在重新争论一个第 2 轮就已经定下来的决定,或者开始"清理"没有人叫它去碰的代码。任何单独一轮看起来都没有错。只有当你把第 1 轮和第 6 轮直接对比时,漂移才会显现。
修复:把规格文件当作唯一的事实来源,每一轮都重新读一遍,而不是让模型依赖它自己对"迄今为止发生了什么"的自我累积总结。这和 maker-checker 切分之所以有效的道理相同——对 SPEC.md 的一次新鲜读取不会漂移,但一段长长的、自我总结的"我们一直在做什么"的记忆绝对会。
静默的成本爆炸
症状:你一周后查看 API 用量,它是你预期的十倍。没有人在盯每一轮的 token 数,而一个卡在震荡中的循环——制作者弄坏了检查者刚刚批准的东西,检查者拒绝了它,制作者又把它"修"回原来的 bug——会像正在取得真实进展的循环一样,轻松地烧掉八轮的 token。
修复:记录每一轮的 token 或成本估算,而不仅仅是每次运行的。按"轮"告警,而不仅仅是按总数——一个单次撞到上限的循环是舍入误差;一个被 cron 触发器每晚启动、却永不收敛的同一个循环,是一笔你直到账单到期才会注意到的月度开支。如果你在按计划跑无人值守的循环,就在轮上限旁边放一个成本上限。两者是同一种护栏:循环无权决定"多少才算够"。
这些失效模式没有一个是稀奇的。它们是任何自动化过程在没有人实时问责它时,都会失败的那些普通方式——一个什么都不告警的监控脚本,一个靠"不做任何可衡量的事"来"工作"的 cron 任务,一个因为是空的所以是绿的测试套件。循环不会引入新的失败种类。它们只是移除了那个过去靠偶然发现旧种类失败的人。
这其实,就是全部的交易。循环还给你那些你过去花在盯梢上的时间。它要求的回报是,你提前、一次性花掉其中一部分时间,确保那个检查工作的东西比那个做工作的东西更仔细,并且在任何东西去判定"完成了"之前,你在纸面上写下了"完成"到底意味着什么。
把那一部分搞对,循环就只是一个工具:无聊、可靠,容易忘记它还在跑。搞错了,你就构建出了一个会自信地、不知疲倦地、悄悄地永远做错事的东西——直到你去检查为止。
提示词
bash
#!/bin/bash
# fix-tests.sh — 一直跑,直到套件通过或者我们试满 10 次
MAX_ITER=10
i=0
mkdir -p logs
while [ $i -lt $MAX_ITER ]; do
echo "=== iteration $i ==="
claude -p "Read TASK.md for context. Run the test suite with 'npm test' \
and read the output. If tests fail, fix the code causing the failure. \
Run the suite again. If all tests pass, write DONE to STATUS.md. \
Do not touch test files themselves." \
--allowedTools "Edit,Read,Bash" \
--output-format json > "logs/iter_$i.json"
if grep -q "DONE" STATUS.md 2>/dev/null; then
echo "done after $i iterations"
break
fi
i=$((i + 1))
done
.claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "./check_done.sh" }
]
}
]
}
}
check_done.sh:
#!/bin/bash
# check_done.sh — Stop 钩子。退出 0 = 让 Claude 停止,退出 2 = 强制它继续工作。
# 自带一个硬性上限,这样它不会永远跑下去。
MAX_ATTEMPTS=10
COUNT_FILE=".loop_count"
# 测试通过 -> 我们做完了。清空计数文件。
if npm test -- --silent > /dev/null
rm -f "$COUNT_FILE"
exit 0
fi
# 仍然失败 -> 记录这一次尝试
count=$(( $(cat "$COUNT_FILE" 2>/dev/null || echo 0) + 1 ))
echo "$count" > "$COUNT_FILE"
if [ "$count" -ge "$MAX_ATTEMPTS" ];
echo "Hit $MAX_ATTEMPTS attempts, the loop can't run forever." >&2
rm -f "$COUNT_FILE"
exit 0 # 优雅放弃,而不是硬失败
fi
echo "Tests still failing (attempt $count). Continuing." >&2
exit 2
你正运行在一个自动化循环里。这个会话可能会针对同一个任务被
多次重新调用。
- 在做任何更改之前,先检查 STATUS.md 里上一个循环迭代留下的
笔记。
- 每次调用只做一处聚焦的更改。不要试图在一次里修完整个代码库。
- 改完代码后,在结束你的回合之前,总是重新运行本任务的验证命令。
- 如果验证失败,在 STATUS.md 里留一条简短笔记,精确描述什么失败了、
你试了什么,好让下一次迭代不再重复你的错误。
- 永远不要编辑测试文件、fixture 或验证脚本本身来让一个检查通过。
如果一个测试看起来是错的,在 STATUS.md 里说出来,而不是改掉它。
文章表格:
触发器 | 适合 | 原语
你,手动 | 一个你想走开的一次性、多步任务 | 一个 shell 循环里的普通 claude -p
文件变化 | 随着规格或 schema 演进,让代码与它保持同步 | 文件监视器(fswatch、inotifywait)调用 claude -p
Cron / 计划 | 夜间清理、依赖检查、报告生成 | 运行无头 Claude 的 cron 条目
Stop 钩子 | "在 X 可验证地为真之前,别让 Claude 退出" | .claude/settings.json
Claude 自己 | Claude 分解成自己的子检查的一个大任务 | 子智能体 + 用 TodoWrite 做步骤追踪
链接
fix-tests.sh
标签:# X # Claude # Automation # AI # Marketing # Guide
相关文章
How to design an AI agent
Who the fuck wants to pay for your platform when $200 already buys Claude Max or Codex?
AI Claude Marketing Automation
原文参考:https://maxed.wiki/posts/you-were-the-loop-here-s-how-to-fix-that-with-claude-code/ (Maxed.wiki,本页为站内中文整理)