跳到主要内容

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 工具,采用操作风格的命令:createlistupdatepauseresumerunremove

任务存储

任务存储在 ~/.hermes/cron/jobs.json 中,并采用原子写入语义(先写入临时文件,再重命名)。每个任务记录包含:

{
"id": "job_abc123",
"name": "Daily briefing",
"prompt": "Summarize today's AI news and funding rounds",
"schedule": "0 9 * * *",
"skills": ["ai-funding-daily-report"],
"deliver": "telegram:-1001234567890",
"repeat": null,
"state": "scheduled",
"next_run": "2025-01-16T09:00:00Z",
"run_count": 42,
"created_at": "2025-01-01T00:00:00Z",
"model": null,
"provider": null,
"script": null
}

任务生命周期状态

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

向后兼容性

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

调度器运行时

时钟周期

调度器以周期性时钟运行(默认:每 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

网关集成

在网关模式下,调度器时钟被集成到网关的主事件循环中。网关在其定期维护周期中调用 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 对话
钉钉dingtalk发送到钉钉
飞书feishu发送到飞书
企业微信wecom发送到企业微信
微信weixin发送到微信(WeChat)
BlueBubblesbluebubbles通过 BlueBubbles 发送到 iMessage

对于 Telegram 的主题,使用格式 telegram:<chat_id>:<thread_id>(例如 telegram:-1001234567890:17585)。

响应包装

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

  • 一个标题,标识 cron 任务名称和任务本身
  • 一个页脚,注明代理无法在对话中看到已发送的消息

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

会话隔离

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

递归保护

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

  • 定时任务创建新的 cron 任务
  • 递归调度导致令牌使用量爆炸
  • 在任务内部意外修改任务调度

锁定机制

调度器使用基于文件的锁定机制,防止多个重叠的 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