Claude Code 第 8 次跑 pnpm test。每次:改同一行 expect(...).toBe(...)、重跑、又挂、回退。或者它在「修 auth bug」的两种解读间反复横跳——修 OAuth 挂 magic-link、修 magic-link 挂 OAuth。Token 烧着、进度条不动。Agent 进了循环。
95% 的 loop 不是「模型不行」——是反馈环坏了:flaky 测试、prompt 没约束、context 污染、工具失败没 flag。修法不是更强模型,是 30 秒内识别 loop 签名、用一句话约束打破。下面 6 种 loop 形态和各自的一句话约束。
常见原因
按命中率从高到低:
1. flaky 测试——过 / 挂 / 过 / 挂
最常见触发。测试用 Date.now()、网络调用、随机、snapshot 时间戳——同代码一次过一次挂。Agent 看到红 → 改 → 绿 → 重跑红 → 回退 → 绿 → 改 → 红,永远。
如何判断:停 agent。你自己不改代码跑 3 次同命令。能复现绿红绿——测试是问题。
2. 模糊 prompt——agent 在两种解读间漂
「修 login bug」——但你有 OAuth 和 magic-link。Agent 修 OAuth,magic-link 测挂;切 magic-link,OAuth 测挂——在互斥的两种解读间反复跳。
如何判断:看最近 5 个 diff——动了两个不同 feature 的两个不同文件、每个新 edit 撤上一个——prompt 没约束住。
3. Plan 错了 agent 拒绝 replan
agent 锁定「改 schema → 改 API → 改 UI」。Step 1 不可能(表有数据,列删不掉)——agent 死磕 step 1 反复重写 migration,而不是 replan。
如何判断:问「你当前 plan 是什么?现在第几步?」——5+ 轮都说”还在 step 1”,强制 replan。
4. context 被旧 error 塞满
长 session 80% context 是过期”上次失败”日志——agent 在拟合不再反映现状的旧 error。
如何判断:开新 session、贴当前代码、重述目标——第一次就成——是 context 污染。
5. Agent 在 mock 和实 implementation 间漂
写 mock 让测试过,下轮觉得 mock 不真实改实 impl,原测试挂,回 mock——loop。
如何判断:跨次 diff 里搜 jest.mock / vi.mock / MagicMock 被加被删。
6. 工具失败 agent 没察觉
Bash exit code 非 0。Agent 把 stdout 当成功,又跑同命令——loop 因为”失败信号”它看不见。
如何判断:看最近 3 个工具调用的 exit code——非 0 但 Claude 没说”命令失败”——它漏了。
最短修复路径
按收益从高到低。Step 1 + 2 在一分钟内打破大多数 loop。
Step 1:停 agent,让它列最近 10 个动作
按 Esc / Stop。然后问:
列最近 10 个动作(文件路径 + edit 总结 + 命令 + exit code)。
不要评论,只表格。
90% 时候你会看到两个文件交替、同一行反复改、或测试反复跑没代码进展——pattern 揭示 loop 签名。
Step 2:按签名套约束句
匹配 loop 类型和约束 prompt:
| Loop 类型 | 约束 prompt |
|---|---|
| 反复改测试期望 | 「不要动测试。测试是对的——改 implementation 匹配它。」 |
| 反复加/删 mock | 「保留真实现,删所有 mock,跑集成测试。」 |
| 反复改 migration | 「schema 冻结,重新 plan 只动 UI 层。」 |
| 来回两个文件 | 「只允许动 src/auth/login.ts,其他全只读。」 |
| 同一 flaky 测试反复挂 | 「停跑这个测试,mock 时间依赖,继续。」 |
| 重跑失败命令 | 「上条命令 exit 非 0。读 error,不要盲重跑。」 |
Step 3:flaky 测试先稳
不要让 agent 在 flaky 信号上 loop——先稳:
# 跑 5 次看稳不稳
for i in {1..5}; do pnpm test -- --testNamePattern="login flow"; done
2+ 次挂就 mock 不确定性源:
// vitest setup
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-01-01'));
然后让 agent 在稳定的测试上继续。
Step 4:开新 session 用全 context
上面都不行——最后杠杆是 context reset:
1. 复制当前文件内容(不是 diff)。
2. 新 session。
3. 重启:
目标:[一句话]
当前代码:[贴整文件]
失败测试/错误:[原样贴]
约束:不动测试,只动 src/X.ts。
先打 plan,等批准再 edit。
新 context 不被旧失败日志污染,通常一次过。
Step 5:设迭代上限
防下个 loop。CLAUDE.md 加:
## Agent 行为约束
- 每任务最多 5 次 build/test 迭代
- 3 次失败后停下汇报
- 同一文件同一块不要 edit 超过 3 次
- 任何工具调用重试 3 次后停下来问
Cursor 用 .cursorrules、Codex 用 AGENTS.md——同理。
Step 6:卡 plan 时强制 replan
agent 永远 step 1:
停。当前 plan 不行。不要继续。
打印:
1. 为什么 step 1 不行
2. 两个替代方案
3. 推荐一个
等我批准再继续。
强制 agent 跳出失败 plan,不是在上面磨。
预防建议
- CLAUDE.md 设迭代上限——loop 有内建出口
- flaky 测试隔离到
flaky.test.ts——agent 永不在不稳信号上迭代 - 同步骤 3 次连失败强制 replan
- 显式完成标准——不要”修 login”而是「
pnpm test -- authexit 0」 - 看 agent 的动作 trace 不只看最终输出——loop 在 trace 里
- loop 出现时开新 session,不要救污染的
相关阅读
标签: #排查 #Claude Code #排查 #Agent 死循环