本教程聚焦一个被反复问到的难题:如何让 Claude Code Agent 不在你眨眼时停下,而是自己跑到任务真正完成。你将掌握 2026 年社区称作"循环工程(Loop Engineering)"的那套设计模式——把 -p 无头模式、Stop 钩子反馈回路、任务预算(Task Budgets)与跨迭代记忆拼成一个收敛、有界、可审计的自驱循环。适合已写过几个 -p 脚本、想让 Claude 真正"值夜班"的开发者,预计 90–120 分钟读完并可复现。
--print/-p、--resume、--output-format、--max-turns(建议 2026 春季之后的版本)npm test 演示)jq 知识,能看懂 NDJSON 流code.claude.com/docs/en/hooks)最佳,没读过也能跟上很多人对自动化的第一反应是 claude -p "修好这个 bug"。这只跑一轮 agent loop:Claude 收到提示 → 评估 → 调用工具 → 观察结果 → 决定停下。问题在于,Claude 决定"停下"的瞬间,任务往往并没有真正完成——测试还红着、lint 还有 3 个警告、它只是"觉得做完了"。
循环工程的核心洞察是:"完成"是一个可验证的条件,而不是 Claude 的主观判断。围绕这个判断,衍生出三种性质完全不同的循环,必须先分清:
| 循环类型 | 驱动者 | 上下文 | 典型用途 |
|---|---|---|---|
| 单轮 agent loop | Claude 内部 | 单次会话 | 一次性问答、小改动 |
| Stop-hook 反馈循环 | 钩子拒绝停止 | 同一会话内累积 | 强制 Claude 做完质检再交付 |
| 外部 fresh-context 循环(Ralph) | Bash while |
每轮全新进程 | 长周期迭代,规避上下文衰减 |
90% 的"它怎么自己停了"的困惑,源于把这三种混为一谈。本教程的目标,是把三者组合成一个自驱到交付的系统。
外部循环有两种范式,选错会导致任务永远不收敛。
范式 A:fresh-context 循环(社区称 Ralph Loop)。 一个 Bash while,每轮 spawn 一个全新的 claude -p 进程,把目标文件喂进去。核心思想是对抗上下文衰减:一个跑了 30 轮的会话,上下文里堆满历史 diff,Claude 会开始遗忘早期约束、重复犯错。每轮从干净的窗口出发,只读取"上一轮的产物 + 进度记录",反而更稳。
#!/usr/bin/env bash
# loop-driver.sh — fresh-context self-driving loop
set -euo pipefail
GOAL_FILE="$1" # e.g. goals/fix-tests.md
MAX_ITERS="${MAX_ITERS:-12}" # hard ceiling, see 块3
for i in $(seq 1 "$MAX_ITERS"); do
echo "=== iteration $i / $MAX_ITERS ==="
# Each call = a fresh context window, same goal, reads its own prior work
claude -p \
--permission-mode acceptEdits \
--max-turns 40 \
"$(cat "$GOAL_FILE")" \
> "logs/iter-$i.json" 2>&1 || true
# Quality gate (see 块4) decides convergence
if bash .claude/hooks/quality-gate.sh --check; then
echo "✅ convergence reached at iteration $i"
break
fi
done
范式 B:--resume 续跑循环。 同一 session 反复 claude -p -r <session_id>,保留全部历史。适合累积式探索(比如逐步重构,每一步依赖前一步的现场),但扛不住长任务——上下文会膨胀到拖慢、甚至触发压缩。判断口诀:任务每步互相独立、可重放 → fresh-context;任务强依赖同一现场、不可重放 → resume。
Stop 钩子在 Claude 每轮结束、准备停下时触发。它能做一件关键的事:用 decision: "block" 拒绝停止,并把原因注入回上下文,逼 Claude 继续。
.claude/settings.json:
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "bash .claude/hooks/quality-gate.sh" }
]
}
]
}
}
.claude/hooks/quality-gate.sh:
#!/usr/bin/env bash
# Read JSON from stdin, decide whether Claude may stop
input="$(cat)"
stop_active=$(echo "$input" | jq -r '.stop_hook_active // false')
# Run the real check: are tests green?
if npm test --silent >/dev/null 2>&1; then
echo '{"decision":"approve"}' # allow stop
exit 0
fi
# Block + inject a concrete, verifiable reason back into context
# Claude will see this and continue working
echo '{"decision":"block","reason":"测试套件仍有失败。运行 npm test,逐个修复,直到全绿才能停下。"}'
exit 0
两个必须知道的护栏(踩过坑的人都懂):
stop_hook_active 字段防死循环:当 Claude 因 block 而再次尝试停止时,输入里会带上 stop_hook_active: true。成熟的脚本会检查它,在连续 N 次相同失败后主动放行,避免被 GitHub issue #2956 那样的卡死困住。没有预算的自驱循环,等于一张空白支票。2026 年有三层预算控制,建议全部配上:
层 1:单轮轮数上限 --max-turns。 限制一次 claude -p 内 agent loop 最多转多少圈,防止某一轮卡在工具调用里空转。超限时的动作由 --max-turns-action(继续/停止)控制,默认安全停止。
层 2:会话级 token 上限。 通过环境变量(如 CLAUDE_CODE_MAX_OUTPUT_TOKENS)或配置项给单次会话封顶。
层 3:任务预算(Task Budgets)。 这是 Opus 4.7 之后引入的建议性(advisory)整会话 token 预算。一个反直觉但官方明确警告的陷阱:预算太小会让 Claude 产生"拒绝式"行为——它看到一个紧巴巴的预算,会倾向于说"这个任务我做不完"而直接放弃。所以预算要给足,再用外部循环的 MAX_ITERS 做硬封顶,二者分工:Task Budget 管"单轮质量",MAX_ITERS 管"总成本"。
# 组合预算:每轮质量预算 + 总迭代硬封顶
export MAX_ITERS=12 # 硬天花板,块1已用
export CLAUDE_CODE_MAX_OUTPUT_TOKENS=60000 # 单会话上限
收敛条件的本质是一道质量门(Quality Gate)。最好的质量门是项目已有的 CI 命令,因为它是客观、可复现的:
npm test && npm run test:e2etsc --noEmiteslint . --max-warnings 0npm run build把质量门同时接到两个地方:Stop 钩子(块2),让 Claude 每次想停都被拦;外部循环(块1) 的 --check,让循环整体知道何时可以 break。注意区分:PostToolUse 钩子适合"每次改文件后跑单测做快速反馈",而 Stop 钩子适合"整轮交付前的终检"。两者配合,反馈更密。
fresh-context 每轮都是失忆的新生,必须靠外部记忆接力。三类持久化要分开用:
CLAUDE.md(项目记忆):写稳定不变的全局约束——"测试必须用 pnpm test""禁止动 legacy/ 目录"。每轮自动加载。.loop/state.json,记录已完成/待办项,脚本在每轮 prompt 里拼上 $(cat .loop/state.json)。logs/iter-N.json(每轮的 stream-json 输出)、diff,供事后审计和回滚。// .loop/state.json — 任务记忆
{
"goal": "让 npm test 全绿",
"remaining_failures": ["auth.test.ts", "cache.test.ts"],
"last_action": "重构了 auth.ts 的 mock",
"iteration": 3
}
flowchart TD
Start([循环启动]) --> Read[读取 GOAL + CLAUDE.md + state.json]
Read --> Spawn["claude -p --max-turns 40<br/>fresh context 每轮"]
Spawn --> AgentLoop[Claude 内部 agent loop<br/>评估→工具→观察]
AgentLoop --> WantStop{Claude 想停下?}
WantStop -->|是| StopHook["Stop 钩子<br/>quality-gate.sh"]
StopHook --> Gate{质量门通过?<br/>npm test 全绿}
Gate -->|否| Block["decision: block<br/>原因注入回上下文"]
Block -->|8次熔断保护| AgentLoop
Gate -->|是| Approve["decision: approve<br/>允许停止"]
Approve --> Persist[写 state.json + 产物 + 日志]
Persist --> BudgetCheck{预算/迭代数耗尽?}
BudgetCheck -->|否| Read
BudgetCheck -->|是| Done([循环结束 交付])
WantStop -->|否| AgentLoop
目标:给你一个有 4 个失败测试的项目,机器人自己循环修到全绿或耗尽预算为止。
步骤 1 —— 写目标文件 goals/fix-tests.md,要求明确且可验证:
# 目标
让 `npm test` 全绿。逐个修复失败用例,不要跳过、不要改测试期望值。
完成标准:`npm test` 退出码为 0。
当前进度见 .loop/state.json。每修一个,更新它。
步骤 2 —— 落地块2 的 Stop 钩子和块4 的质量门(代码见上文,记得 chmod +x .claude/hooks/quality-gate.sh)。
步骤 3 —— 启动外部循环:
mkdir -p logs .loop
echo '{"goal":"fix-tests","remaining_failures":["unknown"],"iteration":0}' > .loop/state.json
MAX_ITERS=10 bash loop-driver.sh goals/fix-tests.md
预期结果:终端逐行打印 iteration N,每轮 Claude 攻克 1–2 个用例,state.json 的 remaining_failures 递减。约 3–6 轮后出现 ✅ convergence reached,或 10 轮后因预算停手。logs/ 下留下每轮完整决策日志可审计。
步骤 4 —— 验证交付:npm test 退出码为 0,git diff 只动了业务代码没动测试期望。
state.json 里记录"已尝试且失败的方法",prompt 里显式告诉 Claude"别重复这些"。MAX_ITERS 控总成本。stop_hook_active 死循环(卡在 issue #2956):脚本里加计数器,连续 3 次相同失败就 decision: approve 放行,让外部循环接手下一轮。CLAUDE.md 明令禁止,并在质量门里加 git diff --stat 'tests/*' 检测,若测试文件被改则判失败。code.claude.com/docs/en/hooks 与 hooks 指南 code.claude.com/docs/en/hooks-guide(后者含 8 次熔断的官方说明)code.claude.com/docs/en/agent-sdk/agent-loopclaude.com/plugins/ralph-loopplatform.claude.com/docs/en/build-with-claude/task-budgetsdecision:block 反馈回路与"连续 8 次无进展自动 override"熔断机制-p 用法与一个真实带测试的项目。[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
