目标:讲清 Claude Code 的定时调度系统——CronCreate(5 字段 cron 表达式 + prompt + recurring/durable 控制)、ScheduleWakeup(
/loop动态循环的延迟唤醒)、两种机制的适用场景、prompt-cache TTL(300s)对延迟选择的约束、避峰设计(避开 :00/:30)、durable 持久化 vs 内存存储。这是运行时纵深的第二章——从"当前"到"未来"。 受众:专业程序员。本机版本2.1.220。
19.1 两种调度工具
Claude Code 有两个调度工具,分工不同:
| 维度 | CronCreate | ScheduleWakeup |
|---|---|---|
| 触发方式 | 5 字段 cron 表达式 | 延迟秒数 |
| 用途 | 定时任务、"提醒我 X" | /loop 动态循环的下次唤醒 |
| 跨 session | 可(durable: true) | 否(仅当前 session) |
| 过期机制 | 重复任务 7 天自动过期 | 无——由 /loop 控制停止 |
| 精度 | 分钟级 | 秒级 |
19.2 CronCreate:定时任务
输入结构
interface CronCreateInput {
cron: string; // 5 字段:"M H DoM Mon DoW"
prompt: string; // 触发时执行的提示词
recurring?: boolean; // true(默认)=重复;false=一次性
durable?: boolean; // true=写磁盘;false(默认)=仅内存
}
Cron 表达式
标准 5 字段,本地时间——无需时区转换:
分钟 小时 日 月 星期
M H DoM Mon DoW
"0 9 * * *" = 每天早上 9:00
"*/5 * * * *" = 每 5 分钟
"0 9 * * 1-5" = 工作日早上 9:00
"30 14 28 2 *" = 2 月 28 日下午 2:30
一次性 vs 重复
一次性(recurring: false)——触发后自动删除:
// "下午 2:30 提醒我检查部署" →
{ cron: "30 14 28 7 *", recurring: false, prompt: "检查部署状态" }
// ↑ 分钟和小时精确指定。触发一次后 job 自动消失。
重复(recurring: true,默认)——每次匹配都触发:
// "每天早上提醒我检查 PR" →
{ cron: "57 8 * * *", recurring: true, prompt: "检查待 review 的 PR" }
7 天自动过期——为什么
重复任务有硬编码的 7 天上限。从二进制确认:
"Recurring tasks auto-expire after 7 days — they fire one final time, then are deleted."
这不是 bug,是设计决策:防止"忘记删的 cron job 永远运行"。每个"每天早上提醒我"的任务在被遗忘后最多再活 7 天——然后最后触发一次,自动清理。
如果你真的需要超过 7 天的定时任务? 快到 7 天时重新创建一个。这是有意为之的摩擦——如果一个任务重要到需要永久运行,你应该确认它还值得存在。
durable + recurring 的组合
durable: false, recurring: true → 每 N 分钟/小时触发,但 session 关了就没
durable: true, recurring: true → 写入 .claude/scheduled_tasks.json,跨 session 持续
但仍然有 7 天上限
durable: true, recurring: false → 一次性持久化——"明天下午 3 点提醒我",即使今晚关机也有效
durable: false, recurring: false → "5 分钟后提醒我"——最轻量,内存里,用完即弃
durable:内存 vs 磁盘
durable | 存储位置 | 生命周期 |
|---|---|---|
false(默认) | 内存 | Session 结束 = job 消失 |
true | .claude/scheduled_tasks.json | 跨 session 持久,重启后恢复 |
durable: true 只应在用户明确要求持久化时使用。 大多数"5 分钟后提醒我"的任务不需要跨 session 存活。
19.3 避峰设计:躲开 :00 和 :30
二进制 system prompt 中的一条明确的调度规则:
"Avoid the :00 and :30 minute marks when the task allows it."
原因:全球每个用户敲"9am"都得到 0 9,所有请求在整点同时打中 Anthropic API。选一个偏一点的分钟值——57 8 或 3 9——效果一样但分散了负载。
实践:
- "每天早上 9 点" → 写成
"57 8 * * *"或"3 9 * * *" - "每小时" → 写成
"7 * * * *"(不是"0 * * * *") - "一个小时后提醒我" → 选当前分钟 + 偏移,别刻意凑整
19.4 ScheduleWakeup:/loop 的引擎
/loop 命令就是靠 ScheduleWakeup 实现的。工作模型:
用户: /loop check the deploy every 10m
│
▼
Agent 执行任务 → ScheduleWakeup(delaySeconds=540, prompt="/loop check the deploy every 10m")
│
▼
540 秒后 → Agent 被唤醒 → 重新执行 prompt → 再次 ScheduleWakeup → ...
输入结构
interface ScheduleWakeupInput {
delaySeconds?: number; // 60 ~ 3600 秒
reason?: string; // 简短说明(显示给用户 + 遥测记录)
prompt?: string; // 唤醒后执行什么
stop?: boolean; // true = 结束循环
}
stop: true 会立即结束循环,不再触发后续唤醒。其他字段被忽略。
延迟选择的物理约束:Prompt Cache
这不是 Claude Code 自己定的规则,是 Anthropic API 的物理现实。
背景:每次 Agent 请求 LLM 前,Claude Code 把你的 system prompt + messages 发过去。Anthropic 服务器计算一个"prompt 的指纹"并缓存结果,有效期 5 分钟(300 秒)。相同内容 5 分钟内再发 → 命中缓存 → 不花钱重新计算 → 便宜。
所以 ScheduleWakeup 的延迟选择 = 决定下次请求要不要多付钱。 权衡如下:
延迟 < 300s 延迟 = 300s 延迟 > 600s
│ │ │
│ 缓存还活着 │ 缓存刚好死掉 │ 缓存早死了
│ 每次请求都便宜 │ 请求变贵了 │ 请求贵了
│ 查多少次都不心疼 │ 但你没等够长—— │ 但你等了 20 分钟
│ ← 积极轮询选这边 │ 贵得很不划算 │ 才查一次,值
│ 270s │ ← 千万别选这里 │ ← 安静等待选这边
│ │ 300s = 最亏 │ 1200~1800s
直觉类比:等电梯。你等 2 秒门开了 → 走,不亏。你等 2 分钟门开了 → 也行,不用爬 20 层楼。你等 29 秒门开了 → 最气——时间花了,但离"不需要等"就差一点点。300s 就是这个 29 秒:缓存刚好过期的那一刻你发请求,最贵但没换回任何好处。
实际用法:
- 等 CI 跑完(预计 8 分钟)→ 设 270s,查 2~3 次,全部缓存命中,便宜
- 等部署完成 → 同上
- 纯粹的空闲等待(没人在乎精确时机)→ 设 1200~1800s(20-30 分钟),让一次 cache miss 换足够长的安眠
- 无论如何别设 300s——既没省下等待时间,又没省下钱
自主循环哨兵
当 /loop 没有用户指定的 prompt(自主模式),ScheduleWakeup 的 prompt 字段传特殊值 <<autonomous-loop-dynamic>>。runtime 唤醒时将其解析为自主循环指令。
这与 CronCreate 模式的 <<autonomous-loop>> 哨兵不同——后者走静态 cron 触发,延迟固定;前者由 ScheduleWakeup 动态决策每次延迟。
19.5 CronDelete 与 CronList
// 删除
interface CronDeleteInput {
id: string; // CronCreate 返回的 job ID
}
// 列出
interface CronListInput {} // 空输入,列出所有当前 job
/cron 会话命令可以查看当前所有定时任务和下次触发时间。
19.6 本章核心带走
-
两种调度,两个场景:CronCreate 管"在某个时间点执行"(定时提醒、日报),ScheduleWakeup 管"等 N 秒后继续
/loop"(动态轮询)。 -
CronCreate 三控制:
recurring(一次性 vs 重复,重复 7 天后过期)、durable(内存 vs 磁盘持久化)、cron 表达式(5 字段,本地时间)。 -
避峰:躲开 :00 和 :30。 全球用户同时触发会打爆 API。偏一点的分钟值效果一样但分散负载。
-
300s 是最坏延迟。 Prompt cache TTL 刚好过期——付了 cache miss 的钱却没换回更长的等待。要么 <270s(缓存命中),要么 >600s(值得的 cache miss)。
-
durable 别滥用。 "5 分钟后提醒我"不需要持久化。只有用户明确说"这个任务要一直在"才设
durable: true。