claudecode学习 第 19 章 · Cron 与调度

0 阅读6分钟

目标:讲清 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 有两个调度工具,分工不同:

维度CronCreateScheduleWakeup
触发方式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 *"   = 228 日下午 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 83 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 本章核心带走

  1. 两种调度,两个场景:CronCreate 管"在某个时间点执行"(定时提醒、日报),ScheduleWakeup 管"等 N 秒后继续 /loop"(动态轮询)。

  2. CronCreate 三控制recurring(一次性 vs 重复,重复 7 天后过期)、durable(内存 vs 磁盘持久化)、cron 表达式(5 字段,本地时间)。

  3. 避峰:躲开 :00 和 :30。 全球用户同时触发会打爆 API。偏一点的分钟值效果一样但分散负载。

  4. 300s 是最坏延迟。 Prompt cache TTL 刚好过期——付了 cache miss 的钱却没换回更长的等待。要么 <270s(缓存命中),要么 >600s(值得的 cache miss)。

  5. durable 别滥用。 "5 分钟后提醒我"不需要持久化。只有用户明确说"这个任务要一直在"才设 durable: true