首页 › tutorials › 常驻Agent循环:无人值守构建

概述

本教程教你把 Claude Code 从"一次性 -p 任务"升级为常驻 Agent:按 cron 节奏定时唤醒、读写状态文件、自我验证、安全熔断。内容覆盖最小可行循环、/loop 技能、状态记忆设计、护栏四件套与闭环验证,并给出一个可在夜间无人值守运行"PR 看护者"的完整案例。适用人群:已掌握 claude -p 无头模式与 settings.json hooks,想把 Agent 跑成 7×24 后台进程的开发者。

前置条件


一、先搞清"循环"是什么——为什么 -p 一次性任务不够

很多人把 claude -p 当作自动化终点:写个脚本、传入提示词、拿到 JSON、退出。这是同步一次性模型——无状态、不连续、做完即忘。它适合"批量改造"(如对一批文件做同一件事),却不适合"持续看护"(如整夜盯着 PR、CI 一失败就修)。

Claude Code 作者 Boris Cherny 在 2026 年 6 月给出了清晰的阶梯:

阶段 你在做什么 你的角色
1. 自动补全 手写代码 + AI 提示 打字员
2. 并行会话 开 5~10 个会话,逐个提示 提示操作员
3. 循环 写循环;Agent 读 GitHub/状态自行决定下一步 循环工程师

关键转变:你不再是循环里敲提示词的人,而是写循环的人。模型从"对话对象"降级为"循环体里的一个子程序"。

那么"循环"和古老的 cron 有何区别?一句话:循环 = cron + 一个会决策的身体。

维度 普通 cron job Agent 循环
每次 tick 跑固定脚本 跑一个读取当前状态的模型
分支 永远相同 每次决定下一步动作
自我纠错 无 能验证、失败、重试
进程拓扑 单进程 可派生并监督其他子 Agent

理解了这一点,下面所有工程都是"在那个会决策的身体周围,包上让它不跑下悬崖的一切"。

二、最小可行循环(MVP)——while + claude -p

常驻 Agent 只需三件套:驱动脚本 + 状态文件 + 调度器。

先建目录骨架:

mkdir -p ~/my-agent/{logs,knowledge}
cd ~/my-agent

写最小驱动脚本 run.sh:

#!/bin/bash
# run.sh —— 常驻 Agent 最小驱动
AGENT_DIR="$HOME/my-agent"
STATE="$AGENT_DIR/state.md"
LOG="$AGENT_DIR/logs/$(date +%Y-%m-%d_%H-%M-%S).log"

# 首次运行时初始化状态文件
if [ ! -f "$STATE" ]; then
  cat > "$STATE" <<'EOF'
# Agent 状态
last_loop: 0
goal: 在 ~/my-agent 下用 Python 实现一个 TODO CLI 工具
next: 阅读 specs/TODO.md,挑下一个未完成项实现
EOF
fi

# 把状态注入提示词,无头执行
claude -p "你是一个常驻自治 Agent。先读取下方状态,决定本轮做什么,
执行后用可验证事实更新 state.md。
$(cat "$STATE")" \
  --allowedTools Read,Write,Edit,Bash \
  --permission-mode acceptEdits \
  2>&1 | tee "$LOG"

加调度(每小时一次):

chmod +x ~/my-agent/run.sh
crontab -e
# 追加这一行:
0 * * * * /home/<你>/my-agent/run.sh

预期结果:每小时整点,Agent 醒来一次,读取 state.md、干活、回写状态,日志落盘到 logs/。

常见问题与解决: - 状态文件无限膨胀:到第 20 轮,state.md 可能 50KB,吃光上下文。必须拆 hot/cold(见第四节)。 - 同一结论反复出现:"我应该做 X" 连续 5 轮却无进展——缺目标追踪,靠护栏的"无进展检测"(见第五节)。 - Agent 忘了自己做过什么:把"做过的事"写入 git 提交信息,而不是塞进 state 散文。

三、用 /loop 技能一步替代手写循环

如果不想维护 shell 脚本,Claude Code 自带的 /loop 技能是最快的入口。在交互式会话里直接:

/loop 5m /babysit
/loop 30m /slack-feedback
/loop 5m check the deploy
/loop babysit all my PRs. Auto-fix build issues, and when comments come in,
     use a worktree agent to fix them

要点:

常见问题与解决: - /loop 跑着跑着笔记本一合盖就停了:交互式 /loop 依赖会话存活。要真"关机也能跑",把任务迁到 Claude Code 云端 routine(服务器侧执行),或用下一节的外部 cron + -p 方案。 - /loop 与外部 cron 二选一:别两套同时跑同一任务,否则会重复触发污染状态。

四、设计"会记忆"的状态层——常驻 Agent 的真正难点

循环本身不难,记忆才是。Anthropic 工程团队在长时 Agent 研究中给出三条铁律,配合 dev.to 实战经验,总结如下:

铁律 1:状态文件必须小(<4KB) state.md 每个 tick 都被注入提示词,每一个字节每轮都在烧 token。用结构化键值对,不要让 Agent 写心情散文:

# 当前状态(hot,每轮读)
## 当前
last_loop: 42
last_action: 实现 add 命令
blocked_on: 无

## 指标
tests_passing: 23
tests_failing: 0

## 下一步
1. 实现 list 命令
2. 运行 pytest 验证

铁律 2:冷热分离 事实("Python 3.12 移除了 distutils")放 knowledge/;每轮发生了什么进 git 提交信息或日志。绝不混进 state。

铁律 3:搜索而非全量加载 积累了几百个 knowledge 文件后,别整体注入——建一个 BM25 搜索函数让 Agent 按需查。TF-IDF 加文档长度归一,文本场景够用。

Anthropic 的"初始化 Agent"模式(来自其长时 Agent harness):第一个会话用一个特殊提示词,要求 Agent 搭好三样东西:

随后每个编码会话只做增量进展,结束时把环境留在"可合并到 main 的干净状态",再提交、写进展。JSON 优于 Markdown:Anthropic 发现模型更不倾向于乱改 JSON,于是用 feature_list.json(含 passes 字段)而非 Markdown 清单来防止 Agent 偷偷删测试。

常见问题与解决: - "自我夸赞反馈循环":Agent 在 state 写"今天进展很棒",下一轮读到就真以为一切顺利。用可验证事实(tests_passing: 23)替代主观自评。 - 过早宣布完工:Agent 看到已有进展就喊"做完了"。对策是 feature_list 里每项初始标 passes: false,只允许改状态字段、严禁删测试。

五、护栏四件套——让循环不会"跑飞"

裸 while 循环最大的风险是无限迭代 + 账单爆炸。生产级循环必须配齐四道刹车:

(1) 防重入锁

如果一轮耗时超过调度间隔,两个实例会并发写同一个 state 文件而互相破坏。用文件锁:

LOCKFILE="$AGENT_DIR/.lock"
if [ -f "$LOCKFILE" ]; then
  LOCK_PID=$(cat "$LOCKFILE")
  if kill -0 "$LOCK_PID" 2>/dev/null; then
    echo "已有迭代在跑 (PID $LOCK_PID),跳过本轮"; exit 0
  fi
fi
echo $$ > "$LOCKFILE"
trap 'rm -f "$LOCKFILE"' EXIT

进阶坑(dev.to 评论区实战):kill -0 有两个边界——PID 复用(旧进程崩溃后 PID 被无关进程占走,kill -0 成功导致永远跳过)和 claude 子进程比 cron shell 活得久(MCP server 仍在跑,杀父 shell 不回收)。生产建议:锁文件里写 PID + UUIDv7 owner token,释放时 compare-and-delete;claude 用 setsid 起在独立进程组,超时杀整个进程组。

(2) 最大迭代数

MAX_ITER=20
for i in $(seq 1 "$MAX_ITER"); do
  claude -p "$(cat PROMPT.md)" --permission-mode acceptEdits
done

裸 ralph 循环没有天花板,必须手动加。

(3) 无进展检测

连续 N 次出现同一条错误信息 / 空 diff / 同一个失败测试就停。Geoffrey Huntley 调 ralph 提示词"像调吉他一样"依据失败模式迭代。

(4) 预算上限

设每循环美元预算再睡觉。Uber 曾因 4 个月烧光年度 AI 预算,把每人每工具月费封顶 $1500——这是真实的生产教训。

完整带护栏的 run.sh v2:

#!/bin/bash
set -euo pipefail
AGENT_DIR="$HOME/my-agent"
cd "$AGENT_DIR"

# —— 护栏 (1):防重入锁 ——
LOCK="$AGENT_DIR/.lock"
exec 9>"$LOCK"
if ! flock -n 9; then echo "占用中,跳过"; exit 0; fi

# —— 护栏 (2)+(4):迭代与预算上限 ——
MAX_ITER="${MAX_ITER:-15}"
BUDGET_USD="${BUDGET_USD:-5}"
ITER=0

while [ "$ITER" -lt "$MAX_ITER" ]; do
  ITER=$((ITER+1))

  # —— 护栏 (3):无进展检测 ——
  BEFORE=$(git rev-parse HEAD 2>/dev/null || echo "none")

  claude -p "$(cat PROMPT.md)" \
    --allowedTools Read,Write,Edit,Bash \
    --permission-mode acceptEdits \
    --max-turns 30 \
    --output-format json > "/tmp/tick_$$.json" || true

  # 提取本轮花费,累加预算(需 jq)
  COST=$(jq -r '.total_cost_usd // 0' "/tmp/tick_$$.json" 2>/dev/null || echo 0)
  SPENT=$(echo "${SPENT:-0} + $COST" | bc)
  echo "[iter $ITER] 花费 \$$COST,累计 \$$SPENT / 预算 \$$BUDGET_USD"

  AFTER=$(git rev-parse HEAD 2>/dev/null || echo "none")
  [ "$BEFORE" = "$AFTER" ] && NO_PROGRESS=$((NO_PROGRESS+1)) || NO_PROGRESS=0
  [ "${NO_PROGRESS:-0}" -ge 3 ] && { echo "连续 3 轮无进展,熔断"; break; }

  (( $(echo "$SPENT >= $BUDGET_USD" | bc) )) && { echo "达预算上限,熔断"; break; }
  sleep 60
done
rm -f "/tmp/tick_$$.json"

六、批准门(Approval Gates)——敏感动作留给人

自治 Agent 必须有"能自己做"和"需人批准"的边界。在 system-prompt.md 里写清:

你是一个常驻自治 Agent。

可以自主:
- 读写工作区文件
- 运行 shell 命令、做 web 研究
- 提交 git commit

未经人类批准【不可】:
- 花钱(调用付费 API)
- 公开发布(社交、论坛、PR 评论)
- 联系他人
- 删除生产数据

对需批准的动作,Agent 写一个请求文件,人审:

# Agent 生成:
echo "我想发这条推文:..." > gates/pending-tweet.md
# 人类审阅后批准/拒绝
# Agent 下一轮检查是否有 approved 文件

判断标准:用可逆性决定给多少自主权——可逆、低风险的活让它直接干;面向外部、难撤销的,生成草稿等人批。

七、闭环验证——让循环能"说不"

社区一句箴言:"一半是设计循环,另一半是放进一个能说『不』的东西:测试、类型检查、真实的错误。一个没有东西顶回去的循环,就是 Agent 在跟自己循环赞同。"

三种循环:

类型 行为 适用
开环 Agent 写到自称"完成" 仅 demo
闭环 每次写入后跑测试/lint/review 再继续 生产配护栏
审查环 后台审查者趁上下文新鲜反馈发现 长会话最佳

Anthropic 的做法:feature_list.json 每项有 passes 字段,编码 Agent 被强制要求先用浏览器自动化工具像真人一样端到端测试,只有通过测试才能把 passes 改成 true。这一步把"自信的错代码"挡在门外。


实战案例:夜间 PR 看护者(Nightly PR Babysitter)

一个可运行的常驻 Agent:每 10 分钟巡检自己的 open PR;CI 失败就修、有 review 评论就用 worktree 子代理处理、全绿且无新评论则本轮结束。

目录结构:

pr-babysitter/
├── run.sh            # 带护栏的循环驱动
├── loop.md           # 每轮注入的任务提示词
├── system-prompt.md  # 自主权边界
├── state.md          # hot 状态(<4KB)
├── knowledge/        # 冷知识
├── gates/            # 待批准动作
└── logs/

loop.md:

你是 PR 看护者。按序执行:
1. `gh pr list --author @me --state open`,读取所有 open PR。
2. 对每个 PR:
   - 若 CI 失败:读取失败日志,用 `git worktree` 在隔离分支修复,推送。
   - 若有未处理 review 评论:用 worktree 子代理逐条处理并推送。
   - 若全绿且无新评论:跳过。
3. 把本轮结果(PR 状态、修了什么)以可验证事实写入 state.md。
4. 若全部 PR 已全绿且无待办,输出 `ALL_GREEN` 后退出本轮。
禁止:直接 force push 到 main、删除分支、合并未经批准的 PR(写 gates/)。

run.sh(带四件套护栏,复用第五节 v2 模板,关键差异):

#!/bin/bash
set -euo pipefail
cd "$HOME/pr-babysitter"
exec 9>.lock; flock -n 9 || { echo "占用中"; exit 0; }

for i in $(seq 1 6); do               # 最多 6 轮 / 调度周期
  OUT=$(claude -p "$(cat loop.md)
$(cat state.md)" \
    --allowedTools "Read,Write,Edit,Bash(mkdir:*,gh:*,git worktree:*,npm:*,jq:*)" \
    --permission-mode acceptEdits \
    --output-format json)
  echo "$OUT" | jq -r '.result' >> logs/$(date +%F).log
  echo "$OUT" | jq -r '.total_cost_usd' >> logs/cost.tsv
  echo "$OUT" | jq -re '.result' | grep -q "ALL_GREEN" && break
  sleep 60
done

state.md(结构化示例):

## 当前
last_loop: 8
last_action: 修复 PR #142 的 lint 失败
blocked_on: 无

## PR 状态
pr_142: ci=green comments=0
pr_150: ci=failing comments=2

## 下一步
1. 处理 PR #150 的 2 条评论(worktree: fix-150)

调度(每 10 分钟,错峰避免整点拥堵):

crontab -e
# 每 10 分钟,偏移到 :03/:13/... 避开整点 API 洪峰
*/10 * * * * sleep 180; /home/<你>/pr-babysitter/run.sh

自验证闭环:gh pr checks 的退出码就是"能说不的东西"——CI 红,退出码非零,循环继续修;全绿才允许 ALL_GREEN 退出。git 提交是最佳调试器:git log --oneline 直接还原 Agent 整夜做了什么。

常驻 Agent 完整生命周期

flowchart TD
    A[cron / /loop 唤醒] --> B{防重入锁}
    B -- 占用中 --> Z[跳过本轮]
    B -- 获取锁 --> C[读 state.md 小状态<br/>读 git log 历史]
    C --> D[claude -p 无头执行<br/>注入 loop.md + state]
    D --> E[派发 worktree 子代理<br/>处理 PR/CI/评论]
    E --> F{闭环验证<br/>gh pr checks / pytest}
    F -- 失败 --> G[读结果、修正、重试]
    G --> F
    F -- 通过 --> H{敏感动作?}
    H -- 是 --> I[写 gates/pending-*<br/>等人批准]
    H -- 否 --> J[提交 git + 回写 state]
    I --> J
    J --> K{护栏检查}
    K -- 迭代/预算/无进展 --> L[熔断退出]
    K -- 正常 --> M[写日志、释放锁、睡眠<br/>等待下次 tick]
    G -.连续3轮无进展.-> L

延伸阅读

训练营适用性评估

参考来源

  1. Effective harnesses for long-running agents — Anthropic Engineering — 初始化 Agent、feature_list.json、端到端自检的长时 Agent 工程范式
  2. Run prompts on a schedule — Claude Code Docs — /loop、CronCreate/List/Delete 与定时唤醒的官方权威说明
  3. Loop Engineering: How to Design Coding Agent Loops (2026 Guide) — explainx.ai — ReAct→ralph→/goal→编排循环谱系、Boris 三阶段、护栏三道刹车
  4. How to Run Claude Code as an Autonomous Agent (With a Cron Job) — DEV Community — 最小可行循环、状态 hot/cold 分离、PID 复用与进程组清理实战
  5. How to Use Claude Scheduled Tasks: Automate Recurring Work on a Cron — Alejandro Rioja — 三种调度场景、生产提示词过度指定、可逆性测试

本文由「51学通信」原创,站长爱卫生(微信:gprshome201101)。

[!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 等场景全覆盖。

51学通信知识星球海报-扫码加入