Skip to main content

Cron 内部机制

Cron 子系统提供定时任务执行功能——从简单的单次延迟到支持技能注入和跨平台交付的周期性 cron 表达式任务。

核心文件

文件用途
cron/jobs.py作业模型、存储,对 jobs.json 的原子读写操作
cron/scheduler.py调度器循环 —— 到期任务检测、执行、重复计数跟踪
tools/cronjob_tools.py面向模型的 cronjob 工具注册与处理器
gateway/run.py网关集成 —— 在长期运行循环中触发 cron 时钟
hermes_cli/cron.pyCLI 中的 hermes cron 子命令

调度模型

支持四种调度格式:

格式示例行为
相对延迟30m, 2h, 1d单次执行,指定时间后触发
间隔every 2h, every 30m周期性执行,按固定间隔重复触发
Cron 表达式0 9 * * *标准 5 字段 cron 语法(分钟、小时、日、月、星期几)
ISO 时间戳2025-01-15T09:00:00单次执行,精确在指定时间触发

面向模型的接口是一个单一的 cronjob 工具,采用动作式操作:create, list, update, pause, resume, run, remove

作业存储

作业存储于 ~/.hermes/cron/jobs.json 中,具有原子写入语义(先写入临时文件,再重命名)。每个作业记录包含:

{
"id": "a1b2c3d4e5f6",
"name": "Daily briefing",
"prompt": "Summarize today's AI news and funding rounds",
"schedule": {
"kind": "cron",
"expr": "0 9 * * *",
"display": "0 9 * * *"
},
"skills": ["ai-funding-daily-report"],
"deliver": "telegram:-1001234567890",
"repeat": {
"times": null,
"completed": 42
},
"state": "scheduled",
"enabled": true,
"next_run_at": "2025-01-16T09:00:00Z",
"last_run_at": "2025-01-15T09:00:00Z",
"last_status": "ok",
"created_at": "2025-01-01T00:00:00Z",
"model": null,
"provider": null,
"script": null
}

作业生命周期状态

状态含义
scheduled激活状态,将在下次预定时间触发
paused暂停状态 —— 不会触发,直到恢复
completed重复次数已耗尽或已执行过的单次任务
running当前正在执行(瞬态状态)

向后兼容性

旧版作业可能仅包含一个 skill 字段,而非 skills 数组。调度器会在加载时进行标准化处理——将单个 skill 提升为 skills: [skill]

调度器运行时

时钟周期

调度器以周期性 tick 运行(默认:每 60 秒一次):

tick()
1. Acquire scheduler lock (prevents overlapping ticks)
2. Load all jobs from jobs.json
3. Filter to due jobs (next_run <= now AND state == "scheduled")
4. For each due job:
a. Set state to "running"
b. Create fresh AIAgent session (no conversation history)
c. Load attached skills in order (injected as user messages)
d. Run the job prompt through the agent
e. Deliver the response to the configured target
f. Update run_count, compute next_run
g. If repeat count exhausted → state = "completed"
h. Otherwise → state = "scheduled"
5. Write updated jobs back to jobs.json
6. Release scheduler lock

网关集成

在网关模式下,调度器 tick 被整合进网关的主事件循环。网关在其定期维护周期中调用 scheduler.tick(),该操作与消息处理并行运行。

在 CLI 模式下,cron 作业仅在运行 hermes cron 命令时或处于活跃 CLI 会话期间才会触发。

新会话隔离

每个 cron 作业都在一个完全独立的代理会话中运行:

  • 无上一次运行的对话历史
  • 无对之前 cron 执行的记忆(除非显式持久化至内存/文件)
  • 提示词必须自包含——cron 作业无法提出澄清问题
  • cronjob 工具集被禁用(防止递归)

技能驱动的作业

一个 cron 作业可通过 skills 字段附加一个或多个技能。执行时:

  1. 技能按指定顺序加载
  2. 每个技能的 SKILL.md 内容作为上下文注入
  3. 作业提示词追加为任务指令
  4. 代理处理合并后的技能上下文 + 提示词

这使得可复用、经过测试的工作流无需将完整说明粘贴到 cron 提示词中。例如:

Create a daily funding report → attach "ai-funding-daily-report" skill

脚本驱动的作业

作业还可通过 script 字段附加一个 Python 脚本。脚本在每次代理回合前运行,其标准输出作为上下文注入提示词。这支持数据采集和变更检测模式:

# ~/.hermes/scripts/check_competitors.py
import requests, json
# Fetch competitor release notes, diff against last run
# Print summary to stdout — agent analyzes and reports

脚本超时默认为 120 秒。_get_script_timeout() 通过三层链路解析该限制:

  1. 模块级覆盖 —— _SCRIPT_TIMEOUT(用于测试/模拟)。仅当与默认值不同时使用。
  2. 环境变量 —— HERMES_CRON_SCRIPT_TIMEOUT
  3. 配置文件 —— cron.script_timeout_secondsconfig.yaml 中(通过 load_config() 读取)
  4. 默认值 —— 120 秒

提供商恢复机制

run_job() 将用户配置的备用提供商和凭证池传递给 AIAgent 实例:

  • 备用提供商 —— 从 config.yaml 读取 fallback_providers(列表)或 fallback_model(旧版字典),匹配网关的 _load_fallback_model() 模式。作为 fallback_model= 传入 AIAgent.__init__,后者将两种格式统一为备用链。
  • 凭证池 —— 通过 load_pool(provider)agent.credential_pool 加载,使用解析后的运行时提供商名称。仅当池中有凭证(pool.has_credentials())时才传递。支持同一提供商在遭遇 429/限流错误时的密钥轮换。

此设计与网关行为一致——否则 cron 代理在限流时将失败且无法尝试恢复。

交付模型

Cron 作业结果可交付至任意受支持平台:

目标语法示例
原始聊天origin交付至创建该作业的聊天
本地文件local保存至 ~/.hermes/cron/output/
Telegramtelegramtelegram:<chat_id>telegram:-1001234567890
Discorddiscorddiscord:#channeldiscord:#engineering
Slackslack交付至 Slack 主频道
WhatsAppwhatsapp交付至 WhatsApp 主页
Signalsignal交付至 Signal
Matrixmatrix交付至 Matrix 主房间
Mattermostmattermost交付至 Mattermost 主页
邮件email通过邮件交付
短信sms通过短信交付
Home Assistanthomeassistant交付至 HA 对话
DingTalkdingtalk交付至钉钉
Feishufeishu交付至飞书
WeComwecom交付至企业微信
Weixinweixin交付至微信(WeChat)
BlueBubblesbluebubbles通过 BlueBubbles 交付至 iMessage
QQ Botqqbot通过腾讯官方 API v2 交付至 QQ

对于 Telegram 订阅话题,使用格式 telegram:<chat_id>:<thread_id>(如 telegram:-1001234567890:17585)。

响应包装

默认情况下(cron.wrap_response: true),cron 交付内容会被包裹:

  • 顶部标题标识作业名称和任务
  • 底部备注:代理无法在对话中看到已交付的消息

若在 cron 响应中使用 [SILENT] 前缀,则完全抑制交付——适用于仅需写入文件或执行副作用的作业。

会话隔离

Cron 交付不会镜像到网关会话的对话历史中。它们仅存在于 cron 作业自身的会话中。这可防止目标聊天中的消息交替违规。

递归防护

运行 cron 的会话禁用了 cronjob 工具集。这可防止:

  • 定时任务创建新的 cron 任务
  • 可能导致 token 使用量爆炸的递归调度
  • 作业内部意外修改任务调度

锁定机制

调度器使用基于文件的锁定机制,防止多个重叠的 tick 重复执行同一组到期任务。这一点在网关模式下尤为重要,因为如果前一个 tick 执行时间超过 tick 间隔,多个维护周期可能发生重叠。

CLI 接口

hermes cron CLI 提供直接的作业管理功能:

hermes cron list                    # Show all jobs
hermes cron create # Interactive job creation (alias: add)
hermes cron edit <job_id> # Edit job configuration
hermes cron pause <job_id> # Pause a running job
hermes cron resume <job_id> # Resume a paused job
hermes cron run <job_id> # Trigger immediate execution
hermes cron remove <job_id> # Delete a job

相关文档