<!-- title: 计划任务让Claude Code无人值守 -->

## 概述

本教程聚焦 Claude Code 在「Agent 模式与自动化」方向上最前沿的一层——**让智能体脱离你的双手，自己按时间表跑任务**。你将学会三种无人值守编排方式：无头一次性任务（`claude -p`）、后台异步任务（background）、定时/计划任务（cron + `/loop`），并掌握三者组合成完整闭环的工程方法。适用人群：已经熟悉交互式 Claude Code、想把它接入 CI、定时巡检、长周期监控或"挂着自动干活"场景的开发者与平台工程师。

## 前置条件

- Claude Code 2.x（计划任务 cron 工具与 `/loop` 技能随 2.1+ 引入）
- 一个可运行 `claude` 命令的终端环境（本地或服务器）
- 基础命令行能力，能看懂 cron 五段表达式
- 可选：一个 git 仓库、一台可长期开机的机器或一个 CI Runner（用于 durable 任务）

---

## 第一步：理解三种自动化模式的本质区别

很多人把"自动化"混为一谈，但 Claude Code 里至少有三套完全不同的机制，弄混就会踩坑。先用一张表把它们对齐：

| 模式 | 触发方式 | 是否阻塞 | 生命周期 | 典型场景 |
|------|----------|----------|----------|----------|
| 无头 `-p` | 命令行一次性调用 | 阻塞，跑完即退 | 进程级，跑完结束 | 脚本里跑一次、CI 流水线 |
| 后台任务 | `run_in_background` | 不阻塞，异步 | 随会话存活 | 起开发服务器、长测试、长构建 |
| 计划任务 | cron 表达式 / `/loop` | 到点排队 | 会话级或持久化 | 定时巡检、轮询状态、定时日报 |

关键认知：**计划任务不是系统 crontab，它是会话级的**。它活在你的 Claude Code 进程里，只有当 REPL 处于空闲（idle）状态时才会触发——你正在打字、它正忙着回答你时，任务会排队等，不会打断你。这一点决定了后面所有的用法设计。

```mermaid
flowchart LR
    A[自动化需求] --> B{触发时机?}
    B -->|跑一次就走| C[无头 -p 模式]
    B -->|现在开始一直跑| D[后台任务 background]
    B -->|按时间表周期触发| E[计划任务 cron/loop]
    C --> F[脚本/CI]
    D --> G[开发服务器/长进程]
    E --> H[巡检/轮询/日报]
    F & G & H --> I[组合闭环]
```

记住这张图，下一步我们逐个拆解。

## 第二步：无头 `-p` 模式做一次性任务

`-p`（`--print`）是最底层的自动化入口：加在任何命令后，Claude 就非交互执行，把结果直接打印到 stdout，然后退出。它本质上是把 Claude 当成一个 Unix 管道里的可执行程序。

基础用法：

```bash
# 一次性分析，结果直接输出
claude -p "分析当前目录的 package.json，列出过时的依赖"

# 配合管道，把别的内容喂给它
cat error.log | claude -p "总结这个日志里的三类高频错误"

# 拿到结构化输出，方便后续脚本解析
claude -p "列出 src 下所有未处理的 TODO 注释" \
  --output-format json
```

**`--output-format stream-json`** 是自动化场景的杀手锏。它把 Claude 的思考过程、每一次工具调用、最终结果都按行输出成 JSON 事件流，让你的外部脚本能逐条消费、实时响应：

```bash
claude -p "修复 lint 错误并提交" \
  --output-format stream-json \
  --verbose \
| jq -r 'select(.type=="tool_use") | .name'
```

这条管道能实时打印 Claude 用了哪些工具——等价于给智能体装了一个可观测的探针。

**预期结果**：终端逐行打印 JSON 事件，脚本可解析；普通 `-p` 则直接打印纯文本结论。

**常见问题**：
- *进程一直挂着不退？* 多半是 Claude 在无头运行时启动了后台 bash 任务（比如 `npm run dev`）。官方明确：`claude -p` 退出时会清理它启动的后台任务。如果你手动 `&` 起了进程，需要自己用 `disown` 或 `nohup` 托管。
- *stream-json 模式偶尔卡住？* 这是社区已知的偶发问题（GitHub issue #53584），重试或加 `--input-format` 通常可绕过。

## 第三步：后台任务让 Agent 异步干活

后台任务解决的是"**这个命令很慢，但我现在还想继续和 Claude 对话**"。在工具调用里用 `run_in_background: true`，命令就被剥离到守护线程执行，主循环立刻返回，Claude 继续思考。

典型场景：

```
（对话中）
你：跑一下完整测试套件
Claude: [启动] npm test   ← run_in_background，立刻返回任务 ID
Claude: 测试在后台跑着，我先帮你看别的。跑完会通知我。

（你继续问别的问题，几分钟后）
系统: <task-notification> 后台任务完成
Claude: 测试跑完了，3 个失败，我来看……
```

几个反直觉但关键的点：

1. **不需要也不应该写 `&`**。直接由 harness 接管为 detached，跨轮次存活。
2. **任务结束会"回呼"主 Agent**。这就是为什么 Claude 知道"测试跑完了"——后台退出时，主循环被重新唤醒，附带退出状态。这意味着**后台任务本身就是一种轻量的事件驱动自动化**。
3. **输出落盘**。后台任务的 stdout/stderr 会写到文件，之后用对应工具读取那一份即可，避免巨量日志冲爆上下文。

工程上，后台任务最适合"等待外部状态变化"的活——构建、部署、长测试、慢查询。它和计划任务的区别是：后台任务**一次性、长时、异步**；计划任务**周期性、到点触发**。

## 第四步：计划任务 cron 做定时循环

这是本教程的核心。计划任务让你对 Claude 说"每 30 分钟检查一次部署"、"每天早上 9 点生成日报"，Claude 在底层用 cron 工具注册任务，到点自动把对应的 prompt 重新喂给自己。

### 4.1 怎么创建

最自然的方式是直接用自然语言告诉 Claude：

```
你：每 30 分钟检查一次生产环境的健康指标，有异常就在这个会话里报告。
```

Claude 会调用 cron 创建工具，生成一个类似 `cron: "*/30 * * * *"` 的任务。底层工具签名长这样（你通常不用手写，但理解它有助于调试）：

```jsonc
// 周期任务
{
  "cron": "*/30 * * * *",     // 每 30 分钟
  "prompt": "检查生产健康指标，异常时报告",
  "recurring": true           // 周期触发
}

// 一次性任务（提醒/一次性巡检）
{
  "cron": "30 14 28 6 *",     // 6 月 28 日 14:30
  "prompt": "提醒我核对季度数据",
  "recurring": false          // 触发一次即自动删除
}
```

### 4.2 cron 表达式速查

五段格式 `分 时 日 月 周`，本地时区，无需换算：

| 表达式 | 含义 |
|--------|------|
| `*/5 * * * *` | 每 5 分钟 |
| `*/30 * * * *` | 每 30 分钟 |
| `0 * * * *` | 每小时整点 |
| `0 9 * * 1-5` | 工作日早 9 点 |
| `30 14 28 6 *` | 6 月 28 日 14:30（一次性）|

### 4.3 黄金法则：避开整点

**这是新手最容易忽略的一条**。所有人写"每早 9 点"都会写 `0 9 * * *`，所有"每小时"都写 `0 * * * *`——结果全网用户的任务在同一秒砸向 API，形成人为的流量尖峰。正确做法是**主动错峰**：

```
❌ cron: "0 9 * * *"      // 每个人都这么写，撞车
✅ cron: "57 8 * * *"     // 早 8:57，效果一样，避开洪峰
✅ cron: "7 * * * *"      // 每小时第 7 分
✅ cron: "*/30 * * * *"   // 周期任务自带 ±10% 抖动，可接受
```

只有用户明确点名"9:00 整"时才用 `0 9`。绝大多数"早上左右"的需求，提前几分钟用户根本无感，却能让任务更稳。这条规则同时对你和整个 Claude 用户群都有利。

**预期结果**：`/` 看任务列表（或让 Claude 列出），能看到注册的任务、下次触发时间；到点时（且 REPL 空闲）自动执行 prompt。

## 第五步：`/loop` 做动态自调度

cron 表达式适合"**固定周期**"——每 30 分钟、每天 9 点。但很多自动化的真实节奏是"**看情况**"：CI 还在跑就 1 分钟看一次，跑完了就歇着。这正是 `/loop` 解决的问题。

`/loop` 是一个内置技能，支持两种节奏：

- **固定间隔**：`/loop 5m /检查部署`——每 5 分钟跑一次指定 prompt 或斜杠命令。
- **动态自调度**：`/loop`（不带间隔）——把"多久再来一次"的决定权交给 Claude 自己。

动态模式下，Claude 用一个"唤醒"机制在未来的某个时刻把自己叫醒。这里有一个工程上很重要的细节——**prompt cache（提示缓存）的 5 分钟 TTL**：

- 缓存窗口内（< 5 分钟，建议 ≤ 270 秒）：下一次唤醒命中缓存，又快又省。
- 超过 5 分钟：缓存失效，重新读全部上下文，慢且贵。

所以选唤醒间隔时的心智模型是：

```
正在轮询一个 ~8 分钟的 CI？
  ❌ 每 60 秒醒一次 → 8 次缓存都热但白白烧掉
  ✅ 每 270 秒醒两次  → 命中缓存窗口，更省
  ✅ 或干脆 1200s 以上 → 一次性付一次缓存失效，但间隔长总量少

❌ 千万别选 300 秒 → 既付了缓存失效的代价，又没摊薄，最差组合
```

默认无特殊信号时，1200–1800 秒（20–30 分钟）是合理的"心跳"间隔。Claude 会在唤醒时把同一个 loop prompt 再喂进来，形成持续运转。

## 第六步：durable 持久化 vs session-only 会话级

计划任务有两种存活范围，选错就是"关了电脑任务就没了"的灾难。

- **session-only（默认）**：任务只活在内存里，Claude 进程退出即消失。适合临时轮询——"帮我看 20 分钟部署"。
- **durable（持久化）**：任务写入 `.claude/scheduled_tasks.json`，**跨会话、跨重启存活**。重启 Claude 后自动恢复；一次性 durable 任务即便在 REPL 关闭期间错过，下次启动也会补跑（catch-up）。适合"每天都要做"的长期自动化。

判定原则很简单：

```
要长期、跨天、机器重启后还要继续 → durable: true
只是这次会话里临时跑跑 → 默认 session-only
```

注意一个安全护栏：**周期任务（recurring: true）最多存活 7 天**，到点自动跑最后一次然后删除。这是为了防止"设了忘了"的任务无限消耗。所以如果要做一个真正长期的服务，你需要每 7 天内重新续期一次，或用外部系统 crontab 调度 `claude -p` 来兜底。

## 第七步：组合模式——完整的无人值守闭环

真正的无人值守不是单一模式，而是三者接力。下面这个闭环是生产级自动化的标准范式：

```mermaid
sequenceDiagram
    participant C as 计划任务(cron)
    participant A as 主Agent(空闲)
    participant B as 后台任务
    participant E as 外部状态(CI/部署)

    C->>A: 到点触发 prompt「巡检」
    A->>E: 读取状态
    alt 一切正常
        A-->>C: 等待下次触发
    else 发现异常(CI失败)
        A->>B: 派发后台修复任务(run_in_background)
        A-->>C: 不阻塞，等下次巡检
        B->>E: 诊断+修复+提交
        E-->>B: 完成
        B-->>A: task-notification 回呼
        A->>E: 验证修复
    end
```

这个闭环的精髓：

1. **cron 负责"心跳"**——周期性、低成本的检查，不阻塞。
2. **后台任务负责"重活"**——一旦发现要处理的活，派到后台异步干，主循环立刻释放。
3. **task-notification 负责"接力"**——后台干完回呼主 Agent，自动进入验证环节，无需人工介入。

三者各司其职，整个系统就具备了"自己发现问题、自己修、自己验证"的能力。

---

## 实战案例：无人值守的 CI 健康巡检员

我们用前面所有知识点，搭一个真实可用的项目：**每 30 分钟自动检查主干 CI 状态，发现失败就派后台 Agent 诊断并提交修复，每天早 9 点（错峰 8:57）生成日报**。

### 1. 注册周期巡检任务（会话内对 Claude 说）

```
你：帮我设置一个巡检任务，每 30 分钟检查一次 main 分支的 CI 状态。
    如果 CI 是红的（失败），不要自己卡住——派一个后台 Agent 去诊断根因、
    尝试修复并提交一个修复分支，修完回呼你做验证。任务持久化到磁盘，
    机器重启后也要继续。
```

Claude 会创建一个 durable 的周期任务：

```jsonc
{
  "cron": "*/30 * * * *",
  "prompt": "检查 main 分支 CI 状态。失败则派后台 Agent 诊断+修复+提交，修完回呼验证。正常则静默。",
  "recurring": true,
  "durable": true
}
```

### 2. 注册每日日报（一次性/周期，错峰）

```
你：再加一个，每天早上 8:57 生成一份「过去 24 小时 CI 失败与修复」日报，
    写到 reports/YYYY-MM-DD.md。持久化。
```

```jsonc
{ "cron": "57 8 * * *", "prompt": "生成过去 24 小时 CI 日报，写入 reports/今天.md", "recurring": true, "durable": true }
```

### 3. 巡检 prompt 内部的执行逻辑（Claude 自动遵循）

当 30 分钟到点、REPL 空闲，巡检 prompt 触发，Claude 大致这样跑：

```bash
# 第一步：低成本读状态（不阻塞）
gh run list --branch main --limit 1 --json conclusion,name  \
  | jq -r '.[0].conclusion'
```

- 输出 `success` → 静默，结束本轮。
- 输出 `failure` → 进入派发逻辑：

```
Claude（思考）：CI 失败了。我直接修会阻塞巡检循环，应该派到后台。
Claude（动作）：[Agent 工具, run_in_background: true]
  目标：拉取失败日志 → 定位根因 → 改代码 → 跑本地测试 → 提交 fix/ci-* 分支
Claude（回复）：已派发后台修复任务，本轮巡检结束。
```

后台 Agent 干完后，`<task-notification>` 回呼主 Agent，主 Agent 自动拉取修复分支、复跑 CI、确认变绿——**全程无人工**。

### 4. 用 `/loop` 处理"修复中"的动态节奏

如果失败修复需要 10 分钟，而你不想干等下一个 30 分钟巡检，可以临时启动一个动态 loop：

```
你：/loop 等 CI 修复完成，跑完就停。
```

Claude 会用动态自调度，在缓存窗口内（≤270s）高频检查，一旦 CI 变绿就停止 loop——比固定 cron 灵敏得多，又不浪费。

### 5. 运维要点

- **续期**：周期任务 7 天自动过期。设一个日历提醒，每周重新说一次"续期所有巡检任务"，或写一个外部 crontab：`0 0 * * 1 cd repo && claude -p "续期 scheduled_tasks.json 里的所有任务" --permission-mode acceptEdits`。
- **查看**：随时说"列出我所有计划任务"，Claude 会列出全部任务和下次触发时间。
- **停用**：说"取消 CI 巡检任务"，Claude 用对应的删除工具按 ID 移除，同时从 `.claude/scheduled_tasks.json` 抹掉。

---

## 常见问题与排错

**Q：任务到点了怎么没触发？**
A：九成是 REPL 不空闲。计划任务只在 Claude 空闲（等你输入、没在生成）时触发；你正打字或它正忙着时，任务会排队。确保那个会话窗口是开着的、处于空闲态。会话关了，session-only 任务就没了；durable 任务要等下次会话起来才恢复。

**Q：durable 任务机器重启后会怎样？**
A：下次 Claude 启动时，`scheduled_tasks.json` 里的任务自动恢复。一次性 durable 任务如果在关机期间错过了触发时间，启动时会补跑（catch-up）；周期任务直接按下一个 cron 点继续。

**Q：后台任务跑飞了怎么停？**
A：用任务管理工具按 ID 停止（对应"停止运行中的后台任务"），或直接说"停掉刚才那个修复任务"。后台任务的退出码和状态会回呼主 Agent，你能看到结果。

**Q：cron 表达式总是撞整点，API 限流怎么办？**
A：严格遵循第四步的错峰法则——任何"大概几点"的需求都偏移到非 0/30 分。系统本身对周期任务加了抖动，但你自己写一次性/整点任务时不抖动，必须手动错峰。

**Q：stream-json 输出太大，外部脚本扛不住？**
A：用 `jq` 按事件类型过滤，只消费你关心的（如只取 `tool_use` 或最终 `result`），不要全量解析。

**Q：想完全脱离 Claude 会话、做 24x7 服务怎么办？**
A：会话级计划任务做不到真 7×24（要开着窗口）。真正的常驻服务应该用外部系统 crontab 调度 `claude -p`，把 Claude 当无头一次性进程来编排，再配合持久化的 `scheduled_tasks.json` 做会话内的细粒度任务。两层结合才是生产级架构。

---

## 延伸阅读

- Claude Code 官方文档·计划任务：<https://code.claude.com/docs/en/scheduled-tasks>
- Claude Code 官方文档·无头模式：<https://code.claude.com/docs/en/headless>
- Agent SDK·Agent 循环原理：<https://code.claude.com/docs/en/agent-sdk/agent-loop>
- Background Claude·后台运行实战指南：<https://backgroundclaude.com/>

## 训练营适用性评估

- **适合训练营类型**：DevOps / 平台工程 / SRE 进阶营、AI 工程化实战营、自动化测试与 CI 营。不适合零基础营——需要先掌握交互式 Claude Code 和命令行基础。
- **前置课程**：建议先上"无头 `-p` 模式入门"和"权限配置"两课，再进入本课。
- **预计教学时长**：理论 1.5 小时 + 动手实战 2 小时。实战环节强烈建议让学员真实注册一个 durable 巡检任务，跨一次会话/重启观察恢复行为，否则 durable vs session-only 的差异讲不透。
- **考核点**：能否独立设计一个"cron 巡检 + 后台修复 + 回呼验证"的三段闭环；能否正确解释错峰原则和 7 天过期护栏。

## 参考来源

1. [Run prompts on a schedule — Claude Code Docs](https://code.claude.com/docs/en/scheduled-tasks) — 官方计划任务文档，涵盖 `/loop`、cron 工具、durable 持久化与 7 天过期机制
2. [Run Claude Code programmatically (Headless)](https://code.claude.com/docs/en/headless) — 官方无头模式文档，`-p`、`--output-format stream-json` 与后台任务退出清理行为
3. [Background Claude — Run Claude Code in the background](https://backgroundclaude.com/) — 后台运行、计划任务与无头模式的工程化实战指南
4. [How the agent loop works — Claude Code Docs](https://code.claude.com/docs/en/agent-sdk/agent-loop) — Agent 执行循环原理，理解后台任务回呼机制的基础
5. [Chapter 3: Scheduled Tasks & Automation Loops — Claude Code Handbook](https://joeyyu23.github.io/claude-code-handbook/en/book3-architect/03-scheduled-tasks) — 三种调度方式的对比与组合编排实践

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