AUTOMATION PATTERN
Cron 任务设计模式¶
When to Use
- Recurring autonomous jobs
- Jobs that must never double-fire
- Jobs needing delivery guarantees
When NOT to Use
- One-off tasks
- Jobs needing interactive input
Skill Contract(本模式的模块声明)
版本 v1.1.1 | 分类 conventions | 相关 state-file-pattern · evolution-gate · error-compact-pattern · control-flow-separation
对应 12-Factor Agents Factor 6: Lifecycle APIs 对应 Loop Engineering Step 5: Automations
核心原则¶
Cron 任务的核心风险不是「跑崩了」,而是「跑偏了但没人发现」。
该不该自动化:频率×可逆性遴选(2026-09-07,借鉴 polydao)¶
不是所有任务都值得做成 cron。先用三条门槛筛,再谈怎么设计。
一个任务值得做成定时自动化的唯一标准:频率高 × 可逆性好。
| 判据 | 合格线 | 解释 |
|---|---|---|
| 频率 | 至少每周重复 | 跑得够多才能看出模式、积累纠正;一月跑一次的任务看不出规律,自动化不划算 |
| 可验证 | 结果一分钟内能确认 | 输出必须有快、便宜的校验路径(读一眼、脚本断言、与 ground truth 对比),否则「准不准」永远悬着 |
| 可逆 | 错了成本≈0、可撤销 | 错了能回滚/丢弃/重跑,不产生不可逆副作用;「错了白花钱」是昂贵教训不是学费 |
反例:发消息 / 支付 / 发布 / 发邮件这类「一旦发出不可撤回」的动作,三条门槛没过前不配进自动化。这类任务要等成熟后,由确定性 gate 包住再上。
合格示例:竞品跟踪、changelog 监控、线索补全、来源筛选、行情监控、财报抓取。不合格示例(现阶段):自动发推、自动下单、自动发布文章。
判断口诀:频率低→不做;验证不了→不做;不可逆→不做。三条都过才配谈三段式与幂等。 本文剩下的三段式、幂等、防静默失败都是「确定要自动化之后」的工程,这条准则是「要不要自动化」的入口。
三段式结构¶
每个 Cron 任务遵循三段式:
Pre-flight¶
1. 读取 STATE.md,检查上次运行状态
2. 检查 Idempotency Keys,跳过已处理的批次
3. 检查资源可用性(磁盘/网络/API Key)
4. 如果任何检查失败 → 记录到 STATE.md → 跳过本次运行
Execute¶
1. 锁定状态(STATE.md status → running)
2. 按步骤执行,每步更新进度
3. 使用确定性代码处理已知路径,LLM 只在决策点介入
4. 失败时按 error-compact-pattern 压缩后写入上下文
Post-flight¶
1. 更新 STATE.md(status → idle,记录统计)
2. 生成运行报告(成功/失败/跳过)
3. 如果失败率超过阈值 → 通知人类
4. 如果连续失败 N 次 → 自动暂停 Cron
幂等性保障¶
| 场景 | 问题 | 解法 |
|---|---|---|
| 任务重复触发 | 同一批数据跑了两遍 | Idempotency Keys |
| 部分成功 | 跑了 50%,下次从哪开始? | STATE.md 进度记录 |
| 静默失败 | 报错了但没人看见 | 失败率阈值+告警 |
| 跑偏 | 输出了错误结果但没报错 | Maker/Checker 验证 |
Idempotency Key 实现¶
def generate_key(task_id: str, batch: str, date: str) -> str:
return f"{task_id}/{batch}/{date}"
def should_skip(key: str, state: dict) -> bool:
return key in state.get("idempotency_keys", [])
防静默失败¶
核心思路:大声失败比沉默通过好一万倍。
| 防护层 | 机制 | 触发条件 |
|---|---|---|
| L1 | 日志记录 | 任何错误 |
| L2 | 失败率告警 | 单次运行失败率 > 20% |
| L3 | 自动暂停 | 连续 3 次运行失败 |
| L4 | 人类通知 | L3 触发后推送到 IM |
Hermes 原生 Monitor 模式(2026-08)¶
上面的三段式是"agent 自查";Hermes 现在提供原生监控模式,从运行时层面解决"跑偏没人发现"——不需要 agent 每 tick 自查,大部分 tick 根本不烧 token。
monitor_script / monitor_url(变化检测)¶
每个 tick:
1. 先运行 monitor 脚本(或抓取 URL),对输出做哈希
2. 哈希与上次相同 → 跳过 agent 运行(0 token,静默 tick)
3. 哈希变化 → 把 unified diff + 新输出注入 agent prompt,跑一轮 agent
4. 首个 tick 总是跑 agent(建立基线)
- 适用:监控网页变化、文件变化、价格/行情变化、外部 API 状态、CI 状态
- 与三段式的关系:monitor 是 Pre-flight 的自动化升级(变化检测交给运行时),agent 只在变化时执行 Execute
- 脚本要求:输出必须稳定(无时间戳/随机顺序),否则每 tick 都"看起来变了"
no_agent=True(Watchdog 模式)¶
脚本即任务:stdout 非空 → 原样投递;stdout 空 → 静默(什么都不发)。适合纯告警/看门狗(磁盘水位、进程存活、API 配额、日志关键词),零 token。
链式与上下文(context_from / script / workdir)¶
| 能力 | 用法 | 场景 |
|---|---|---|
context_from=[jobB] |
job A 最新输出注入 job B 的 prompt | 数据流水线(A 采集 → B 处理) |
script(agent 模式) |
脚本 stdout 注入 prompt 当上下文 | 数据收集 |
enabled_toolsets |
限制 job 工具集 | 降 token:只读监控 job 只给 web 工具 |
attach_to_session |
用户可回复该 job 投递并续上下文 | 交互式任务 |
workdir |
指定目录运行 + 注入 AGENTS.md/CLAUDE.md | 项目内 job |
选择矩阵¶
| 场景 | 推荐 |
|---|---|
| 网页/文件/行情变化检测 | monitor_script / monitor_url |
| 纯脚本告警/看门狗 | no_agent=True |
| 数据流水线(A→B) | context_from 链式 |
| 定期内容生成 | 三段式 + enabled_toolsets |
| 交互式/可追问任务 | attach_to_session |
| 项目内定时维护 | workdir + 三段式 |
与成熟度分级配合¶
| 级别 | Cron 行为 |
|---|---|
| L1 | 只跑报告,不写外部。失败只记日志不告警 |
| L2 | 跑报告+草稿,失败推送摘要到 IM |
| L3 | 全自动执行,失败自动暂停+通知 |
模板¶
# Cron Job: {job-name}
## 调度
- 频率: {cron 表达式}
- 超时: {最大运行时间}
- 重试: {次数和策略}
## 步骤
1. {步骤 1: 描述}
2. {步骤 2: 描述}
## 失败处理
- 可重试: {错误类型}
- 不可重试: {错误类型}
- 人类通知: {通知方式}
## 状态文件
- 路径: reports/{job-name}/STATE.md
真实案例:每日新闻摘要 Cron¶
以下数据来自 Hermes Agent 7×24 运行环境,已脱敏。
接入前(裸 Cron)¶
问题:每天 8:30 跑新闻摘要,一周内出现 2 次静默失败(Agent 输出空内容但标记成功)、1 次重复投递(同一内容发了两次)。
根因:无幂等键 → 重跑时不知道哪些批次已处理;无 Post-flight 验证 → 空输出也当成功。
接入后(cron-job-pattern 三段式 + STATE.md)¶
## STATE.md 快照(运行第 21 天)
### Current Run
- **Last run**: 2026-08-28T08:30:00+08:00
- **Status**: idle
- **Current batch**: 2026-08-28
### Idempotency Keys
- 2026-08-28: news-digest: dispatched ✓
- 2026-08-27: news-digest: dispatched ✓
- 2026-08-26: news-digest: skipped (duplicate trigger, 8:30:02 second fire)
量化结果¶
| 指标 | 接入前(1 周) | 接入后(3 周) |
|---|---|---|
| 静默失败 | 2 次 | 0 次 |
| 重复投递 | 1 次 | 0 次(幂等键拦截) |
| 人工介入 | 3 次 | 0 次 |
| Token 浪费(空跑/重复) | ~40k | ~0 |
关键改进:幂等键拦截了 8:30:02 的重复触发(Hermes cron 调度偶尔在同分钟内触发两次),Post-flight 验证在 8/22 捕获了一次空输出并自动重试成功。