<!-- title: 无人值守的代码巡检Agent -->

## 概述

本教程教你用 Claude Code 的三层调度能力——会话内 `/loop`、Cron 计划任务、后台无头 `-p`——搭建一个**会自己定时体检仓库的巡检 Agent**。学完你能分清" REPL 开着轮询""定时触发""服务器无人值守"三种自动化的边界，并产出一份可跑的巡检流水线。适合已经装好 Claude Code、写过一两个 slash 命令、想让 AI 在你不盯着时也能干活的开发者。

## 前置条件

- Claude Code **v2.1.72 或以上**（计划任务功能从此版本起内置，用 `claude --version` 确认）
- 一个 Git 仓库（带测试与依赖文件，本教程以 Node 项目为例，思路可平移到 Python/Go）
- 了解 `/loop`、`claude -p`、自定义 slash 命令的基础写法
- 想做服务器端无人值守：一台常开的机器或 CI runner，装好 `jq` 与系统 cron/systemd

## 一、先搞清三层调度的边界（否则会踩坑）

很多人把"自动化"当成一个东西，结果在错的层级上使劲。Claude Code 在 2026 年其实有三层调度，**它们的触发条件和持久性完全不同**：

| 层级 | 触发条件 | 持久性 | 典型用途 |
|------|----------|--------|----------|
| **CLI `/loop`** | REPL 处于**空闲**时才触发 | 仅当前会话 | 开发时盯着终端，每 15 分钟轮询一次 |
| **Cron 计划任务**（`CronCreate`） | REPL **空闲**时触发 | 可选持久化到 `.claude/scheduled_tasks.json` | 工作日早上自动跑深度巡检 |
| **后台无头 `-p`** | 由**外部调度器**（系统 cron / systemd / CI）驱动 | 进程级，跟普通脚本一样 | 服务器无人值守，笔记本合上也跑 |

关键认知：**前两层只在 Claude Code 这个 REPL 进程开着、且不在回答问题时才会触发。** 真正"合上笔记本还在跑"的无人值守，必须靠第三层——外部调度器把 `claude -p` 当成一个普通 shell 命令拉起来。这三层不是互斥，而是**按"你在不在场"组合使用**。

```text
flowchart TD
    A[想自动跑某件事] --> B{你正盯着终端吗?}
    B -- 是,想实时盯 --> C[/loop 15m /命令<br/>会话内轮询]
    B -- 否,但 REPL 常开 --> D{需要跨会话存活吗?}
    D -- 否 --> E[会话级 CronCreate]
    D -- 是 --> F[持久化 CronCreate<br/>写入 scheduled_tasks.json]
    B -- 否,机器都不一定开 --> G[外部调度器<br/>系统cron/systemd/CI]
    G --> H[claude -p 无头执行]
    C --> I[产出健康报告]
    E --> I
    F --> I
    H --> I
    I --> J{有 critical 告警?}
    J -- 是 --> K[推送到 webhook/邮件]
    J -- 否 --> L[归档到 .inspections/]
```

## 二、第一步：先写一个只读的巡检 slash 命令

调度只是"定时器"，真正干活的是被调度的命令。我们先造一个**只读、幂等、输出结构化 JSON** 的 `/inspect-repo`。新建 `.claude/commands/inspect-repo.md`：

```markdown
---
description: 巡检当前仓库健康度，输出结构化 JSON 报告
argument-hint: [focus-area]
allowed-tools: Bash(git status:*), Bash(git log:*), Bash(git rev-list:*), Bash(npm test:*), Bash(npm audit:*), Read, Grep
---

你是仓库健康巡检 Agent。对当前仓库做**只读**巡检，最后只输出一段原始 JSON（不要 Markdown 代码围栏、不要解释文字）。

## 巡检项
1. 版本控制：`git status --porcelain` 统计未提交文件数；`git rev-list --count @{u}..HEAD` 与 `git rev-list --count HEAD..@{u}` 算领先/落后远端提交数
2. 测试：`npm test --silent` 统计通过/失败
3. 依赖：`npm audit --json` 统计 critical/high 漏洞数
4. 代码债：Grep 统计 `TODO|FIXME|XXX` 数量
5. 分支：当前分支名与最近 24h 提交数

用户额外关注：$ARGUMENTS（可能为空）

## 输出 schema（严格按此结构，顶层就是对象）
{
  "branch": "字符串",
  "score": 0到100的整数,
  "uncommitted": 整数,
  "ahead": 整数, "behind": 整数,
  "tests": {"passed": 整数, "failed": 整数},
  "audit": {"critical": 整数, "high": 整数},
  "todoCount": 整数,
  "alerts": [{"level":"critical|warning|info","area":"字符串","message":"字符串"}],
  "summary": "一句话总结"
}
```

**操作说明**：把文件放到仓库的 `.claude/commands/` 下即可，无需注册。`allowed-tools` 把权限收窄成只读命令，巡检阶段绝不写文件——这是无人值守能安全跑的前提。

**预期结果**：在 REPL 里输入 `/inspect-repo 测试与依赖`，Agent 跑完测试和审计，吐出一段纯 JSON。你可以手动 `claude -p "/inspect-repo" | tail -1` 看一眼，确认输出确实能被 `jq` 解析。

**常见问题**：
- *输出带了 ` ```json ` 围栏导致 `jq` 报错*：把命令里的"只输出原始 JSON"再强调一遍，或在外层用 `sed` 剥围栏。
- *`npm test` 跑太久*：在 `allowed-tools` 里把命令换成 `npm test -- --bail` 或限定超时；巡检追求快而非全。
- *没有远端分支导致 `@{u}` 报错*：在 prompt 里加"若无上游分支，ahead/behind 填 0"。

## 三、第二步：用 `/loop` 做会话内实时轮询

`/loop` 是最轻的一层——它假设你**正坐在终端前**，只是想让某个命令每隔几分钟自动重跑。语法：

```
/loop 15m /inspect-repo 测试
```

这会**每 15 分钟**重跑一次 `/inspect-repo`，默认间隔是 10m。它的两个硬约束要记住：

1. **只在 REPL 空闲时触发**。你正在和 Claude 对话时，循环会等当前回合结束再补跑，不会打断你。
2. **会话级生命周期**。关掉这个 Claude Code 进程，循环就没了。它不是"定时任务"，是"会话内的轮询器"。

**进阶：动态节奏**。固定间隔有时浪费——巡检没问题就该拉长间隔，有问题才该盯紧。当你用不带间隔的 `/loop`（动态模式）时，调度器会调用 `ScheduleWakeup` 让 Claude **自己决定**下次多久后醒来。你可以这样指挥：

> 用动态 loop 巡检仓库：如果上次报告 score≥90，下次 30 分钟后再查；如果有 critical 告警，5 分钟后复查确认是否自愈。

这比写死的 `15m` 聪明得多——健康的仓库少打扰，生病的仓库密集复查。

**预期结果**：终端里每隔一段时间自动出现一份新的 JSON 报告，上方有一行 narrator 提示下次触发时间。

**常见问题**：
- *忘了循环还在跑*：用 `/loop`（不带参数）可查看当前循环；不需要时记得停掉，否则一直占会话。
- *间隔设成 `5m` 反而很慢*：5 分钟内每次触发都让缓存失效（prompt cache 5 分钟 TTL），成本会涨。健康仓库宁可设 `30m`。

## 四、第三步：用 Cron 计划任务做"工作日早班巡检"

`/loop` 解决"现在盯着"，但你想"每个工作日早上自动深度巡检一次"——这就轮到 **Cron 计划任务**。它用标准 5 字段 cron 表达式（`分 时 日 月 周`），由 `CronCreate` 创建：

> 帮我建一个持久化的定时任务：每个工作日早上 9 点左右，跑一次 `/inspect-repo 深度`，把报告写到 `.inspections/<日期>.json`。

Claude 会调用工具，大致等价于：

```json
{
  "cron": "13 9 * * 1-5",
  "prompt": "/inspect-repo 深度，并把结果保存到 .inspections/ 目录",
  "recurring": true,
  "durable": true
}
```

**为什么是 `13 9` 而不是 `0 9`？** 这是个真实成本/稳定性细节：所有人写"9 点"都会落 `0 9 * * *`，请求在同一秒打到 API 上形成尖峰。把分钟错峰到 13、27、43 这类非整点，既不影响业务，又躲开了"整点拥堵"。**持久化（`durable: true`）** 会把任务写进 `.claude/scheduled_tasks.json`，下次启动 Claude Code 自动恢复——这是它和 `/loop` 最大的区别。

**两个硬限制务必知道**：
- **只在 REPL 空闲时触发**：和 `/loop` 一样，你必须开着 Claude Code 进程。任务到点了但你没开 REPL，就**不会补跑**（除非是 durable 的 one-shot，下次启动会补做漏掉的）。
- **循环任务 7 天自动过期**：recurring 任务最多跑 7 天，到期后自动删除。长期巡检要么每周重建一次，要么——更稳的做法——交给第四步的外部调度。

**预期结果**：`CronList` 能看到这条任务；到点且 REPL 空闲时，会自动出现一次巡检回合并落盘报告。

**常见问题**：
- *到点了没触发*：九成是 REPL 没开、或正忙。计划任务是"会话内闹钟"，不是系统级守护进程。
- *`durable: true` 后改不动了*：用 `CronList` 拿到 ID，再 `CronDelete <id>` 重建。
- *一次性提醒怎么写*：`recurring: false`，把分/时/日/月钉死，到点跑一次就自删。

## 五、第四步：用外部调度 + 无头 `-p` 做"服务器无人值守"

前三层都依赖"REPL 开着"。真正的无人值守——服务器跑着、你下班了、笔记本合上——必须让**外部调度器**把 `claude -p` 当普通命令拉起。这才是 `-p` 无头模式在自动化里的主战场。

写一个 `inspect-and-alert.sh`，把巡检、落盘、告警串起来：

```bash
#!/usr/bin/env bash
# inspect-and-alert.sh — 无人值守巡检 + 关键告警
set -euo pipefail

REPO="/srv/myapp"
STAMP="$(date +%FT%H%M)"
REPORT="$REPO/.inspections/$STAMP.json"
mkdir -p "$REPO/.inspections"

cd "$REPO"

# 关键：plan 权限 + 限回合数，无人值守绝不能让模型乱写文件
claude -p "/inspect-repo 生产环境" \
  --permission-mode plan \
  --output-format json \
  --max-turns 30 \
  > "/tmp/claude-envelope-$STAMP.json"

# --output-format json 返回的是信封，真正的报告文本在 .result 里
jq -r '.result' "/tmp/claude-envelope-$STAMP.json" > "$REPORT"

# 解析 critical 告警，决定是否报警
CRITICAL=$(jq '[.alerts[]? | select(.level=="critical")] | length' "$REPORT")
SCORE=$(jq '.score // 100' "$REPORT")

echo "[$(date +%FT%T)] score=$SCORE critical=$CRITICAL -> $REPORT"

if [ "$CRITICAL" -gt 0 ] || [ "$SCORE" -lt 60 ]; then
  curl -fsS -X POST "$ALERT_WEBHOOK" \
    -H 'Content-Type: application/json' \
    --data-binary "@$REPORT"
fi
```

然后用**系统 cron** 或 **systemd timer** 把它挂上：

```cron
# crontab -e —— 每天早上 6:13 跑一次（注意 % 在 crontab 里要转义，这里没有）
13 6 * * * /usr/local/bin/inspect-and-alert.sh >> /var/log/inspections.log 2>&1
```

为什么这里用系统 cron 而不是 `CronCreate`？因为系统 cron 由操作系统的 cron 守护进程驱动，**与 Claude Code 进程无关**——就算你根本没启动 Claude Code 的 REPL，它照样到点拉起一个全新的 `claude -p` 进程跑完即退。这是"无人值守"四个字的真正含义。

**`-p` 在自动化里的关键参数**：

| 参数 | 作用 | 自动化里的意义 |
|------|------|----------------|
| `--output-format json` | 整个回合结束后输出一个 JSON 信封 | 下游 `jq` 可解析，`stream-json` 则是流式事件 |
| `--permission-mode plan` | 只读规划，不改文件 | 无人值守时默认收窄到最安全 |
| `--max-turns 30` | 限制最大回合数 | 防止模型陷入死循环烧钱 |
| `--session-id <id>` / `--resume` | 复用会话上下文 | 让巡检"记得"上次状态，做趋势对比 |
| `--add-dir <path>` | 额外授权访问目录 | 巡检多个微服务仓库时用 |

**预期结果**：第二天早上一看 `/var/log/inspections.log`，能看到一行 `score=88 critical=0 -> .inspections/2026-07-04T0613.json`；出问题时你的 webhook 收到告警。

**常见问题**：
- *`.result` 解析出来还是带围栏*：在 slash 命令里把"只输出原始 JSON"再加粗强调，或脚本里加 `sed -n '/^{/,/^}/p'` 兜底。
- *`npm test` 在 `-p` 里卡住*：无人值守没有人工超时干预，务必在命令或脚本层加 `timeout 120 npm test`。
- *CI 里跑要排队*：`claude -p` 是有并发上限的，CI 矩阵里别一次性扇出几十个，串行或限并发。

## 六、实战案例：把三层拧成一个"分级响应"巡检系统

光有三层不够，**价值在于组合**。下面这套设计把"你在场/不在场"两种状态都用上：

- **`/loop`（在场）**：开发时 `/loop 20m /inspect-repo 测试`，盯着测试和依赖，出问题立刻在终端看到。
- **持久化 Cron（半在场）**：工作日早上 9:13 深度巡检并落盘，你打开 Claude Code 就能补看到当天体检报告。
- **系统 cron + `-p`（全不在场）**：服务器每天 6:13 跑一次，**只有出 critical 才打扰你**——健康时静默归档，生病时 webhook 推送。

为了让三层共享同一份"判断标准"，把它们都指向同一个 `/inspect-repo` 命令。这样无论谁触发，输出的 schema 完全一致，告警脚本只需写一套。

进阶可以叠一个 **Stop 钩子**做收尾：当任何一次巡检回合结束时，钩子把当次 score 追加进 `.inspections/trend.csv`，长期就能画出仓库健康趋势线。注意钩子只是"每次回合结束自动跑一段脚本"，真正的定时还是由上面三层负责——别把职责搅混。

最后给一个**安全清单**，无人值守前逐条核对：

1. 巡检命令 `allowed-tools` 只放只读工具，写操作一律剥离。
2. `-p` 默认 `--permission-mode plan`，确需改代码时再单独跑一个"修复"流水线，且只在沙箱容器里 `--dangerously-skip-permissions`。
3. 设 `--max-turns` 和脚本级 `timeout`，双重防失控。
4. 设环境变量 `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` 减少无人值守时的非必要请求。
5. 报告里**绝不**包含密钥——`/inspect-repo` 不要去 `Read` `.env`、`*.key`。
6. recurring cron 记得 7 天会过期，长期任务以系统 cron 为主。

## 训练营适用性评估

- **企业内训 / DevOps 训练营**：高度适配。可作为"AI 辅助运维"模块的实战作业，预计 1 个半天（3–4 小时）：1 小时讲三层边界、1 小时写 `/inspect-repo`、1.5 小时搭外部 cron 流水线、余下时间做告警联调。
- **个人开发者训练营**：适配中等。重点是建立"在不在场决定用哪层"的思维，可砍掉 systemd 部分，聚焦 `/loop` + 系统 cron 两层，预计 2 小时。
- **不适合纯零基础**：需要先掌握 slash 命令、`-p`、权限模式，建议排在入门篇之后。
- **考核点建议**：能否正确说出三层各自触发条件、能否产出可被 `jq` 解析的结构化报告、能否配置出"健康静默/生病告警"的策略。

## 延伸阅读

- Claude Code 官方文档：[Run prompts on a schedule](https://code.claude.com/docs/en/scheduled-tasks)——`/loop` 与 cron 工具的权威说明。
- Claude Code 官方文档：[Run Claude Code programmatically](https://code.claude.com/docs/en/headless)——`-p` 无头模式、`--output-format`、Agent SDK 的完整参考。
- 想做"修复闭环"而非只读巡检：把 `--permission-mode plan` 换成在沙箱内的 `acceptEdits`，让 Agent 不仅诊断还自动提 PR，配合 Stop 钩子做收尾验证。
- 趋势分析：把每天的 JSON 报告喂给一个单独的 `claude -p`，让它对比一周数据、输出"健康度在恶化还是好转"的周报。

## 参考来源

1. [Run prompts on a schedule — Claude Code Docs](https://code.claude.com/docs/en/scheduled-tasks) — `/loop` 与 cron 计划任务的官方权威说明，含 v2.1.72 版本要求与 7 天过期规则
2. [Run Claude Code programmatically (Headless) — Claude Code Docs](https://code.claude.com/docs/en/headless) — `claude -p` 无头模式、`--output-format json/stream-json`、Agent SDK 的完整参数参考
3. [Claude Code Scheduling: /loop, /schedule, and Cron — supalaunch](https://supalaunch.com/blog/claude-code-scheduling-loop-schedule-cron-recurring-tasks-guide) — 三层调度（会话内 / 本地持久化 / 云端 Routines）的对比与实战示例
4. [Claude Code Scheduled Tasks & Routines: The Complete Guide (2026) — PopularAiTools](https://popularaitools.ai/blog/claude-code-scheduled-tasks-routines-2026) — 2026 年三层调度体系总览，含 Cloud Routines 研究预览（2026-04-14 发布）的定位
5. [Background Claude — Run Claude Code in the background](https://backgroundclaude.com/) — 无头模式与定时/后台运行的实操田野手册，覆盖认证、成本追踪与 CI/CD 模式

> [!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)
