首页 › tutorials › 无人值守的代码巡检Agent

概述

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

前置条件

一、先搞清三层调度的边界(否则会踩坑)

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

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

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

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:

---
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 会调用工具,大致等价于:

{
  "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,把巡检、落盘、告警串起来:

#!/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 把它挂上:

# 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 矩阵里别一次性扇出几十个,串行或限并发。

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

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

为了让三层共享同一份"判断标准",把它们都指向同一个 /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 为主。

训练营适用性评估

延伸阅读

参考来源

  1. Run prompts on a schedule — Claude Code Docs — /loop 与 cron 计划任务的官方权威说明,含 v2.1.72 版本要求与 7 天过期规则
  2. Run Claude Code programmatically (Headless) — Claude Code Docs — claude -p 无头模式、--output-format json/stream-json、Agent SDK 的完整参数参考
  3. Claude Code Scheduling: /loop, /schedule, and Cron — supalaunch — 三层调度(会话内 / 本地持久化 / 云端 Routines)的对比与实战示例
  4. Claude Code Scheduled Tasks & Routines: The Complete Guide (2026) — PopularAiTools — 2026 年三层调度体系总览,含 Cloud Routines 研究预览(2026-04-14 发布)的定位
  5. Background Claude — Run Claude Code in the background — 无头模式与定时/后台运行的实操田野手册,覆盖认证、成本追踪与 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学通信知识星球海报-扫码加入