为什么需要条件触发器
前面几篇讲过 automations 的 cron(定点跑)和 every(间隔跑)。但这两类都是盲跑:不管有没有事,到点就执行一遍,AI 读上下文、调工具,token 照烧。
真实场景里,很多任务其实是"等某个条件成立才该动":
- 某个后台脚本挂了才需要报警,正常时不该刷屏;
- 某个文件/端口出现变化才需要同步,没变化不用跑;
- 监控一个进程日志,只有出现
ERROR才触发处理。
automations 里的 trigger 字段就是干这个的——它是一个轻量只读的条件看门狗,每轮只跑一个小脚本判断是否"起火",起火才真正调用你的大模型任务。正常时几乎零成本。
触发器的工作机制
在 automations 里给一个 cron / every 任务加上 trigger:
{
"schedule": { "kind": "every", "everyMs": 30000 },
"trigger": { "script": "if down: fire; else save state only", "once": false }
}
机制要点(基于 OpenClaw 真实定义):
- trigger 只跑脚本,不调模型:每轮执行
trigger.script,这是只读检查,成本极低。 - 返回值决定要不要跑 payload:脚本输出
json({fire:true, message, state})时,fire:true才会执行真正的payload(你的 agentTurn / 通知)。 state由脚本自己管理:用trigger.state读写(一次只读快照),靠它做去重——比如"已经告警过就不要重复告警"。once:true触发一次后自动禁用:适合"只处理一次"的一次性动作。- 配额限制:trigger 每轮上限约 30s / 5 次工具调用 / 16KB 状态,足够做条件判断,不够做重活。
关键区别:
cron.triggers.enabled=false时触发器整体失效,别在自己关了之后困惑"为什么没触发"。
实战一:监控后台脚本是否还活着
最经典用法——前面《自动化会悄悄死掉》那篇讲过体检,这里用 trigger 做成自动报警版。
建一个每 30 秒检查一次、只在脚本挂掉时通知的任务:
{
"name": "bg-script-watchdog",
"schedule": { "kind": "every", "everyMs": 30000 },
"trigger": {
"script": "import os,json\npid=os.path.exists('/tmp/myscript.pid')\nrun=os.system('pgrep -f myscript.py >/dev/null')==0\nstate={'alerted':False}\nfire=False;msg=''\nif not run and not state.get('alerted'):\n fire=True;msg='myscript.py 已停止';state['alerted']=True\nif run and state.get('alerted'):\n state['alerted']=False\nprint(json.dumps({'fire':fire,'message':msg,'state':state}))"
},
"payload": {
"kind": "agentTurn",
"message": "后台脚本 myscript.py 停止了,请发微信通知 Z 并给出重启命令。"
}
}
逻辑拆解:
- 脚本用
pgrep -f myscript.py判断进程在不在; - 第一次发现不在 →
fire:true并标记alerted,触发 AI 去发通知; - 之后一直不在,因为
alerted已是true,不会重复刷告警; - 进程复活 → 清掉
alerted,下次再挂能重新报。
这就是 trigger 比纯 every 强的地方:自带去重状态,省 token 又不漏报。
实战二:端口/服务探活,挂了才处理
同理,监控本地服务端口,比如 OpenClaw 网关或你的业务端口:
{
"name": "port-check",
"schedule": { "kind": "every", "everyMs": 60000 },
"trigger": {
"script": "import socket,json\nstate={'down':False}\nhost,port='127.0.0.1',8090\ns=socket.socket();s.settimeout(2)\ntry:\n s.connect((host,port));ok=True\nexcept: ok=False\nfinally: s.close()\nfire=False;msg=''\nif not ok and not state.get('down'):\n fire=True;msg=f'{port} 端口无响应';state['down']=True\nif ok and state.get('down'): state['down']=False\nprint(json.dumps({'fire':fire,'message':msg,'state':state}))"
},
"payload": { "kind": "agentTurn", "message": "8090 端口无响应,检查网关状态并重启。" }
}
trigger 与 cron、every 怎么选
| 场景 | 用 cron | 用 every | 用 trigger |
|---|---|---|---|
| 每天 03:00 发一篇文章 | ✅ 定点 | ||
| 每 10 分钟拉一次热搜 | ✅ 间隔 | ||
| 进程挂了才报警 | ✅ 条件 | ||
| 日志出现 ERROR 才处理 | ✅ 条件 | ||
| 文件出现才同步 | ✅ 条件 |
经验法则:"没变化也要跑"用 cron/every;"只有变化才该动"用 trigger。后者能同时压住 token 成本和告警噪音。
几个踩坑提醒
- trigger.script 必须是纯只读判断:动作(发通知、重启)放到
payload里,靠fire:true触发,不要在 trigger 脚本里直接执行副作用。 - state 去重别漏:没做
alerted/down 标记,脚本每次都会fire,等于退化成盲跑还更烧。 once:true用一次就关:适合"首次检测到就做一次"的一次性处理,别用在需要反复监控的场景。- 别忘了配额:trigger 每轮 30s / 5 工具调用 / 16KB,复杂检查要拆简单点。
- 确认 triggers 没被关:
cron.triggers.enabled若为false,触发器根本不跑。
小结
automations 的 trigger 把"定时轮询"升级成"条件驱动":平时只跑一个轻量脚本看门,真出事了才唤醒大模型干活。既省 token,又避免"一切正常也天天刷你一脸通知"。配合前面讲的 cron 发稿、every 拉热搜,你的 OpenClaw 自动化工具有了完整的"定点 / 间隔 / 条件"三件套。
扫码关注公众号,获取更多 OpenClaw 实操技巧:
