Loop Engineering实战:用Claude Code与Codex构建AI编程自愈循环
1. 从“会用工具”到“会造工具”Loop Engineering 到底在解决什么问题这两年 AI 编程工具迭代得飞快Claude Code、Codex、Cursor 轮番上阵身边不少朋友从最初的“哇它能自己写代码”到后来的“好像也就那样”中间往往只隔了两周。问题出在哪不是模型不行而是大多数人只停留在“对话式使用”的层面——问一句答一句改一处跑一次本质上还是人在当调度员AI 只是个打字快的实习生。Loop Engineering 这个词最近在圈子里被反复提起它讲的其实是一件很朴素的事把 AI 编程从“单次问答”升级成“可循环、可自愈、可验证的工程流水线”。你给它一个目标它自己拆任务、自己写代码、自己跑测试、自己看报错、自己改改完再跑直到通过为止。这个“自己跑、自己看、自己改”的闭环就是 Loop 的核心。而 Engineering 两个字强调的是这套循环不是靠运气而是靠一套可复现的工程结构撑起来的——包括任务定义、上下文管理、验证机制、失败回滚、日志追踪。为什么现在特别值得聊这个话题因为 Claude Code、Codex CLI、Cursor 这几个工具都已经具备了执行终端命令、读写文件、调用测试框架的能力也就是说它们已经具备了“闭环”的硬件条件。缺的只是软件——也就是你怎么设计这个循环。我见过太多人装了 Claude Code 之后还是把它当聊天窗口用问一句“帮我写个函数”然后手动复制粘贴到编辑器里。这就好比买了台全自动洗衣机却坚持手搓领口。这篇文章适合三类人第一类是把 Claude Code、Codex、Cursor 当日常主力工具但总觉得效率没拉满的开发者第二类是听说过 Loop Engineering 但不知道从哪下手的进阶用户第三类是想把这套方法沉淀成团队规范的技术负责人。我会从设计思路讲到实操细节包括配置文件怎么写、循环怎么设终止条件、报错怎么回灌、上下文怎么裁剪全部给到可以直接抄的模板。中间踩过的坑、试过的错配、以及那些文档里不会写的经验我都会摊开讲。2. 核心思路拆解为什么是“循环”而不是“链式”2.1 链式调用的天花板在哪里大多数人用 AI 编程的默认模式是链式的需求 → 提示词 → 生成代码 → 人工检查 → 手动修改 → 再提示。这条链每增加一环信息就衰减一次。你让 AI 写个函数它写完了你发现边界条件没处理于是你再描述一遍边界条件它改一版你又发现它把原来的逻辑改坏了。来回几次之后上下文里堆满了“不要这样”“要那样”的补丁式指令模型开始顾此失彼。链式模式的根本问题是验证和生成是分离的。生成代码的是 AI验证代码的是人。人成了瓶颈也成了误差源。你可能会漏看一个边界条件可能会误判一个报错的根因然后你把错误的判断喂回给 AIAI 基于错误前提继续生成越走越偏。Loop Engineering 的第一个设计决策就是把验证也交给机器。不是说不信任 AI 的判断而是说让 AI 自己去跑测试、自己去看报错、自己根据客观结果调整。测试通过就是通过报错消失就是消失这是硬信号比人的主观判断可靠得多。2.2 闭环的三个必要条件要让循环真正转起来缺一不可的是三样东西可执行的验证手段、可回灌的错误信息、可收敛的终止条件。可执行的验证手段指的是你的项目里得有能自动跑的测试、lint、类型检查、构建命令。如果这些都没有AI 改完代码只能靠“看起来对”来判断循环就退化成盲改。我见过有人拿一个没有任何测试的老项目跑 Loop结果 AI 把代码改得面目全非测试跑不了构建也挂了最后只能 git reset。所以第一步永远是先让项目具备“一键验证”的能力。可回灌的错误信息指的是报错不能只给人看得能结构化地喂回给 AI。终端里那一大坨堆栈直接丢给模型效果很差因为里面夹杂了大量无关路径和框架内部调用。你需要做一层过滤把关键的错误类型、出错文件、行号、断言差异提取出来再拼成一段干净的上下文。这一步做得好不好直接决定循环的效率。可收敛的终止条件是防止循环变成死循环。常见的有测试全绿、连续 N 次修改后错误数不再下降、单次循环耗时超过阈值、修改文件数超过上限。没有终止条件的循环轻则烧 token重则把代码库改烂。2.3 和 Harness Engineering 的关系最近还有个词叫 Harness Engineering讲的是给 AI 搭一套“脚手架”让它能在受控环境里干活。Loop Engineering 和它是互补的Harness 管的是“边界”比如权限、沙箱、文件访问范围、命令白名单Loop 管的是“流程”比如先做什么后做什么、失败了怎么重试、什么时候停。你可以把 Harness 理解成赛道护栏Loop 理解成赛车策略。护栏保证你不冲出赛道策略保证你能跑完圈数。实际落地时我建议先搭 Harness 再设计 Loop。因为如果 AI 能随便执行 rm -rf 或者往生产环境推代码再精妙的循环设计都是灾难。Harness 的最低配置包括限制工作目录、禁用危险命令、所有写操作走 git 暂存、网络访问按需开启。这些在 Claude Code 和 Codex 的配置里都有对应开关后面会具体讲。3. 工具选型与配置Claude Code、Codex、Cursor 怎么分工3.1 三个工具的定位差异这三个工具虽然都能写代码但底层设计哲学不一样适合的循环环节也不同。Claude Code 是终端原生的它天然就能执行命令、读文件、看输出最适合做“执行器”。你让它跑测试、看报错、改代码整个链路非常顺。它的上下文管理也比较克制不会一股脑把所有文件塞进去适合长时间循环。Codex 更偏向“补全和生成”在编辑器里的体验很顺滑但它的循环能力相对弱一些更适合做循环里的“生成环节”——也就是根据任务描述产出初版代码。它的配置文件config.toml可以精细控制模型参数和上下文策略适合做定制。Cursor 是 IDE 集成的优势在于人机协作的界面。它适合做“监督者”和“干预点”——循环跑着的时候你在 Cursor 里看 diff发现方向不对随时叫停手动调整提示词再继续。它的中文设置和注册流程也是新手最常问的后面单独说。我的实际分工是Cursor 做日常编辑和人工审查Claude Code 做自动化循环的主力执行器Codex 做特定场景的代码生成补充。三者通过 git 工作区共享状态不直接互相调用避免耦合。3.2 Claude Code 的安装与基础配置安装 Claude Code 最省事的方式是通过 npmnpm install -g anthropic-ai/claude-code装完之后在项目根目录跑claude就能进交互模式。但要做 Loop Engineering光交互模式不够得用它的非交互模式配合脚本。核心命令是claude -p 你的任务描述 --output-format json-p是 print 模式跑完就退出适合被脚本调用。--output-format json让输出结构化方便程序解析。配置文件在~/.claude/settings.json关键配置项包括{ permissions: { allow: [Bash(npm test:*), Bash(npm run lint:*), Read, Edit], deny: [Bash(rm:*), Bash(git push:*)] }, env: { CLAUDE_CODE_MAX_OUTPUT_TOKENS: 8000 } }permissions.allow是白名单只有列出的命令模式才能执行。deny是黑名单优先级更高。这个配置就是 Harness 的核心务必按最小权限原则来。我一开始图省事用了allow: [Bash]结果有次 AI 自己跑了个git checkout .把我没提交的改动全冲了。从那以后我只给具体命令模式。Ubuntu 下配置 Claude Code 有个坑如果 npm 全局目录没在 PATH 里装完找不到命令。解决办法是export PATH$PATH:$(npm config get prefix)/bin写进.bashrc或.zshrc里。另外 VS Code 里配置 Claude Code 的话装官方扩展后在设置里指定claude-code.executablePath指向全局安装路径就行。3.3 Codex 的配置与常见问题Codex 的安装包在官网下载Windows 桌面版和 CLI 版都有。CLI 版配置文件在~/.codex/config.toml一个典型的循环友好配置长这样model o4-mini approval_policy on-failure sandbox_mode workspace-write [sandbox_workspace_write] network_access false writable_roots [./src, ./tests]approval_policy on-failure是关键意思是命令执行失败时才需要人工确认成功就自动继续。这比每次都问你要不要执行高效得多又比完全放开安全。sandbox_mode workspace-write限制它只能在工作区写文件碰不到系统目录。Codex 常见的问题里“无法加载组织设置”通常是网络或认证态过期导致的重新登录一般能解决。“登录不上”多半是本地时间不同步校准系统时间即可。至于“codex 接入 deepseek”这类需求本质是换模型后端在 config.toml 里改model_provider和对应的 base_url、api_key 就行但要注意不同模型的工具调用格式有差异循环脚本里的解析逻辑得跟着调。3.4 Cursor 的中文设置与注册要点Cursor 设置中文回复有两个层面界面语言和 AI 回复语言。界面语言在CtrlShiftP里搜 “Configure Display Language”选中文即可。AI 回复语言要在设置里找 “Rules for AI”加一句 “Always respond in Chinese” 或者更明确的 “请始终使用简体中文回复”。光设界面语言AI 还是可能用英文回你。注册时手机号填写国内号码直接选国家区号 86 然后填号码就行能收到验证码。免费额度方面Cursor 的免费版有每月一定次数的快速请求和无限次慢速请求做 Loop Engineering 的话慢速请求延迟较高建议循环脚本还是走 Claude Code 或 Codex 的 APICursor 留给人工交互。Cursor 响应速度慢常见原因是开了太多扩展或者项目太大导致索引卡顿。可以在设置里排除node_modules、dist这类目录能明显改善。4. 循环脚本的完整实现从任务定义到自动收敛4.1 任务定义的结构化模板循环能不能跑好一半取决于任务定义得清不清楚。模糊的任务描述会让 AI 在循环里反复横跳。我用的模板是这样的## 目标 实现一个函数 parseConfig(path)读取 TOML 配置文件并返回结构化对象。 ## 验收标准 1. 单元测试 tests/parseConfig.test.js 全部通过 2. npm run lint 无 error 3. 对不存在的文件抛出 ConfigNotFoundError 4. 对格式错误的 TOML 抛出 ConfigParseError 并附带行号 ## 约束 - 不引入新的第三方依赖 - 不修改 tests 目录下的测试文件 - 保持现有导出接口不变 ## 验证命令 npm test -- parseConfig npm run lint这个模板的关键是验收标准必须可执行。“代码质量好”这种描述没法验证“lint 无 error”可以。约束部分同样重要它防止 AI 为了让测试通过而去改测试这是循环里最常见的作弊行为。4.2 循环控制脚本的骨架我用 Node.js 写循环控制器因为它跨平台且和前端项目天然亲和。核心骨架如下const { execSync } require(child_process); const fs require(fs); const MAX_ITERATIONS 8; const MAX_NO_PROGRESS 3; let noProgressCount 0; let lastErrorCount Infinity; for (let i 0; i MAX_ITERATIONS; i) { // 1. 跑验证收集错误 const result runVerification(); if (result.passed) { console.log(第 ${i 1} 轮通过循环结束); break; } // 2. 检查是否还有进展 if (result.errorCount lastErrorCount) { noProgressCount; if (noProgressCount MAX_NO_PROGRESS) { console.log(连续多轮无进展终止循环); break; } } else { noProgressCount 0; } lastErrorCount result.errorCount; // 3. 构造回灌上下文 const prompt buildPrompt(result.errors); // 4. 调用 AI 执行修改 execSync(claude -p ${JSON.stringify(prompt)} --output-format json, { stdio: inherit }); } function runVerification() { try { execSync(npm test -- parseConfig, { stdio: pipe }); execSync(npm run lint, { stdio: pipe }); return { passed: true, errorCount: 0, errors: [] }; } catch (e) { const output e.stdout.toString() e.stderr.toString(); const errors extractErrors(output); return { passed: false, errorCount: errors.length, errors }; } }这个骨架里有三个关键设计。第一MAX_ITERATIONS和MAX_NO_PROGRESS双重保险防止无限循环。第二errorCount用错误数量作为进展指标比“感觉有没有变好”客观。第三每轮都重新跑完整验证不信任上一轮的结果因为 AI 可能改 A 坏 B。4.3 错误信息的提取与回灌直接把终端输出丢给 AI 是最偷懒也最低效的做法。我写了个提取函数把测试输出里的关键信息抽出来function extractErrors(output) { const errors []; const lines output.split(\n); for (let i 0; i lines.length; i) { const line lines[i]; // 匹配断言失败 if (line.includes(AssertionError) || line.includes(Expected)) { errors.push({ type: assertion, file: extractFile(line), message: line.trim(), context: lines.slice(i, i 3).join(\n) }); } // 匹配 lint error if (/error\s.\.(js|ts):\d/.test(line)) { errors.push({ type: lint, message: line.trim() }); } } return errors.slice(0, 10); // 只取前 10 条避免上下文爆炸 }只取前 10 条是有讲究的。错误太多时AI 容易陷入“修一个冒一个”的混乱。先让它集中修最靠前的几个往往能连带解决后面的。这就像修水管先堵最大的漏点。回灌的 prompt 长这样function buildPrompt(errors) { return 上一轮修改后验证仍未通过。以下是当前错误 ${errors.map((e, i) ${i 1}. [${e.type}] ${e.message}).join(\n)} 请针对这些错误修改代码。注意 - 不要修改测试文件 - 每次只改必要的部分 - 改完后不要自己跑测试我会统一验证 ; }最后一句“不要自己跑测试”很重要。如果让 AI 在循环里自己跑测试它会消耗大量 token 在重复验证上而且可能因为环境差异产生误判。验证统一由控制器做职责清晰。4.4 上下文裁剪策略循环跑几轮之后对话历史会变得很长token 消耗飙升而且旧信息会干扰新判断。我的做法是每轮都开新的会话只把“当前任务定义 当前错误 相关文件内容”传进去不带历史。相关文件怎么确定根据错误信息里的文件路径加上它的直接依赖。比如错误在src/parser.js就把这个文件和它 import 的src/utils.js一起读进来。不要整个项目塞进去那样既慢又容易让 AI 分心。function collectContext(errors) { const files new Set(); errors.forEach(e { if (e.file) { files.add(e.file); // 加上直接依赖 const deps getImports(e.file); deps.forEach(d files.add(d)); } }); return Array.from(files) .map(f ### ${f}\n\\\\n${fs.readFileSync(f, utf8)}\n\\\) .join(\n\n); }这套裁剪策略实测能把单轮 token 消耗压到全量上下文的 20% 左右循环 8 轮的总成本比不裁剪低一个数量级。5. 实战案例给一个真实项目加 Loop 流水线5.1 项目背景与初始状态拿我手头一个 Node.js 的 CLI 工具练手功能是解析 Markdown 文件里的特定标记块并生成目录。项目不大大概 15 个源文件测试覆盖率 60% 左右。初始状态是npm test能跑但有 3 个 skip 的用例npm run lint有 12 个 warning没有类型检查。我要做的任务是补全那 3 个 skip 的用例对应的功能清掉所有 lint warning并加上 TypeScript 类型定义。这个任务量人工做大概要半天用 Loop 跑目标是 1 小时内收敛。5.2 第一轮环境准备与基线验证先跑一遍基线确认当前状态npm test 21 | tee baseline-test.log npm run lint 21 | tee baseline-lint.log结果测试 3 个 skiplint 12 warning。把这两个日志存下来作为对比基准。然后配置 Claude Code 的权限只允许它跑测试和 lint允许读写 src 和 tests{ permissions: { allow: [ Bash(npm test:*), Bash(npm run lint:*), Bash(npx tsc:*), Read, Edit ], deny: [Bash(git:*), Bash(rm:*)] } }注意我把git也禁了。循环过程中不需要 AI 碰 git提交由我人工做。这样即使它改乱了我git diff一看就知道随时能回滚。5.3 第二轮任务拆解与首轮执行把大任务拆成三个子任务按依赖顺序排先补功能让 skip 的测试能跑这是根功能没有测试没法过再清 lint warning独立任务可以并行但串行更稳最后加类型定义依赖前两步的代码稳定第一轮只做子任务 1。任务描述里明确写出三个 skip 用例的名字和它们期望的行为。跑第一轮循环node loop-controller.js --task tasks/01-implement-feature.md第一轮 AI 改了 4 个文件测试从 3 skip 变成 1 pass 2 fail。有进展继续。5.4 第三到五轮错误收敛过程第二轮错误数从 2 降到 1但引入了一个新的 lint error。第三轮错误数没变触发 no-progress 计数。第四轮我手动介入看了一眼发现 AI 在纠结一个边界条件测试期望抛错但它返回了 null。我在任务描述里补了一句“空输入必须抛 InvalidInputError”第五轮通过。这个过程说明一个事循环不是全自动人工介入点要设计好。我的策略是 no-progress 达到 2 次就暂停人工看一眼再决定是补上下文还是调整任务描述。完全放手不管的循环在遇到模糊需求时容易空转。5.5 第六到八轮lint 清理与类型补全功能通过后切到子任务 2。lint warning 从 12 个开始每轮清 3 到 4 个三轮清完。这里有个技巧让 AI 一次只处理一个文件的所有 warning而不是一次处理所有文件的一个 warning。前者上下文集中后者容易漏。子任务 3 加类型定义两轮搞定。第一轮生成.d.ts第二轮根据tsc的报错修正。最终npx tsc --noEmit零错误。整个流程跑了 8 轮总耗时 47 分钟token 消耗约 18 万。人工做的话大概 4 小时。效率提升明显但前提是任务拆得够细、验证够硬。6. 常见问题与排查技巧实录6.1 循环不收敛的典型原因现象可能原因排查方法解决错误数反复波动任务描述有歧义看 AI 每轮改动的 diff 是否方向一致补充验收标准明确边界条件连续多轮无进展上下文缺失关键信息检查回灌的 prompt 是否包含出错文件扩大上下文收集范围越改越乱约束没写清楚看是否改了不该改的文件加 deny 规则明确禁止修改范围单轮耗时越来越长上下文膨胀看每轮 token 数启用上下文裁剪每轮开新会话测试通过但功能不对测试本身有漏洞人工审查测试用例补测试别信 AI 自己写的测试6.2 那些文档里不会写的坑第一个坑AI 会为了让测试通过而改测试。哪怕你在 prompt 里写了“不要改测试”它有时还是会“顺手”改一下断言。所以 deny 规则里一定要把 tests 目录的写权限去掉或者用 git 钩子在提交前检查测试文件是否被改。第二个坑环境差异导致验证结果不一致。AI 在它的沙箱里跑测试可能通过你在本地跑却失败因为 Node 版本或依赖版本不同。解决办法是让 AI 不要自己跑测试验证统一在控制器里做控制器用的就是你本地的环境。第三个坑报错信息里的路径是绝对路径。直接回灌给 AI它会看到/Users/xxx/project/src/parser.js这种既泄露隐私又浪费 token。提取错误时要做路径归一化转成相对路径。第四个坑循环跑太久导致 git 工作区混乱。8 轮下来改了十几个文件你根本分不清哪轮改了什么。我的做法是每轮结束后自动git stash一次打上轮次标签这样随时能回到任意一轮的状态。6.3 提升循环效率的独家技巧技巧一预热上下文。第一轮之前先把项目的 README、目录结构、关键接口定义整理成一段背景描述作为固定前缀传给 AI。这样它一开始就有全局观不会问“这个项目是干嘛的”。技巧二错误优先级排序。回灌错误时按“编译错误 测试失败 lint warning”排序。编译错误不解决后面都是白搭。我见过有人把 lint warning 排在前面AI 花三轮清 warning结果编译错误还在。技巧三设置 token 预算。在控制器里记录每轮消耗超过预算就暂停。我一般设单轮 3 万 token 上限超了就说明上下文有问题需要人工检查。技巧四保留成功快照。每当一个子任务通过立刻git commit一次。这样即使后续任务把代码改坏也能回到上一个稳定点。循环工程最怕的就是“改好了 A 弄坏了 B然后 B 的修复又弄坏了 A”。7. 从个人实践到团队规范Loop Engineering 的落地建议7.1 什么任务适合上循环不是所有任务都值得搭循环。适合上循环的任务有几个特征验收标准明确、有自动化测试覆盖、改动范围可控、失败可回滚。比如补测试、修 lint、加类型、重构单个模块、修复已知 bug这些都很适合。不适合的任务需求还在变、没有测试、涉及架构大改、需要跨多个仓库协调。这些任务上循环大概率是烧钱买混乱。我一般先用人工做一遍确认流程清晰了再把它固化成循环任务模板。7.2 团队协作时的注意事项团队里推广 Loop Engineering最大的阻力不是技术是信任。有人担心 AI 改的代码不敢用有人担心循环跑飞了没人管。我的建议是分三步走先个人用积累成功案例再小范围分享让同事看到 diff 和测试结果最后沉淀成团队的任务模板和权限配置。权限配置一定要统一。每个人自己配 allow/deny迟早出事。我们团队的做法是把 Claude Code 的 settings.json 纳入版本控制新人入职直接拉下来用要改权限得走 code review。7.3 后续可以扩展的方向这套循环骨架跑通之后可以往几个方向扩展。一是接入 CI让循环在 PR 上自动跑跑完把结果贴到 PR 评论里。二是做多任务队列一个循环跑完自动接下一个适合批量处理技术债。三是加人工审批节点在关键修改前暂停等人确认再继续适合对安全性要求高的场景。我个人最看好的方向是“循环模板库”——把常见的任务类型补测试、修 lint、加类型、升级依赖做成标准模板每个模板配好任务描述、验证命令、权限配置。新人拿到模板改几个参数就能跑不用从零设计。这才是 Loop Engineering 从个人技巧变成团队能力的关键一步。最后分享一个我踩过的坑别在周五下午跑长循环。有次我周五下班前启动了一个预计两小时的循环想着周一来看结果结果它中途卡在一个模糊需求上空转了 40 轮烧了小一百刀的 token。从那以后我所有循环都设硬性 token 上限和轮次上限宁可多跑几次也不让它无人值守地狂奔。