Cron 内部机制
cron 子系统提供计划任务执行——从简单的一次性延迟到具有技能注入和跨平台交付的重复 cron 表达式作业。
关键文件
| 文件 | 目的 |
|---|---|
cron/jobs.py | 作业模型、存储、对 jobs.json 的原子读/写 |
cron/scheduler.py | 调度器循环 — 到期作业检测、执行、重复跟踪 |
tools/cronjob_tools.py | 面向模型的 cronjob 工具注册与处理器 |
gateway/run.py | 网关集成——长时间运行循环中的 cron 滴答作响 |
hermes_cli/cron.py | hermes cron CLI 子命令 |
调度模型
支持四种计划格式:
| 格式 | 示例 | 行为 |
|---|---|---|
| 相对延迟 | 30m、2h、1d | 一发,指定持续时间后开火 |
| 间隔 | every 2h、every 30m | 定期发生、定期发生火灾 |
| Cron 表达式 | 0 9 * * * | 0 9 * * *标准 5 字段 cron 语法(分钟、小时、日、月、工作日) |
| ISO 时间戳 | 2025-01-15T09:00:00 | 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 | scheduled活动,将在下一个预定时间触发 |
paused | paused已暂停 — 在恢复之前不会触发 |
completed | completed重复计数已用尽或已发射一发 |
running | 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() 通过三层链解决限制:
- 模块级覆盖 —
_SCRIPT_TIMEOUT(用于测试/monkeypatching)。仅当与默认值不同时才使用。 - 环境变量 —
HERMES_CRON_SCRIPT_TIMEOUT - 配置 —
config.yaml中的cron.script_timeout_seconds(通过load_config()读取) - 默认 — 120 秒
提供商恢复
run_job() 将用户配置的后备大模型提供商(provider)和凭证池传递到 AIAgent 实例中:
- 后备大模型提供商(provider) — 从
config.yaml读取fallback_providers(列表)或fallback_model(旧版字典),匹配网关的_load_fallback_model()模式。作为fallback_model=传递到AIAgent.__init__,这将两种格式标准化为后备链。 - 凭证池 — 使用解析的运行时大模型提供商(provider)名称通过
load_pool(provider)从agent.credential_pool加载。仅当池具有凭据 (pool.has_credentials()) 时才通过。针对 429/速率限制错误启用同一提供商密钥轮换。
这反映了网关的行为——没有它,cron 代理将在速率限制上失败,而不尝试恢复。
交付模式
Cron 作业结果可以传送到任何支持的平台:
| 目标 | 语法 | 示例 |
|---|---|---|
| 起源聊天 | origin | origin交付到创建职位的聊天室 |
| 本地文件 | local | local保存到 ~/.hermes/cron/output/ |
| 电报 | telegram 或 telegram:<chat_id> | telegram:-1001234567890 |
| 不和谐 | discord 或 discord:#channel | discord:#engineering |
| 松弛 | slack | slack交付到 Slack 主频道 |
whatsapp | whatsapp送货到 WhatsApp 首页 | |
| 信号 | signal | signal交付给信号 |
| 矩阵 | matrix | matrix送到 Matrix 家庭房间 |
| 最重要 | mattermost | mattermost送货到 Mattermost 家 |
| 电子邮件 | email | email通过电子邮件发送 |
| 短信 | sms | sms通过短信发送 |
| 家庭助理 | homeassistant | homeassistant交付给 HA 对话 |
| 钉钉 | dingtalk | dingtalk发送至钉钉 |
| 飞书 | feishu | feishu发送至飞书 |
| 微康 | wecom | wecom交付至WeCom |
| 微信 | weixin | weixin发送至微信 |
| 蓝色泡泡 | bluebubbles | bluebubbles通过 BlueBubbles 发送至 iMessage |
| QQ机器人 | qqbot | qqbot通过官方API v2 投递至QQ(腾讯) |
对于 Telegram 主题,请使用格式 telegram:<chat_id>:<thread_id>(例如 telegram:-1001234567890:17585)。
响应包装
默认情况下(cron.wrap_response: true),cron 交付包含以下内容:
- 标识 cron 作业名称和任务的标头
- 页脚指出客服人员无法在对话中看到已传递的消息
cron 响应中的 [SILENT] 前缀完全抑制传递——对于只需要写入文件或执行副作用的作业很有用。
会话隔离
Cron 交付不会镜像到网关会话对话历史记录中。它们仅存在于 cron 作业自己的会话中。这可以防止目标聊天对话中出现消息交替违规。
递归守卫
Cron 运行会话已禁用 cronjob 工具集。这可以防止:
- 创建新的 cron 作业的预定作业
- 递归调度可能会导致令牌使用量激增
- 工作内部的工作安排意外改变
锁定
调度程序使用基于文件的锁定来防止重叠的滴答执行相同的到期作业批次两次。这在网关模式中很重要,如果前一个刻度花费的时间比刻度间隔长,则多个维护周期可能会重叠。
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