Translates the full English docs corpus (335 files) into Simplified Chinese under website/i18n/zh-Hans/. Combined with PR #31895 (cross- locale link fix), the 简体中文 locale toggle now serves a complete Chinese site with working cross-page navigation. Pipeline: - Claude Sonnet 4.6 via OpenRouter, 8-way concurrent - Preserves frontmatter keys, code blocks, MDX/JSX, link URLs, brand names, and technical jargon (prompt/token/hook/MCP/ACP/etc.) - Translates only frontmatter title/description and prose - Two largest files (configuration.md 93KB, research-paper-writing.md 107KB) retried with 64K max_tokens after initial fence-drift - 3 manual post-fixes for MDX edge cases the model didn't escape: < in optional-skills-catalog table, double-quotes in an alt= tag, and a bare URL adjacent to a full-width period Cost: ~$30 total (Sonnet 4.6 input $3/M + output $15/M). Verified `npm run build` succeeds for both en and zh-Hans locales, no double-prefixed /docs/zh-Hans/docs/ URLs in rendered output, all in-page navigation resolves correctly. Translations are machine-generated and may need human review on specific pages — but they're an enormous improvement over the previous state (3 zh-Hans pages out of 335).
8.9 KiB
| sidebar_position | title | description |
|---|---|---|
| 11 | Cron 内部机制 | Hermes 如何存储、调度、编辑、暂停、加载技能以及投递 cron 任务 |
Cron 内部机制
cron 子系统提供定时任务执行能力——从简单的单次延迟到带技能注入和跨平台投递的周期性 cron 表达式任务。
关键文件
| 文件 | 用途 |
|---|---|
cron/jobs.py |
任务模型、存储、对 jobs.json 的原子读写 |
cron/scheduler.py |
调度器循环——到期任务检测、执行、重复计数跟踪 |
tools/cronjob_tools.py |
面向模型的 cronjob 工具注册与处理器 |
gateway/run.py |
Gateway 集成——在长运行循环中触发 cron tick |
hermes_cli/cron.py |
CLI 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 周期
调度器按周期性 tick 运行(默认:每 60 秒):
tick()
1. 获取调度器锁(防止 tick 重叠)
2. 从 jobs.json 加载所有任务
3. 筛选到期任务(next_run <= now 且 state == "scheduled")
4. 对每个到期任务:
a. 将状态设为 "running"
b. 创建全新的 AIAgent 会话(无对话历史)
c. 按顺序加载附加技能(以用户消息形式注入)
d. 通过 agent 执行任务 prompt(提示词)
e. 将响应投递到配置的目标
f. 更新 run_count,计算下次运行时间
g. 若重复次数耗尽 → state = "completed"
h. 否则 → state = "scheduled"
5. 将更新后的任务写回 jobs.json
6. 释放调度器锁
Gateway 集成
在 gateway 模式下,调度器运行在专用后台线程中(gateway/run.py 中的 _start_cron_ticker),每 60 秒调用一次 scheduler.tick(),与消息处理并行运行。
在 CLI 模式下,cron 任务仅在运行 hermes cron 命令或活跃 CLI 会话期间触发。
全新会话隔离
每个 cron 任务在完全全新的 agent 会话中运行:
- 无前次运行的对话历史
- 无前次 cron 执行的记忆(除非已持久化到内存/文件)
- prompt 必须自包含——cron 任务无法提出澄清性问题
cronjob工具集已禁用(递归防护)
技能支持的任务
cron 任务可通过 skills 字段附加一个或多个技能。执行时:
- 按指定顺序加载技能
- 每个技能的 SKILL.md 内容作为上下文注入
- 任务的 prompt 作为任务指令追加
- Agent 处理技能上下文与 prompt 的组合内容
这使得可复用、经过测试的工作流无需将完整指令粘贴到 cron prompt 中。例如:
创建每日融资报告 → 附加 "ai-funding-daily-report" 技能
脚本支持的任务
任务还可通过 script 字段附加 Python 脚本。该脚本在每次 agent 轮次之前运行,其 stdout 作为上下文注入到 prompt 中。这支持数据采集和变更检测模式:
# ~/.hermes/scripts/check_competitors.py
import requests, json
# 获取竞争对手发布说明,与上次运行结果进行差异比对
# 将摘要打印到 stdout——agent 进行分析并报告
脚本超时默认为 120 秒。_get_script_timeout() 通过三层链路解析限制:
- 模块级覆盖 —
_SCRIPT_TIMEOUT(用于测试/monkeypatching)。仅在与默认值不同时使用。 - 环境变量 —
HERMES_CRON_SCRIPT_TIMEOUT - 配置 —
config.yaml中的cron.script_timeout_seconds(通过load_config()读取) - 默认值 — 120 秒
Provider 恢复
run_job() 将用户配置的备用 provider 和凭证池传入 AIAgent 实例:
- 备用 provider — 从
config.yaml读取fallback_providers(列表)或fallback_model(旧版字典),与 gateway 的_load_fallback_model()模式一致。以fallback_model=形式传入AIAgent.__init__,后者将两种格式规范化为备用链。 - 凭证池 — 通过
agent.credential_pool中的load_pool(provider)使用解析后的运行时 provider 名称加载。仅在池中有凭证时传入(pool.has_credentials())。在遭遇 429/限速错误时启用同 provider 的密钥轮换。
这与 gateway 的行为保持一致——否则 cron agent 在遭遇限速时将直接失败而不尝试恢复。
投递模型
Cron 任务结果可投递到任何受支持的平台:
| 目标 | 语法 | 示例 |
|---|---|---|
| 来源聊天 | origin |
投递到创建该任务的聊天 |
| 本地文件 | local |
保存到 ~/.hermes/cron/output/ |
| Telegram | telegram 或 telegram:<chat_id> |
telegram:-1001234567890 |
| Discord | discord 或 discord:#channel |
discord:#engineering |
| Slack | slack |
投递到 Slack 主频道 |
whatsapp |
投递到 WhatsApp 主会话 | |
| Signal | signal |
投递到 Signal |
| Matrix | matrix |
投递到 Matrix 主房间 |
| Mattermost | mattermost |
投递到 Mattermost 主频道 |
email |
通过邮件投递 | |
| SMS | sms |
通过短信投递 |
| Home Assistant | homeassistant |
投递到 HA 对话 |
| DingTalk | dingtalk |
投递到钉钉 |
| Feishu | feishu |
投递到飞书 |
| WeCom | wecom |
投递到企业微信 |
| Weixin | weixin |
投递到微信(WeChat) |
| BlueBubbles | bluebubbles |
通过 BlueBubbles 投递到 iMessage |
| QQ Bot | qqbot |
通过官方 API v2 投递到 QQ(腾讯) |
对于 Telegram 话题,使用格式 telegram:<chat_id>:<thread_id>(例如 telegram:-1001234567890:17585)。
响应包装
默认情况下(cron.wrap_response: true),cron 投递内容会被包装:
- 头部标识 cron 任务名称和任务内容
- 尾部说明 agent 无法在对话中看到已投递的消息
cron 响应中的 [SILENT] 前缀会完全抑制投递——适用于只需写入文件或执行副作用的任务。
会话隔离
Cron 投递不会镜像到 gateway 会话的对话历史中。它们仅存在于 cron 任务自身的会话中。这可防止目标聊天对话中出现消息交替违规。
递归防护
Cron 运行的会话已禁用 cronjob 工具集。这可防止:
- 定时任务创建新的 cron 任务
- 可能导致 token 用量爆炸的递归调度
- 在任务内部意外修改任务调度
锁机制
调度器使用跨进程文件锁(Unix 上的 fcntl.flock,Windows 上的 msvcrt.locking)防止重叠的 tick 对同一批到期任务执行两次——即使在 gateway 的进程内 ticker 与独立的 hermes cron / 手动 tick() 调用之间也如此。若无法获取锁,tick() 立即返回 0。
CLI 接口
hermes cron CLI 提供直接的任务管理功能:
hermes cron list # 显示所有任务
hermes cron create # 交互式创建任务(别名:add)
hermes cron edit <job_id> # 编辑任务配置
hermes cron pause <job_id> # 暂停运行中的任务
hermes cron resume <job_id> # 恢复已暂停的任务
hermes cron run <job_id> # 触发立即执行
hermes cron remove <job_id> # 删除任务