Hermes Agent 源码解析
第 17 讲:Curator 编排器与后台自治
基于 Hermes Agent v0.16.0 源码 · 2026-06-30
一、从"对话机器人"到"自治Agent"的跃迁
前 16 讲我们逐层拆解了 Hermes 的核心运行时——从架构概览、对话循环、工具系统、插件、Provider 抽象、记忆与技能、网关适配、上下文压缩、MCP 集成、多模态、ACP 协议、CLI 交互、认证配置、预算控制、System Prompt 构建到工具执行。这些构成了 Hermes 的"前台能力"。
但 Hermes 的真正野心不止于此。它要成为一个24/7 运行的自治 Agent——即使你没有打开终端,它也在后台默默工作:管理技能库、执行定时任务、在每次对话后自我学习。这一讲我们深入 Hermes 的"后台自治系统",核心组件包括:
📦 本讲核心文件
agent/curator.py(1835 行)— 技能编排器主逻辑
agent/curator_backup.py(695 行)— 快照与回滚
agent/background_review.py(608 行)— 每轮对话后的后台审查
cron/scheduler.py(2213 行)— 定时任务调度器
cron/jobs.py(1212 行)— 任务存储与管理
hermes_cli/curator.py(598 行)— CLI 子命令
二、Curator 编排器:技能库的"自治管家"
随着使用时间的增长,Agent 会创建大量技能——有些重复、有些过时、有些可以合并。Curator 就是为了解决这个问题而生的后台技能维护编排器。它不依赖 cron 守护进程,而是采用空闲触发机制:当 Agent 空闲超过 min_idle_hours(默认 2 小时),且距上次 Curator 运行超过 interval_hours(默认 7 天),就会自动启动一次审查。
Curator 的核心职责:
🔹 自动状态转换:基于技能活跃度将技能标记为 active/stale/archived
🔹 LLM 审查:派生一个 forked AIAgent 进行大规模技能合并与归档
🔹 快照与回滚:每次审查前自动备份,支持一键回滚
🔹 状态持久化:将运行状态写入 .curator_state 文件
三、技能生命周期状态机
Curator 维护一个三态生命周期,转换完全基于时间戳计算,不需要 LLM 参与:
🟢 Active(活跃)
⬇️ 超过 stale_after_days(默认 30 天)无活动
🟡 Stale(陈旧)
⬇️ 超过 archive_after_days(默认 90 天)无活动
🔴 Archived(归档)
关键规则:
🔹 只处理 agent-created 技能:内置技能和 Hub 安装的技能不受影响(除非开启 prune_builtins)
🔹 从不自动删除:归档 = 移动到 .archive/,随时可恢复
🔹 Pinned 技能免疫:被用户标记为 pinned 的技能跳过所有自动转换
🔹 使用辅助客户端:不触碰主会话的 prompt cache,独立运行
状态机代码在 apply_automatic_transitions() 中,是一个纯函数——遍历所有候选技能,基于 last_activity_at 或 created_at 时间戳做出决定:
def apply_automatic_transitions(now=None) -> Dict[str, int]:
"""遍历所有候选技能,基于活跃度时间戳转换状态。"""
stale_cutoff = now - timedelta(days=stale_after_days)
archive_cutoff = now - timedelta(days=archive_after_days)
for row in agent_created_report():
if row.get("pinned"):
continue # Pinned 技能跳过
anchor = last_activity or created_at or now
if anchor <= archive_cutoff:
archive_skill(name) # → Archived
elif anchor <= stale_cutoff:
set_state(name, STATE_STALE) # → Stale
elif current == STATE_STALE:
set_state(name, STATE_ACTIVE) # 重新活跃
四、LLM 审查:伞形技能合并引擎
自动状态转换只是"粗筛"。真正的智能审查由 run_curator_review() 触发——它派生一个forked AIAgent,使用专门的 CURATOR_REVIEW_PROMPT(约 1400 词)指导 LLM 执行大规模技能整合。
这个 Prompt 的核心哲学是"伞形技能"(Umbrella Skills):
"目标形状是 CLASS-LEVEL 技能,每个都有丰富的 SKILL.md 和 references/ 目录存储会话级细节。 而不是数百个窄技能,每个只记录一个特定 bug —— 那是技能库的失败,不是特性。"
审查器识别前缀簇(Prefix Clusters)——共享首词或领域关键词的技能组,然后将它们合并为伞形技能。三种合并策略:
| 策略 | 适用场景 | 操作 |
|---|---|---|
| A. 合并入现有伞 | 簇中已有足够宽泛的技能 | patch 伞形 SKILL.md 添加子章节,归档兄弟技能 |
| B. 创建新伞形技能 | 没有成员足够宽泛 | skill_manage create 新伞,归档被吸收的窄技能 |
| C. 降阶为支持文件 | 有价值但过于窄化的内容 | 移入 references/、templates/、scripts/ 子目录 |
五、三重信号融合:去伪存真的分类机制
如何区分一个被归档的技能是"被合并进了伞形技能"还是"纯粹被清理了"?Curator 使用三重信号融合机制:
信号 1:Model-declared absorbed_into
skill_manage delete 时的 absorbed_into 参数(最高优先级)
⬇️ 模型在删除瞬间声明意图
信号 2:YAML 结构化摘要
LLM 最终输出中的 consolidations/prunings 列表
⬇️ 模型自述的分类结果
信号 3:工具调用审计
扫描 skill_manage 调用的 file_path/content 字段
_reconcile_classification() 函数按优先级融合这三个信号,处理各种矛盾场景(如模型声称合并但目标不存在 = 幻觉,回退到工具调用审计)。
六、快照与回滚系统
每次 Curator 运行前,curator_backup.py 自动创建技能树的 tar.gz 快照,存储在 ~/.hermes/skills/.curator_backups/ 下。快照包含:
🔹 所有 SKILL.md 及其子目录(scripts/、references/、templates/、assets/)
🔹 .usage.json(使用遥测数据)
🔹 .archive/(已归档技能,保证回滚完整性)
🔹 .curator_state(运行状态指针)
🔹 jobs.json(Cron 任务引用,回滚时自动修复技能链接)
回滚本身也是可撤销的——回滚前会先对当前状态创建快照,形成无限可回退链。默认保留最近 5 个快照,通过 curator.backup.keep 配置。
七、Cron 调度器:定时任务的"引擎"
Hermes 的后台自治能力很大程度上依赖 Cron 调度器。Gateway 每 60 秒调用一次 tick(),检查并执行到期任务。调度器架构非常精密:
① tick() 入口
文件锁确保全局唯一执行
⬇️ get_due_jobs() 筛选到期任务
② 预推进 next_run_at
at-most-once 语义保证
⬇️ 分区:有 workdir → 串行池 / 无 workdir → 并行池
③ 双线程池执行
_sequential_pool (1 worker) + _parallel_pool (N workers)
⬇️ _process_job():执行 → 保存 → 投递 → 标记
④ 后置清理
MCP 孤儿进程回收 + 旧输出清理
串行 vs 并行分区逻辑:
# workdir 任务修改 os.environ["TERMINAL_CWD"]
# 这是进程全局状态,必须串行执行
sequential_jobs = [j for j in due_jobs
if (j.get("workdir") or "").strip()]
parallel_jobs = [j for j in due_jobs
if not (j.get("workdir") or "").strip()]
三种任务类型:
| 类型 | 说明 | LLM 参与 |
|---|---|---|
| no_agent | 纯脚本执行,直接输出 stdout | ❌ 无 |
| wakeAgent 门控 | 先运行脚本,脚本决定是否需要 LLM | 🔀 条件 |
| 完整 Agent | 派生 AIAgent 执行完整对话循环 | ✅ 是 |
八、后台审查:每轮对话后的自我学习
background_review.py 实现了 Hermes 的"自我改进循环"。每次对话结束后,AIAgent.run_conversation 可能调用 spawn_background_review,派生一个后台线程审查本轮对话:
对话结束
⬇️ spawn_background_review()
派生 forked AIAgent
继承父 Agent 运行时(provider/model/credentials)
⬇️ 工具白名单:仅 memory + skills
审查 Prompt 执行
_MEMORY_REVIEW_PROMPT / _SKILL_REVIEW_PROMPT
⬇️ 更新 MEMORY.md / SKILL.md
返回动作摘要给用户
"Memory updated" / "Skill patched"
关键设计细节:
🔹 继承父运行时:fork 使用相同的 provider/model/credentials,命中相同的 prefix cache
🔹 工具白名单:仅允许 memory 和 skill 管理工具,其他工具运行时拒绝
🔹 跳过外部记忆:skip_memory=True 防止 fork 污染外部记忆插件(honcho/mem0 等)
🔹 禁用压缩:审查需要完整上下文,压缩会丢失细节
🔹 危险命令自动拒绝:安装非交互式审批回调,防止死锁
九、后台自治全景图
将 Curator、Cron 调度器、后台审查三个组件放在一起,就构成了 Hermes 完整的后台自治系统:
🔄 后台自治循环
前台路径:用户对话 → AIAgent 循环 → 后台审查 → 更新记忆/技能
定时路径:Gateway tick(60s) → 筛选到期任务 → 双线程池执行 → 投递结果
空闲路径:Agent 空闲 2h + 距上次审查 7d → Curator 审查 → 合并/归档技能
三个路径共享同一个辅助客户端(auxiliary_client.py),确保不干扰主会话的 prompt cache。辅助客户端有独立的 provider 解析链(主 provider → OpenRouter → Nous Portal → Custom → Anthropic),保证后台任务始终有可用的 LLM 后端。
十、安全与隔离
后台自治系统有多层安全机制:
| 安全层 | 机制 | 保护范围 |
|---|---|---|
| 工具集隔离 | Cron 任务禁用 cronjob/messaging/clarify | 防止递归调度/交互 |
| Prompt 注入扫描 | _build_job_prompt 扫描最终 Prompt | 技能内容中的注入载荷 |
| 文件锁 | fcntl/msvcrt 跨平台文件锁 | 防止多实例重复执行 |
| 路径逃逸防护 | _job_output_dir 拒绝 .. / 绝对路径 | 输出目录沙箱 |
| 危险命令拒绝 | _bg_review_auto_deny 回调 | 后台审查中的危险操作 |
十一、CLI 管理命令
hermes curator 提供完整的 CLI 管理接口:
hermes curator status # 查看 Curator 状态和技能统计 hermes curator run # 立即触发一次审查 hermes curator run --dry-run # 预览模式(只报告不修改) hermes curator pause/resume # 暂停/恢复自动审查 hermes curator pin <name> # 保护指定技能不被自动归档 hermes curator unpin <name> # 取消保护 hermes curator restore <name> # 恢复已归档的技能 hermes curator archive <name> # 手动归档技能 hermes curator prune --days N # 批量归档闲置 N 天的技能 hermes curator backup # 手动创建快照 hermes curator rollback # 回滚到指定快照
十二、配置参数一览
| 配置项 | 默认值 | 说明 |
|---|---|---|
| curator.enabled | true | 是否启用 Curator |
| curator.interval_hours | 168 (7天) | 审查间隔 |
| curator.min_idle_hours | 2 | 最小空闲时间 |
| curator.stale_after_days | 30 | 标记为陈旧的阈值 |
| curator.archive_after_days | 90 | 归档阈值 |
| curator.prune_builtins | true | 是否清理内置技能 |
| curator.backup.keep | 5 | 保留快照数 |
| cron.max_parallel_jobs | 无限制 | 并行任务数上限 |
十三、总结与展望
Curator 编排器和后台自治系统让 Hermes 从一个"对话机器人"进化为一个能自我维护、自我学习的自治 Agent。关键设计原则:
🔹 空闲触发:不占用对话资源,只在 Agent 空闲时运行
🔹 伞形合并:将碎片化技能整合为类级别的知识库
🔹 三重信号:模型声明 + YAML 摘要 + 工具审计,去伪存真
🔹 快照回滚:每次操作可撤销,无限回退链
🔹 纵深防御:工具集隔离 + Prompt 注入扫描 + 文件锁 + 路径沙箱
🔹 双线程池:串行/并行分区,workdir 任务不互相污染
下一讲我们将深入 TUI、Dashboard 与桌面应用,看看 Hermes 如何通过多种前端形态触达用户。
📚 系列导航
← 第 16 讲:Hermes Agent 源码-Tool Executor 深度解析与安全护栏
→ 第 18 讲:TUI、Dashboard 与桌面应用
关注公众号获取更多技术干货 📖
🔗 源码:https://github.com/NousResearch/hermes-agent
夜雨聆风