老周的工具笔记

老周的工具笔记

已发布 24 篇文章

触发器与定时:让技能在正确的时间自己醒

触发器与定时:让技能在正确的时间自己醒

技能默认是「你喊它才动」。但很多活的本质是按时发生——每天爬一次数据、每小时跑一次健康检查、每周一发周报。这时候你要的不是「手动触发」,是触发器(Trigger)和定时(Schedule)。

OpenClaw 里这套能力通常落在「自动化(Automation)」机制上:你定义一个任务(prompt/指令)、一个时间规则,它在到点时自己醒来、自己跑、自己收尾。这套机制用熟了,你就是把一堆重复劳动交给时钟,自己只管看结果。

一、两种触发形态

1.1 定时触发( ScheduleType = once / recurring )

  • once:只跑一次,在某个具体时刻(scheduledAt)。适合「明早 9 点发一条提醒」这种一次性任务。
  • recurring:按周期反复跑,用调度表达式描述「多久一次、在哪天哪点」。适合日报、巡检、清理。

1.2 条件触发

不是按钟点,而是「某件事发生了就跑」。比如监听文件变化、监听外部事件、监听某个状态达到阈值。这类实现细节因版本而异,具体事件源与配置方式以官方文档当前版本为准。但思路一致:把「判断条件」从你的脑子里搬到系统里,到点或到条件自动执行。

经验法则:能定时的别用条件(定时可预期、好排查、好复盘);定不了时的才用条件(比如「上游一产出就处理」「错误率超 5% 就报警」)。凡是能用时间描述的,优先用时间,因为时间是最容易被人类理解和验证的维度。

二、周期任务怎么配

recurring 任务的核心是调度表达式。OpenClaw 这类系统常用 RRULE(RFC 5545 标准) 描述重复规则。下面用真实可读的例子讲清楚它怎么读、怎么写、有哪些坑。

RRULE 由若干 KEY=VALUE 用 ; 拼成,最关键的几个:

字段含义举例
FREQ重复基本单位DAILY / HOURLY / WEEKLY / MONTHLY / YEARLY
INTERVAL间隔倍数INTERVAL=2 表示「每 2 个 FREQ」
BYDAY限定星期几MO,WE,FR(周一周三周五)
BYHOUR限定小时BYHOUR=9
BYMINUTE限定分钟BYMINUTE=0
BYMONTHDAY限定月内几号BYMONTHDAY=1,15
BYMONTH限定月份BYMONTH=1,6,12

几个能直接抄的模板

# 每 2 小时跑一次(不分工作日)
FREQ=HOURLY;INTERVAL=2

# 每个工作日的 09:00 跑(周一到周五)
FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;BYHOUR=9;BYMINUTE=0

# 每周一、三、五的 09:00 跑
FREQ=WEEKLY;BYDAY=MO,WE,FR;BYHOUR=9;BYMINUTE=0

# 每月 1 号、15 号、28 号的 09:00 跑
FREQ=MONTHLY;BYMONTHDAY=1,15,28;BYHOUR=9;BYMINUTE=0

# 每年 1 月、6 月、12 月 1 号的 09:00 跑
FREQ=YEARLY;BYMONTH=1,6,12;BYMONTHDAY=1;BYHOUR=9;BYMINUTE=0

读法口诀:先看 FREQ 定节奏,再看 INTERVAL 定倍率,最后看 BY* 系列把「具体时刻」钉死。缺了 BYHOUR/BYMINUTE 的周期规则,系统往往不知道在当天的哪一分钟醒,配置会直接被判不合法——所以日/周/月/年级别的周期,小时和分钟必须给。

RRULE 还能表达「边界」

标准 RRULE 还支持 COUNT(总共跑几次就停)、UNTIL(跑到哪个绝对时刻止)、EXDATE(排除某些具体日期)。比如「工作日 9 点跑,但国庆那几天别跑」,就可以在规则外配合排除日期来实现。具体是否支持这些修饰符,以官方文档当前版本为准,但思路是通用的:周期描述「常态」,排除描述「例外」。

三、一次性和周期的配置对照

维度oncerecurring
必填scheduledAt(具体时刻)rrule(周期表达式)
适用提醒、单次批处理日报、巡检、清理
失效后跑完即结束下一周期自动再来
排查重点时刻是否过期表达式是否合法、时区

配置时通常还要给任务一个 name(方便识别)和 prompt(到点执行的具体指令),以及 status(ACTIVE 才真正生效,PAUSED 是暂停)。指令写清楚「醒来后要做什么、产出放哪」,不然它醒了也不知道干啥。

四、生效范围与生命周期

周期任务可以限定生效区间:从 validFrom 开始,到 validUntil 结束。比如「双十一期间每天巡检」,就把区间钉在活动周期内,活动一结束自动停,不用你手动关。这对「阶段性任务」极其省心——你设定一次,系统帮你收尾。

另外,自动化可以绑定工作目录(cwds)——它决定任务醒来时在哪个目录里干活。如果你的技能要读写特定项目的文件,这个目录必须配对,否则跑出来的是空结果或者找不到文件。很多「定时任务跑了但啥也没干」的案子,根因就是 cwds 指错了地方。

五、常见场景清单

  • 监控巡检:每小时跑一次健康检查,异常才通知,正常静默。
  • 数据备份:每天凌晨把关键目录打包落盘。
  • 周期报告:每周一 9 点汇总上周数据出周报。
  • 临时提醒:明早 8 点提醒开会(once)。
  • 清理任务:每月 1 号清理临时目录过期文件。

这些场景的共同点是「我懒得记、也不该记」。凡是你发现自己每周手动重复同一件事,就该写成一个 recurring 自动化。

这里最容易忽略的一步,是想清楚产出去哪、谁来消费。很多人配定时任务只想着「让它跑」,却没想「它跑完的东西给谁」。结果任务确实每天醒了、确实干了活,但报告躺在一个没人看的目录里,等于白跑。配之前先回答三个问题:产出文件放哪个确定路径?下游是另一个技能自动读,还是人手动收?失败时谁会被通知?把这三个答案写进任务的 prompt,定时任务才真正闭环,而不是只完成「跑」这一个动作。

六、定时任务失效排查

定时任务「没按时醒」是高频投诉。按这个顺序查:

  1. 状态是不是 ACTIVE:PAUSED 的任务再怎么配也不会跑。先确认开关。
  2. 时区对不对:你以为是北京时间 9 点,系统是 UTC 9 点,差 8 小时。周期任务尤其要核对时区配置——这是排第一的坑。
  3. RRULE 是否合法:缺 BYHOUR/BYMINUTE 的日级规则、拼错的 FREQ 值,都会被直接拒。先用上面模板对照。
  4. scheduledAt 是否已过:once 任务如果设定的时刻已经过去,自然不会再触发。
  5. 工作目录是否存在:绑定的 cwds 指向的目录要是没了,任务醒了也干不了活。
  6. 进程/服务在不在:调度依赖后台服务常驻。服务没起来,再对的规则也白搭。具体服务名与管理方式以官方文档当前版本为准。
现象优先怀疑第一步动作
一次都没跑状态 PAUSED / 时刻过期查 status 与 scheduledAt
每天错 8 小时时区对照本地时区配置
配置报错RRULE 非法用模板逐一比对字段
跑了但没产物cwds 错核对工作目录路径
跑一段时间后停validUntil 到了查生效区间

七、一个排错走查

现象:我配了「每个工作日 9 点出日报」,结果连续三天没收到。走查:

  • 查 status:是 ACTIVE,没暂停。
  • 查时区:系统默认 UTC,我按北京时间 9 点写的 BYHOUR=9,实际是北京时间 17 点才跑,而我 17 点已经下班没注意 → 改成对应 UTC 的小时,或显式设时区。
  • 查 cwds:指向的目录存在,没问题。
  • 修正时区后第二天准时收到。

你看,绝大多数「没按时醒」都不是调度引擎坏了,是时区或字段的小错位。把 RRULE 读熟、时区钉死、工作目录配对,剩下的交给时钟。

八、RRULE 实战换算练习

光看模板不够,动手算几个,把读法变成肌肉记忆:

  • FREQ=DAILY;BYHOUR=23;BYMINUTE=30 → 每天 23:30 跑一次。注意 DAILY 必须给小时分钟,否则不合法。
  • FREQ=WEEKLY;INTERVAL=2;BYDAY=SU;BYHOUR=3;BYMINUTE=0 → 每两周的周日 03:00 跑(双周任务)。
  • FREQ=MONTHLY;BYMONTHDAY=-1;BYHOUR=20;BYMINUTE=0 → 每月最后一天 20:00 跑(负数表示从月末倒数,具体是否支持以官方为准)。
  • FREQ=HOURLY;INTERVAL=6;BYDAY=MO,TU,WE,TH,FR → 工作时间内每 6 小时跑一次,周末不跑。

练到「看到表达式能在三秒内说出『哪天几点、隔多久』」,你才算真会读 RRULE。排错时这一步能省大量来回。

九、条件触发深入:事件源长什么样

条件触发虽然具体实现随版本变化,但事件源通常有几种形态值得提前想清楚:文件系统事件(某目录出现新文件就处理)、Webhook/外部推送(别人调你一个接口就触发)、内部状态事件(错误率超阈值就报警)。选哪种,取决于「谁先知道该跑了」:如果是外部先知道,用 Webhook;如果是你自己巡检发现,用定时 + 条件判断更稳。别为了用条件而用条件——很多「条件触发」其实用「定时轮询 + 判断」就能优雅实现,还更好排查。

这里有个权衡:条件触发胜在「实时」,但实时意味着你要一直盯着事件源、要处理事件丢失和重复投递;定时轮询胜在「简单可靠」,代价是有一点延迟。对绝大多数内部场景,那几分钟的延迟根本无伤大雅,而少维护一套事件管道省下的心力是实打实的。除非业务真的对延迟零容忍,否则我默认选轮询。

十、定时与钩子、子代理的组合

这三样不是孤立的:一个 recurring 定时任务(第 03 篇)到点醒来,内部可以派子代理并行干活(第 04 篇),干完在 Stop 钩子(第 02 篇)里自动发通知。这就是「组合技」的雏形——时间负责唤醒,子代理负责分摊,钩子负责收尾。理解每一块单独的语义,组合时才不会互相踩。比如定时任务的 cwds 配错,子代理即便派得再对也读不到文件;Stop 钩子若返回非零,整个定时任务会被判失败。组合越深,越要回到「分层排查」的基本功。

十一、新手配定时最易犯的三种错

结合上面所有内容,把最高频的三个坑单独拎出来强调,因为它们占了定时任务工单的绝大部分:

第一种,以为「配了就一定会跑」。实际上 status 是 PAUSED、或 scheduledAt 已经过期、或后台调度服务没起,任何一个条件不满足都不会醒。配完第一件事不是放手,是手动触发一次验证它真能跑。

第二种,时区想当然。人在国内,系统默认 UTC,于是「每天 9 点」实际是下午 5 点。这种错最隐蔽,因为任务确实在跑,只是不在你以为的时间。配完务必核对时区,最好显式声明。

第三种,把指令写得太含糊。prompt 只写「做日报」,技能不知道日报长啥样、放哪、发给谁,醒来也只能干瞪眼。定时任务的指令比手动对话更要写死细节,因为它没有你坐在旁边临场补充。

十二、验证清单:上线前过一遍

  • 状态是 ACTIVE 吗?
  • 时区对吗?BYHOUR/BYMINUTE 给了吗?
  • cwds 指向的目录存在且可读写吗?
  • 指令里写清了产出位置和格式吗?
  • 手动触发一次,确认真跑出结果了吗?

五条全过,再放手让它自己跑。

定时任务的魅力在于把「记得做」变成「系统做」。写好 RRULE、配对时区和工作目录,剩下的交给时钟——你只需要在它第一次没醒时,按上表把脉。等它稳定跑上一个月,你会惊讶于自己省下了多少本该用来「记得做」的脑容量。