Loop Engineering 实战:用 opencode-loop 搭一个能自我修正的 AI 开发循环
我用 opencode-loop 跑了一次完整的 Loop Engineering 实践,让 AI 自己写代码、自己 Review、自己跑测试。大约十几分钟后,测试从 8 个变成 23 个,全部通过。这不是魔法,关键在 verifier 和 checkpoint。
一、手动用 AI 写代码,人到哪都是瓶颈
过去两年,用 AI 写代码的基本流程是:
你写 prompt → AI 回复 → 你读代码 → 再写 prompt → AI 再回复 → ...
人是方向盘,每一轮都要在场。问题是,这个模式有几个死结:
- 写完就忘。Agent 可能一次改十几个文件,你不可能每一行都仔细看,bug 就藏在没看的那几行里。
- 测试后置。经常是代码写完了才想起来跑测试,失败了一堆再回头找,效率低。
- Review 走形式。自己 Review AI 写的代码,看多了会疲劳,边界条件、类型安全、可访问性这些很容易漏。
本质矛盾是:人成了瓶颈,但完全放手又不敢。
2026 年 6 月,Google 工程师 Addy Osmani 写了一篇 Loop Engineering,把这个问题的解法说得很直接:
Loop engineering is replacing yourself as the person who prompts the agent. You design the system that does it instead.
Peter Steinberger 也说过类似的话:
You shouldn't be prompting coding agents anymore. You should be designing loops that prompt your agents.
翻译成大白话:别再一遍一遍给 AI 写 prompt 了,去设计一个能自动循环的系统。
二、Loop Engineering 的最小控制环
Loop Engineering 不是某个具体工具,而是一种设计方法。Addy Osmani 在原文里讲的是 Automations、Worktrees、Skills、Plugins/Connectors、Sub-agents 这些能力层组件。落到一个最小可运行的控制环,我更喜欢 Codersarts 的拆法:
- Goal:清晰、可衡量的完成标准。
- Prompter:生成下一轮指令。
- Agent:执行指令、产出代码。
- Reader:读取代码 diff、测试输出、日志和进度文件。
- Verifier:独立验证结果是否满足 goal,不能信 Agent 自评。
- Controller:决定停止、重试还是升级给人。
还有一个贯穿整个循环的要素,Addy 叫它 State/Memory:循环必须有一个对话上下文之外的记忆文件,记住"完成了什么、下一步做什么",因为模型每次都会遗忘。CLAUDE.md、AGENTS.md、progress.md 都可以承担这类记忆职责,只是粒度不同。
Codersarts 在 Loop Engineering Explained 里把这个控制环画成了一个最小骨架:
for iteration in range(1, MAX_ITERATIONS + 1):
passed, output = run_tests() # Verifier
if passed: # Controller
return True
prompt_agent(output) # Prompter → Agent
return False # 达到上限,升级给人
注意两个关键点:
- Verifier 必须独立。Agent 自己说"我修好了"不算数,要跑真实的测试。
- 必须设上限。没有
MAX_ITERATIONS或预算上限,循环会无限烧钱。
三、我的实现:opencode-loop + Editor/Reviewer 双 Agent
我选择用 opencode-loop 作为 loop 调度器,OpenCode 作为 Agent 运行环境,搭了一个 Next.js Todo Web Demo。
完整项目已经开源在 Gitee: https://gitee.com/chenjim/opencode-loop-engine
3.1 Agent 设计
在 .opencode/agents/ 下定义了两个 Agent:
- editor:DeepSeek V4 Pro,负责实现代码。
- reviewer:Kimi K2.7-code,只读审查,不能改文件、不能跑命令。
opencode.json 里的核心配置:
{
"agent": {
"editor": {
"mode": "primary",
"model": "opencode-go/deepseek-v4-pro",
"prompt": "{file:.opencode/agents/editor.md}"
},
"reviewer": {
"mode": "subagent",
"model": "opencode-go/kimi-k2.7-code",
"permission": { "edit": "deny", "bash": "deny" },
"prompt": "{file:.opencode/agents/reviewer.md}"
}
}
}
Reviewer 的 prompt 强制要求它输出结构化结果:
## Review Result: PASS / NEEDS_FIX
### Issues
- ...
### Suggestions
- ...
3.2 Loop 命令
启动循环的 /loop-goal 命令:
/loop-goal \
--check "npm test" \
--check "npm run lint" \
--check "npx tsc --noEmit" \
--complete-when-checks-pass \
--max-failures 3 \
--max-runtime 2h \
--checkpoint-only \
--safe \
--progress-file progress.md \
--prompt-file loop-prompt.md \
"完成 Next.js Todo Web Demo 的所有 TODO。每次修改后调用 @reviewer 审查,按反馈修复,直到所有检查通过。"
几个关键参数的作用:
--check:Verifier,每轮后自动跑测试、lint、类型检查。--complete-when-checks-pass:Controller,所有检查通过就停止。--max-failures 3和--max-runtime 2h:安全上限,防止无限循环。--checkpoint-only:只保存 diff 快照,不自动 git commit。--safe:阻挡危险命令。--progress-file progress.md:外部状态文件,Agent 每次读取并更新 TODO。
整个流程可以用下面这张图概括:
3.3 为什么要有 CLAUDE.md
Loop 每次运行都是一次新的对话,模型不会记住上一轮的项目约定。如果不在仓库里写清楚,Agent 每轮都会重新猜测:
- 这个 demo 用不用数据库?
- 样式是 Tailwind 还是 inline style?
- 哪些命令是 verifier?
- 哪些命令绝对不能跑?
我在项目根目录放了 CLAUDE.md,把项目定位、技术栈、关键文件、验证命令、Agent 约定、已知问题都写进去。它和 AGENTS.md 一样,本质上都是仓库级记忆文件,让 loop 不用每次都从零推导项目上下文。
Addy Osmani 把这类文件叫 State/Memory,Codersarts 会把它们作为 Reader 读取的输入。名字不同,作用一样:把意图写在对话上下文之外,落到仓库文件里,而不是让 Agent 每次自己猜。
四、实战过程:从 8 个测试到 23 个测试
项目初始骨架已经搭好:
package.json和测试配置已经提交src/和tests/有基础实现,共 8 个测试progress.md里列了 6 个 TODO,全部未勾选
运行 /loop-goal 后,Agent 的行为如下:
- 探索代码库,读取
progress.md和关键文件。 - 运行初始检查:
npm test、npm run lint、npx tsc --noEmit,8 个测试全过。 - 调用 Reviewer 对项目结构、类型、API 路由、前端页面、测试分别审查。
- 按 Review 反馈修复。
- 扩展测试覆盖,并重新跑检查。
- 再次 Review,处理剩余问题。
- 更新
progress.md,标记所有 TODO 完成。
整个循环跑了 大约十几分钟,最终结果:
Test Files 2 passed (2)
Tests 23 passed (23)
测试从 8 个增加到 23 个,全部通过。
五、Reviewer 到底发挥了什么作用
这次实践里,Reviewer 不是摆设。它确实找出了代码里的真实问题:
| 问题 | 位置 | 严重程度 |
|---|---|---|
toggleTodo 只能标记完成,不能取消 |
src/app/page.tsx |
高 |
| API 路由 catch-all 返回 400,不区分服务器错误 | src/app/api/todos/route.ts |
高 |
| PATCH body 没有运行时校验 | src/app/api/todos/[id]/route.ts |
高 |
| 页面缺少 loading/error 状态 | src/app/page.tsx |
中 |
| input 缺少 label,按钮缺少 aria-label | src/app/page.tsx |
中 |
| 测试覆盖不足,缺少交互测试 | tests/page.test.tsx |
中 |
修复后,代码质量比初始版本高了一档。
但 Reviewer 也不是万能。它没发现把 eslint-config-next 升到 16.2.9 后,在当前 FlatCompat 配置下会触发兼容问题。这个坑是在实际跑 lint 时踩到的。这说明:Reviewer + 自动化测试 = 两层验证,比单层更可靠,但都不是 100%。
六、踩坑记录
6.1 eslint-config-next 版本不兼容
Reviewer 建议把 eslint-config-next 从 15.1.7 升级到 16.2.9,与 next 版本对齐。但升级后,FlatCompat 处理新配置时直接崩溃:
Converting circular structure to JSON
Agent 回退到 15.1.7 后 lint 才恢复正常。这个兼容性问题被写进了 progress.md 的 Known Issues 里。
6.2 opencode-loop 是小众社区插件
ByBrawe/opencode-loop 目前还是小众社区插件,没有官方背书,文档主要靠 README。但它完整实现了 Loop Engineering 需要的核心能力:调度、验证、checkpoint、安全限制。
6.3 模型选择有讲究
- Editor 用 deepseek-v4-pro:推理和实现能力强,适合写代码。
- Reviewer 用 kimi-k2.7-code:代码审查细致,适合找问题。
如果反过来,或者两个用同一个模型,效果可能会差一些。至少在这次实践里,"实现模型"和"审查模型"拆开后,Reviewer 更容易站在旁观者视角挑问题。
6.4 verifier 全绿后,人工核验又找出 bug
循环跑完、npm test、npm run lint、npx tsc --noEmit 全部通过,progress.md 也全勾选。我本以为可以收工,结果手动拉起来看了一遍页面和 diff,又发现几个问题:
- input label 用了
display: 'none':屏幕阅读器也会忽略这个 label,等于 input 还是没有可访问名称。 - 加载态把整页替换成
Loading...:骨架结构都没有,首屏体验差。 tsconfig.json的jsx还是preserve:Next.js 16 新编译器下应该配成react-jsx。- mutation 没有防重入:快速点两次 Delete 会触发并发请求,第二个会 404。
这些问题都没被自动化 verifier 拦住。测试覆盖的是功能正确性,lint 覆盖的是代码风格,tsc 覆盖的是类型,但可访问性、交互细节、框架版本适配仍然需要人仔细看。
七、Loop Engineering 的边界
这次实践让我确认了几件事:
第一,Loop 不是全自动魔法。
人仍然要定义目标、设计 Agent prompt、配置 verifier、审阅最终的 diff。Loop 只是把"重复prompt"这部分自动化了。
第二,Verifier 是底线,但不是上限。
如果循环只问 Agent"你完成了吗",它会自信地说完成了,然后留下一堆 bug。独立的测试、lint、类型检查是 loop 能信任的底线,能拦住功能错误和类型错误。
但就像 6.4 里写的那样,verifier 全绿之后,人工复核仍然可能发现可访问性、交互细节、框架版本适配这类问题。Loop 能替你跑重复验证,不能替你负责最终质量。
第三,理解债务会加速积累。
Loop 跑得越快,代码库增长越快,你越可能不理解自己仓库里的代码。Addy Osmani 管这叫 comprehension debt。解决方案没有捷径:定期读 Loop 产出的代码。
第四,成本必须设上限。
--max-runtime、--max-failures、token 预算,这些不是可选项。一个无人看管的 loop,可以在几小时内烧掉很多钱。
适合用 Loop 的场景:重复性改造、测试驱动重构、长时编码任务、需要多轮修复的 bug。
不适合的场景:架构设计、需要深度业务判断的代码、需要创造性 UI/UX 的工作。
Loop Engineering 把 AI 编程从"人拿着方向盘"推进到了"人设计导航系统,车自己跑"。但车最后还是你的,路也要你选。
这次用一个 Todo Demo 验证了这条路是通的。下一步,我会把它用到一个更真实的项目里,看看在复杂代码库上,Reviewer 还能不能保持同样的命中率。
如果你也想试,建议从一个小功能开始,先把 verifier 写好,再让 loop 跑起来。
本文链接:Loop Engineering 实战:用 opencode-loop 搭一个能自我修正的 AI 开发循环 - https://h89.cn/archives/634.html
版权声明:原创文章 遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接和本声明。