<!-- title: 常驻Agent循环：无人值守构建 -->

## 概述

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

## 前置条件

- 已安装 **Claude Code v2.1+**（内置 `/loop` 技能与 `CronCreate/CronList/CronDelete` 工具）
- 熟练使用 `claude -p "提示词" --allowedTools ... --output-format json` 无头调用
- 一台常开的机器或云主机（macOS 用 launchd，Linux 用 systemd timer / cron）
- 掌握 git 基本操作（提交、revert、worktree）
- 已配置好 `CLAUDE.md` 项目记忆文件

---

## 一、先搞清"循环"是什么——为什么 `-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 只需三件套：**驱动脚本 + 状态文件 + 调度器**。

先建目录骨架：

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

写最小驱动脚本 `run.sh`：

```bash
#!/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"
```

加调度（每小时一次）：

```bash
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 [间隔] [任务]`。间隔可选 `5m`/`30m`/`1h` 等。
- **省略间隔 = 动态间隔**：Claude 会在 1 分钟到 1 小时之间**自适应**选择延迟——构建还在跑就短等、没有待办就长等，省 token。
- **自定义默认任务**：在项目根放一个 `loop.md`，裸 `/loop` 会用它替换内置维护提示词。
- **停止**：在等待下一次迭代时按 `Esc`。
- **底层即调度工具**：`/loop` 内部调用 `CronCreate`/`CronList`/`CronDelete`，本质仍是 cron 语义，但帮你封装了"下次唤醒该喂什么提示词"。

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

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

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

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

```markdown
# 当前状态（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 搭好三样东西：

- `init.sh`：能启动 dev server 的脚本
- `claude-progress.txt`：进展日志
- 一个**初始 git 提交**，记录已添加的文件

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

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

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

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

### (1) 防重入锁

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

```bash
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) 最大迭代数

```bash
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**：

```bash
#!/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` 里写清：

```markdown
你是一个常驻自治 Agent。

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

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

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

```bash
# 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`**：

```markdown
你是 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 模板，关键差异）：

```bash
#!/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`（结构化示例）**：

```markdown
## 当前
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 分钟，错峰避免整点拥堵）：

```bash
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 完整生命周期

```mermaid
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
```

## 延伸阅读

- **Claude Code 官方：Run prompts on a schedule**（`/loop` 与 cron 工具权威说明）
- **Anthropic：Effective harnesses for long-running agents**（初始化 Agent + feature_list.json + 端到端自检的工程范式）
- **Geoffrey Huntley：Ralph Wiggum loop**（`while :; do cat PROMPT.md | claude; done` 的纪律化循环源头）
- **Anthropic Claude 4 prompting guide**（多上下文窗口工作流最佳实践）
- **explainx.ai：Loop Engineering 2026 Guide**（ReAct→AutoGPT→ralph→/goal→编排循环的完整谱系）

## 训练营适用性评估

- **适合类型**：中高级"AI 工程化 / Agent 自动化"训练营、DevOps 智能化实战营、SRE 自动化方向。**不适合**零基础入门营（需先吃透 `-p` 与 hooks）。
- **难度**：★★★★☆（中高）。难点不在写循环，而在记忆设计与护栏工程。
- **建议教学时长**：4~6 学时。前 2 学时讲概念与 MVP，2 学时带做护栏与状态层，1~2 学时跑通"PR 看护者"案例并复盘失败模式。
- **评估方式**：让学员提交一个跑满 24 小时的循环，附 `git log --oneline`、成本累计与一次熔断记录——能"自我纠错 + 安全停机"即合格。

## 参考来源

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

---
本文由「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学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
