1. 从“写提示词”到“搭循环”:Loop Engineering 到底在解决什么问题
如果你最近半年一直在用 Claude Code、Codex、Cursor 这类 AI 编程工具,大概率经历过这样一个阶段:一开始觉得“哇,一句话就能生成一个函数”,用着用着发现不对劲——同一个 bug 反复改反复出现,模型改完 A 文件又把 B 文件搞崩了,上下文一长就开始胡言乱语,最后你不得不手动把每一步都拆开、盯着它改、改完再手动验证。这个过程用久了,人反而变成了 AI 的“人肉调度器”。
Loop Engineering(循环工程)要解决的,就是这个“人肉调度”的问题。它不是某一个具体工具的功能,而是一套围绕 AI 编程助手构建可重复、可验证、可收敛的自动化循环的方法论。核心思路是:把“让 AI 改代码”这件事,从一次性的对话,变成一个有明确输入、明确验证条件、明确退出机制的循环结构。每一轮循环里,AI 负责生成或修改,你预设的验证机制负责判断“这轮改动到底行不行”,不行就带着失败信息进入下一轮,直到满足退出条件。
这套东西为什么现在火起来?因为 Claude Code、Codex CLI、Cursor 这些工具已经具备了读写文件、执行命令、查看报错的能力,也就是说它们天然适合被放进一个循环里。以前你只能让模型“说”代码,现在它能真的去跑测试、看日志、改文件。Loop Engineering 就是把这几个能力串起来,让 AI 自己迭代,而不是你一句一句喂。
这篇文章适合三类人看:第一类是用过 Claude Code 或 Codex 但总觉得“不够顺手”的开发者;第二类是刚接触 AI 编程工具、想直接学一套靠谱工作流的新手;第三类是团队里想把 AI 编程引入日常开发、但担心“AI 改坏了没人管”的技术负责人。我会从整体设计思路讲到具体实操,包括 Claude Code 的循环配置、Codex 的自动化脚本、Cursor 的规则约束,以及怎么把三者串成一条流水线。全程按我实际踩过的坑来讲,不整虚的。
2. 循环工程的整体设计与核心思路拆解
2.1 为什么“单次对话”模式必然失败
先说清楚一个底层问题:为什么你直接跟 Claude Code 说“帮我修这个 bug”,它经常修不好?不是模型不行,是单次对话模式缺少反馈闭环。模型看到的是你贴的报错和它自己读到的代码,它改完之后,没有人告诉它“你改的这版跑起来还是报错,报错信息变成了 XXX”。它只能凭“看起来对”来判断,而“看起来对”和“真的对”之间差了十万八千里。
我做过一个统计,在一个中等规模的 TypeScript 项目里,让 Claude Code 单次修复一个类型错误,首次修复成功率大概在 40% 左右。但如果加上“改完自动跑 tsc,把新的报错喂回去再改一轮”,三轮之内成功率能到 85% 以上。这个差距就是循环的价值。循环工程的第一性原理很简单:AI 的自我判断不可靠,外部验证才可靠。所以整个方法论的核心不是“怎么让 AI 更聪明”,而是“怎么设计一个让 AI 能自己发现自己错了的机制”。
2.2 一个标准循环的四个组成部分
我把一个完整的 Loop 拆成四块:任务定义、执行器、验证器、退出条件。任务定义是你告诉 AI“要做什么”,必须具体到可验证,比如“让npm test全部通过”而不是“优化一下代码”。执行器就是 Claude Code 或 Codex 这类能动手的工具。验证器是你预设的检查手段,可以是测试命令、类型检查、lint、甚至一个自定义脚本。退出条件是“什么时候停”,可以是“测试全绿”或者“循环超过 5 轮就停,交给人看”。
这四块里,验证器是最容易被忽略但最重要的。很多人搭循环只搭了“执行器 + 退出条件”,结果 AI 改了半天,你也不知道改得对不对,最后还得自己看。验证器的作用是给每一轮循环一个客观的“通过/不通过”信号,这个信号直接决定下一轮循环的输入。没有验证器的循环,本质上还是单次对话,只是重复了几遍而已。
2.3 工具选型:Claude Code、Codex、Cursor 各自适合放在循环的哪个位置
这三个工具不是互斥的,它们在循环里扮演的角色不一样。我自己的用法是这样的:
| 工具 | 在循环中的角色 | 适合场景 | 关键能力 |
|---|---|---|---|
| Claude Code | 主力执行器 | 复杂重构、多文件修改 | 读写文件、执行命令、长上下文 |
| Codex CLI | 轻量执行器 / 批处理 | 单文件修复、脚本化任务 | 命令行调用、可脚本化 |
| Cursor | 交互式编辑器 + 规则约束 | 人工介入环节、规则校验 | 实时编辑、.cursorrules约束 |
Claude Code 适合当主力,因为它能在一个会话里连续执行多个命令、读多个文件,上下文管理也相对成熟。Codex CLI 适合被脚本调用,比如你写一个 bash 脚本,循环里每次调codex exec跑一个任务。Cursor 则适合放在“人工审核”这一环,当循环退出后,你在 Cursor 里用它的规则文件做最后一道检查。三者串起来就是:Codex 跑批量循环,Claude Code 处理复杂轮次,Cursor 做人工兜底。
2.4 循环的收敛性设计:怎么保证它不会无限跑下去
这是实操里最容易被坑的地方。我最早搭的一个循环,让 Claude Code 修 lint 错误,结果它改了一个文件引入了新错误,下一轮又改回去,来回震荡了十几轮。后来我加了两个约束:最大轮次限制和改动范围限制。最大轮次就是硬性规定“最多跑 N 轮”,到了就停。改动范围限制是告诉 AI“这一轮只允许改src/utils/下的文件”,防止它到处乱改。
收敛性设计的核心是让每一轮的改动空间递减。第一轮可以让它自由改,如果没通过,第二轮就缩小范围到报错涉及的文件,第三轮再缩小到具体函数。这样即使 AI 判断力有限,也不会在同一个地方反复横跳。另外,每一轮都要把“上一轮改了什么、验证结果是什么”作为输入传下去,让 AI 知道自己上一轮干了什么,避免重复劳动。
3. 核心细节解析与实操要点
3.1 Claude Code 的循环配置:从安装到跑通第一个 Loop
先把 Claude Code 装好。国内用户如果遇到网络问题,按官方文档配置好环境变量就行,这里不展开。装完之后,核心是理解它的两种运行模式:交互模式和非交互模式。交互模式就是你平时用的那种,你一句它一句。非交互模式是claude -p "你的任务"这种,执行完就退出,适合放进脚本循环。
跑第一个 Loop 我建议从最简单的场景开始:让 Claude Code 修复一个已知的测试失败。步骤是这样的:
- 先手动跑一遍
npm test,确认有一个测试失败,记下失败信息。 - 写一个 shell 脚本,循环调用 Claude Code:
#!/bin/bash MAX_ROUNDS=5 for i in $(seq 1 $MAX_ROUNDS); do echo "=== Round $i ===" # 跑测试,捕获输出 TEST_OUTPUT=$(npm test 2>&1) if echo "$TEST_OUTPUT" | grep -q "All tests passed"; then echo "Tests passed, exiting loop." exit 0 fi # 把失败信息喂给 Claude Code claude -p "以下是测试失败信息,请修复代码。只修改必要的文件,不要重构无关代码。失败信息:$TEST_OUTPUT" done echo "Max rounds reached, manual intervention needed." exit 1这个脚本的逻辑很直白:每轮先跑测试,通过了就退出,没通过就把失败信息喂给 Claude Code 让它改,改完进入下一轮。MAX_ROUNDS=5是硬性上限,防止无限循环。
注意:
claude -p默认不会自动执行命令,它只是生成修改建议。如果你希望它直接改文件,需要加上对应的权限参数,具体参数名以你安装的版本为准,建议先在小项目上试。
3.2 验证器的设计:测试、类型检查、lint 三层过滤
验证器不能只有一个。我一般用三层:第一层 lint,快速过滤格式和明显错误;第二层类型检查,比如tsc --noEmit,抓类型问题;第三层单元测试,抓逻辑错误。三层按顺序跑,任何一层失败就停在那层,把该层的输出喂回给 AI。
为什么按这个顺序?因为 lint 最快,几秒钟就能跑完,能在早期拦掉大量低级错误,避免浪费时间跑完整的测试套件。类型检查比 lint 慢但比测试快,能抓到 lint 抓不到的接口不匹配问题。测试最慢但最准,放在最后。这个顺序能让循环的每一轮尽量快,轮次多了也不会太耗时。
验证器的输出格式也很关键。不要直接把原始日志喂给 AI,太长了。我一般用grep或sed提取关键行,比如只保留报错的文件名、行号、错误信息,去掉堆栈里的无关帧。这样 AI 拿到的信息密度高,改起来更准。
3.3 Codex 的脚本化调用:怎么把 Codex 变成循环里的一个函数
Codex CLI 的优势是它可以被当成一个命令行工具调用,输入输出都是文本,非常适合脚本化。我常用的模式是:
codex exec "修复 src/parser.ts 里的类型错误,只改这个文件" > /tmp/codex_output.txt然后把/tmp/codex_output.txt里的内容作为下一轮的输入。Codex 的配置文件一般在~/.codex/config或者项目根目录的.codex文件里,可以配置默认模型、超时时间、是否自动应用修改等。国内用户如果遇到登录问题,检查一下配置文件里的 endpoint 设置,确保指向可用的服务地址。
Codex 在循环里的定位是“轻量执行器”。它比 Claude Code 快,适合处理单文件、单函数的修复。我一般把循环分成两段:先用 Codex 跑几轮快速修复,如果还没通过,再切到 Claude Code 处理复杂情况。这样能省不少时间。
3.4 Cursor 的规则约束:用.cursorrules给循环加护栏
Cursor 在循环里的作用是“规则约束”和“人工兜底”。.cursorrules文件可以定义项目的编码规范、禁止修改的文件、必须遵守的接口约定等。当循环退出后,你在 Cursor 里打开改动过的文件,它会根据规则文件给出提示,帮你快速判断 AI 的改动是否合规。
我一般会在.cursorrules里写这几类规则:禁止修改的文件列表(比如配置文件、迁移脚本)、必须通过的检查命令(比如提交前必须跑npm run lint)、代码风格约定(比如函数命名、注释语言)。这些规则在循环运行时不会自动生效,但会在人工审核环节帮你省很多事。
提示:
.cursorrules的内容要具体,不要写“保持代码整洁”这种模糊的话,要写“所有导出函数必须有 JSDoc 注释”这种可检查的规则。
4. 实操过程与核心环节实现
4.1 环境准备:把三个工具装好并验证
先把三个工具都装好。Claude Code 的安装按官方文档走,装完跑claude --version确认。Codex CLI 装完跑codex --version。Cursor 是 GUI 工具,装完打开一个项目,确认能正常编辑和运行命令。
环境变量方面,我建议把三个工具的配置分开管理,不要混在一起。Claude Code 的配置一般在~/.claude/下,Codex 在~/.codex/下,Cursor 在项目根目录的.cursor/下。分开管理的好处是出问题时容易定位,不会互相干扰。
验证环境是否就绪,跑一个最小测试:在一个空项目里创建一个故意有语法错误的文件,然后分别用三个工具尝试修复,看哪个能跑通。这一步能帮你提前发现配置问题,避免在正式循环里踩坑。
4.2 搭建第一个完整循环:从失败测试到全绿
我拿一个真实的例子来讲。假设你有一个 Node.js 项目,npm test有一个测试失败,报错是TypeError: Cannot read property 'id' of undefined。目标是让测试全绿。
第一步,先手动确认失败信息,把完整的报错复制下来。第二步,写循环脚本,我用的版本是这样的:
#!/bin/bash MAX_ROUNDS=6 PROJECT_DIR="/path/to/your/project" cd $PROJECT_DIR for i in $(seq 1 $MAX_ROUNDS); do echo "=== Round $i ===" # 第一层:lint LINT_OUTPUT=$(npm run lint 2>&1) if [ $? -ne 0 ]; then echo "Lint failed, feeding back..." claude -p "Lint 失败,请修复。只改报错涉及的文件。报错:$LINT_OUTPUT" continue fi # 第二层:类型检查 TSC_OUTPUT=$(npx tsc --noEmit 2>&1) if [ $? -ne 0 ]; then echo "Type check failed, feeding back..." claude -p "类型检查失败,请修复。报错:$TSC_OUTPUT" continue fi # 第三层:测试 TEST_OUTPUT=$(npm test 2>&1) if echo "$TEST_OUTPUT" | grep -q "All tests passed"; then echo "All checks passed at round $i" exit 0 fi echo "Tests failed, feeding back..." claude -p "测试失败,请修复。只改必要的文件。失败信息:$TEST_OUTPUT" done echo "Max rounds reached. Manual review needed." exit 1这个脚本跑起来后,你会看到它一轮一轮地跑,每轮输出当前状态。我实测下来,大部分情况下 2 到 3 轮就能全绿。如果超过 4 轮还没通过,通常说明问题不是简单的代码错误,而是设计层面的问题,这时候就该人工介入了。
4.3 参数计算:怎么定最大轮次和超时时间
最大轮次不是拍脑袋定的。我的经验公式是:最大轮次 = 预期修复复杂度 × 2。预期修复复杂度按“涉及文件数”算,单文件问题算 1,跨 2-3 个文件算 2,跨模块算 3。所以单文件问题最多跑 2 轮,跨模块问题最多跑 6 轮。超过这个数还没通过,基本可以判定是 AI 理解不了的问题,继续跑也是浪费。
超时时间按“单轮平均耗时 × 最大轮次 × 1.5”算。单轮平均耗时包括 AI 生成时间 + 验证命令执行时间。比如 AI 生成平均 30 秒,验证平均 20 秒,单轮 50 秒,最大 6 轮就是 300 秒,乘以 1.5 就是 450 秒。给整个循环设一个 450 秒的超时,超了就杀掉,避免卡死。
4.4 实操现场记录:一次真实的循环过程
我拿上周修的一个 bug 来复盘。项目是一个 TypeScript 的 CLI 工具,npm test有一个测试失败,报错是解析器在处理空输入时崩溃。我跑了上面的循环脚本,记录如下:
第一轮:lint 通过,类型检查通过,测试失败。Claude Code 拿到报错后,改了src/parser.ts里的一个空值判断。第二轮:lint 通过,类型检查通过,测试还是失败,但报错变了,变成了“解析结果不符合预期”。Claude Code 又改了一轮,这次改了src/parser.ts和src/types.ts。第三轮:全绿。总共耗时约 4 分钟,三轮。
这个案例里,第一轮 AI 只改了表面问题,第二轮才找到根因。如果没有循环,我可能第一轮改完就以为好了,结果测试还是挂。循环的价值就在于它逼着 AI 面对“改了还是错”这个事实,继续往下挖。
5. 常见问题与排查技巧实录
5.1 循环跑不起来:环境与配置类问题
最常见的问题是工具本身跑不起来。Claude Code 如果报“找不到命令”,检查 PATH 和安装路径。Codex 如果登录不上,检查配置文件里的 endpoint 和认证信息,国内用户特别注意网络环境配置。Cursor 如果中文设置不生效,检查设置里的语言选项,或者直接在.cursorrules里指定注释语言。
还有一个坑是权限问题。Claude Code 默认不会自动执行命令,需要显式授权。如果你在脚本里调用它,要确保授权参数配置正确,否则它只会生成建议不会真的改文件。我建议先在交互模式里手动跑一次,确认它能正常读写文件,再放进脚本。
5.2 循环震荡:AI 反复改同一个地方
这是最烦人的问题。表现是:AI 改了一处,下一轮又改回去,来回好几轮。根因通常是验证器的反馈信息不够具体。如果只告诉 AI“测试失败”,它不知道失败在哪,只能瞎猜。解决办法是把验证器的输出细化,精确到文件、行号、错误类型。比如不要给“测试失败”,要给“src/parser.ts:42期望null但得到undefined”。
另一个办法是加改动历史。每一轮把“上一轮改了什么”作为输入传给 AI,让它知道自己已经试过什么。我一般会在脚本里维护一个changes.log,每轮追加改动摘要,下一轮开始时读这个文件。
5.3 循环不收敛:跑了十几轮还没通过
如果超过最大轮次还没通过,说明问题超出了 AI 的能力范围。这时候不要硬跑,停下来人工分析。我一般会做三件事:第一,看 AI 改过的文件,找出它反复改的地方,那里通常是问题核心;第二,看验证器的输出,确认是不是验证器本身有问题(比如测试写错了);第三,把问题拆小,只让 AI 处理其中一个子问题。
注意:不要因为“再跑一轮可能就好了”就无限增加轮次。我踩过这个坑,跑了 20 轮,最后发现是测试本身写错了,AI 怎么改都不可能通过。
5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决技巧 |
|---|---|---|---|
| 工具命令找不到 | PATH 未配置 | which claude/which codex | 检查安装路径,重装 |
| 登录失败 | 认证配置错误 | 查看配置文件 | 重新配置认证信息 |
| 循环震荡 | 反馈信息不具体 | 检查验证器输出 | 细化到文件行号 |
| 不收敛 | 问题超出 AI 能力 | 看改动历史 | 拆小问题,人工介入 |
| 超时 | 单轮耗时过长 | 计时每轮 | 减少验证范围,加超时 |
| 改动范围失控 | 未限制文件 | 看 git diff | 加改动范围约束 |
5.5 独家避坑技巧:我踩过的三个坑
第一个坑是验证器太慢。我一开始把完整的 e2e 测试放进循环,每轮跑 5 分钟,跑三轮就 15 分钟,效率极低。后来改成先跑单元测试,单元测试过了再跑 e2e,快了很多。
第二个坑是AI 改配置文件。有一次循环里 AI 把package.json的依赖版本改了,导致整个项目跑不起来。后来我在.cursorrules和循环脚本里都加了“禁止修改配置文件”的约束。
第三个坑是忘记提交。循环跑之前一定要先git commit,这样如果 AI 改坏了,可以随时回滚。我吃过这个亏,循环跑完发现改动一塌糊涂,又没有备份,只能手动恢复。
6. 把循环工程用进日常:我的实际工作流
我现在的工作流是这样的:每天早上先跑一遍npm test,如果有失败,直接启动循环脚本,让它自己跑。我趁这个时间去处理别的事,跑完了看结果。如果循环退出时是全绿,我就 review 一下改动,没问题就提交。如果循环退出时是“达到最大轮次”,我就人工介入,看 AI 卡在哪,手动修。
这个工作流用下来,我每天花在“修小 bug”上的时间少了大概 60%。以前一个类型错误可能要手动改十几分钟,现在循环跑两轮就搞定了。省下来的时间可以去做设计、写文档、或者处理更复杂的问题。
对于团队使用,我建议先在小范围试点,选一两个开发者用这套流程,跑一两周看看效果。重点观察两个指标:循环通过率(多少比例的循环能在最大轮次内通过)和人工介入率(多少比例需要人工兜底)。如果通过率低于 60%,说明验证器或任务定义有问题,需要调整。如果人工介入率高于 40%,说明 AI 处理的任务太复杂,需要拆小。
最后分享一个小技巧:把常用的循环脚本做成模板,放在项目根目录的scripts/下,每个项目复制一份改改路径就能用。我现在的模板里已经预置了 lint、类型检查、测试三层验证,以及最大轮次、超时、改动范围约束,开箱即用。这个模板帮我省了很多重复配置的时间,也避免了每次重新踩坑。