Curator:技能库自我维护
让 Agent 创建的 skill 库不腐烂。状态机、归档策略、 auxiliary LLM review——这是 self-improving agent 的"图书管理员"。
第 8 章我们看到 Agent 能自己创建 skill。问题是——一年下来你的
~/.hermes/skills/ 可能堆了几百个,大部分不再被用、有些彼此重复、
有些有 bug。如果不管,技能库就会腐烂。Curator 是 Hermes 的解法。
10.1问题:技能库会腐烂
具体腐烂方式:
- 过时:6 个月前为某 API v1 写的 skill,API 已经升级到 v3。
- 重复:Agent 上周写了
parse-csv-stats,今天又写了csv-quick-summary——几乎一样。 - 从未真正用过:Agent 一时兴起创建,后来再没启用过。
- schema 漂移:skill 里写 "用 vision_analyze 看图", 但 vision_analyze 工具最近改了参数名。
- 注意力稀释:500 个 skill 让 slash 补全菜单成了垃圾场, LLM 也很难选对的那个。
解决方案:定期 review、归档不活跃的、合并相似的、修破损的。 这事让 Agent 自己做——Curator 是这个想法的实现。
10.2设计原则
看 agent/curator.py 顶部的文档:
agent/curator.py:1-25
"""Curator — background skill maintenance orchestrator.
The curator is an auxiliary-model task that periodically reviews
agent-created skills and maintains the collection. It runs
inactivity-triggered (no cron daemon): when the agent is idle and
the last curator run was longer than ``interval_hours`` ago,
``maybe_run_curator()`` spawns a forked AIAgent to do the review.
Responsibilities:
- Auto-transition lifecycle states based on derived skill activity timestamps
- Spawn a background review agent that can pin / archive / consolidate /
patch agent-created skills via skill_manage
- Persist curator state (last_run_at, paused, etc.) in .curator_state
Strict invariants:
- Only touches agent-created skills (see tools/skill_usage.is_agent_created)
- Never auto-deletes — only archives. Archive is recoverable.
- Pinned skills bypass all auto-transitions
- Uses the auxiliary client; never touches the main session's prompt cache
"""
四条不变量决定 Curator 的性格:
- 只动 agent-created skill。bundled / user-authored 不碰。
(第 8 章讲过
created_by: agent这条元数据。) - 从不自动删除,最重的操作是 archive。可恢复。
- pinned 跳过所有转换。用户钉过的就是要留的。
- 用 auxiliary client——绝不污染主 session 的 prompt cache。
.archive/
目录——还能恢复。这种"自动化但可逆"的设计是可信 AI的关键。
对照那些"AI 帮你管理收件箱时直接删邮件"的产品。
10.3三态生命周期
每个 agent-created skill 有三个状态:
| 状态 | 含义 | 用户感受 |
|---|---|---|
active |
正常使用,在 slash 补全菜单里 | 正常工作 |
stale |
太久没用,从补全菜单里隐藏 | 看不到,但 session_search 仍能找到 |
archived |
移到 .archive/,完全隐藏 |
需要 hermes curator restore 才能恢复 |
加上一个独立的 pinned 布尔,正交于三态:
tools/skill_usage.py:35-45
STATE_ACTIVE = "active"
STATE_STALE = "stale"
STATE_ARCHIVED = "archived"
_VALID_STATES = {STATE_ACTIVE, STATE_STALE, STATE_ARCHIVED}
# 每个 skill 在 ~/.hermes/skills/.usage.json 里的记录
# {
# "created_at": "...",
# "created_by": "agent" | "user",
# "last_used_at": "...",
# "use_count": 12,
# "state": "active" | "stale" | "archived",
# "pinned": true | false
# }
状态转换图
stateDiagram-v2 direction LR [*] --> ACTIVE: skill 创建 ACTIVE --> ACTIVE: 被使用 ACTIVE --> STALE: 30 天没用 STALE --> ACTIVE: 再次使用
(reactivate) STALE --> ARCHIVED: 90 天没用
(移到 .archive/) ARCHIVED --> ACTIVE: hermes curator restore note right of ARCHIVED 永不删除 可手动恢复 end note note left of ACTIVE pinned: true 的 skill 跳过所有自动转换 end note
10.4实际的转换代码
看 agent/curator.py 第 256–296 行:
agent/curator.py:256-296
def apply_automatic_transitions(now=None) -> Dict[str, int]:
"""Walk every agent-created skill and move active/stale/archived
based on the latest real activity timestamp. Pinned skills are
never touched. Returns a counter dict describing what changed."""
from tools import skill_usage as _u
if now is None:
now = datetime.now(timezone.utc)
stale_cutoff = now - timedelta(days=get_stale_after_days())
archive_cutoff = now - timedelta(days=get_archive_after_days())
counts = {"marked_stale": 0, "archived": 0,
"reactivated": 0, "checked": 0}
for row in _u.agent_created_report():
counts["checked"] += 1
name = row["name"]
if row.get("pinned"):
continue
last_activity = _parse_iso(row.get("last_activity_at"))
# 如果从来没活动过,用 created_at 作为锚
anchor = last_activity or _parse_iso(row.get("created_at")) or now
if anchor.tzinfo is None:
anchor = anchor.replace(tzinfo=timezone.utc)
current = row.get("state", _u.STATE_ACTIVE)
if anchor <= archive_cutoff and current != _u.STATE_ARCHIVED:
ok, _msg = _u.archive_skill(name)
if ok:
counts["archived"] += 1
elif anchor <= stale_cutoff and current == _u.STATE_ACTIVE:
_u.set_state(name, _u.STATE_STALE)
counts["marked_stale"] += 1
elif anchor > stale_cutoff and current == _u.STATE_STALE:
# 又被用了 — 自动 reactivate
_u.set_state(name, _u.STATE_ACTIVE)
counts["reactivated"] += 1
return counts
注意几个细节:
- "如果从来没活动过":用
created_at当锚。这避免新 skill 被立即归档("它创建时还没来得及用,凭啥归档")。 - "再次活动了自动 reactivate":用户用了一个 stale skill,它自动升级回 active。 不要让用户手动改状态。
- 纯纯的状态机,没有任何"思考"。Curator 这一步是无 LLM的—— 纯 Python,规则化。
10.5触发:inactivity-based,不是 cron
什么时候跑 Curator?Hermes 的答案:"等用户闲下来"。
agent/curator.py (简化)
def maybe_run_curator() -> None:
"""Called at session_end / idle detection. Decides whether to run."""
state = load_curator_state()
cfg = _load_config()
if not cfg.get("enabled", True):
return
if state.get("paused"):
return
last_run = _parse_iso(state.get("last_run_at"))
interval_hours = get_interval_hours() # 默认 168 = 一周
min_idle_hours = get_min_idle_hours() # 默认 2 小时
if last_run and (now - last_run).total_seconds() < interval_hours * 3600:
return # 还没到时间
last_activity = _get_user_last_activity()
if (now - last_activity).total_seconds() < min_idle_hours * 3600:
return # 用户最近还在用,别打扰
# 满足条件 — 跑
_run_curator()
state["last_run_at"] = now.isoformat()
save_curator_state(state)
关键设计:
- 不是 cron 守护进程。Hermes 不开后台 daemon。 Curator 在 session 结束时检查一次。
- 双重门槛:(a) 距上次 run 超过
interval_hours, (b) 用户至少闲min_idle_hours。 - 状态持久化到
~/.hermes/.curator_state。 - 跨 session 等价:Hermes 一周不开机,下次开机仍然知道"上次 Curator 跑过"。
10.6Auxiliary LLM Review
除了无脑状态机,Curator 还会启动一个 review agent 做智能审查。
这个 review agent 是个独立的 AIAgent 实例,用 auxiliary model(便宜小模型, 不是主对话用的旗舰),加载这些工具:
review_agent = AIAgent(
model=cfg.get("curator.review_model", "claude-haiku-4-5"),
enabled_toolsets=["skills"], # 只能动 skill 相关工具
skip_memory=True, # 不污染主 memory
skip_context_files=True, # 不要 AGENTS.md 等
quiet_mode=True,
max_iterations=30,
)
它的 system prompt 大致是:
You are the Hermes Skills Curator. Review the following agent-created skills.
Active skills (last used ≥ 30 days ago, candidate for stale):
- data-processing-pipeline (last used 35 days ago, use_count: 2)
- csv-quick-summary (last used 40 days ago, use_count: 1)
Recently archived (90+ days no use):
- (none)
Your tasks:
1. For each candidate, decide:
- keep as active (if still useful)
- mark stale
- merge with similar skill
- patch (fix broken references / outdated APIs)
2. Use skill_view to inspect content before deciding.
3. Use skill_manage(action="consolidate") to merge.
4. Use skill_manage(action="patch") to fix.
5. Output a brief summary.
Do NOT delete anything. Only archive. Pinned skills are off-limits.
这个 review agent 跑完,输出报告:
Curator Review · 2026-05-23
─────────────────────────────────────────
Reviewed 12 skills.
Consolidated:
- merged 'csv-quick-summary' + 'parse-csv-stats' → 'csv-analyze'
Patched:
- 'web-deep-research': updated outdated reference to web_search v1 → v2
Marked stale:
- 'old-mlflow-integration' (last used 67 days ago, low value)
Active: 9 skills remain.
报告写进 ~/.hermes/.curator_state,用户可以用 hermes curator status 看。
10.7backup:每次 review 前快照
Curator 可能改 / 归档 skill。如果它判断错了怎么办? Hermes 在每次 review 前打一个 tar.gz 快照:
~/.hermes/skills/.backups/
├── 2026-05-16_curator.tar.gz
├── 2026-05-09_curator.tar.gz
├── 2026-05-02_curator.tar.gz
└── ...
每个 backup 是整个 skills/ 目录的快照。用户随时可以:
hermes curator rollback 2026-05-16
# 把 skills/ 还原到那个时间点的状态
backup 保留策略:默认 4 周。可在 config.yaml 改。
10.8用户怎么干预
Hermes 的 hermes curator CLI 给用户完全控制:
| 命令 | 作用 |
|---|---|
hermes curator status | 看上次 run 时间、转换结果 |
hermes curator run | 立即跑一次(绕过 interval / idle 检查) |
hermes curator pause | 暂停自动 review |
hermes curator resume | 恢复自动 review |
hermes curator pin <name> | 钉住一个 skill,免疫所有转换 |
hermes curator unpin <name> | 取消钉住 |
hermes curator archive <name> | 手动归档 |
hermes curator restore <name> | 恢复归档 skill |
hermes curator prune | 清理 90 天前的 archived(用户确认后) |
hermes curator backup | 手动打快照 |
hermes curator rollback <date> | 还原快照 |
10.9配置
~/.hermes/config.yaml:
curator:
enabled: true # 默认 ON
interval_hours: 168 # 一周一次
min_idle_hours: 2 # 用户至少闲 2 小时
stale_after_days: 30 # 30 天没用变 stale
archive_after_days: 90 # 90 天没用归档
review_model: claude-haiku-4-5 # review 用的便宜模型
backup:
enabled: true
retain_weeks: 4 # 快照保留 4 周
三个数字(30 / 90 / 168)是经验值——Hermes 团队跑了几个月得出的。 你可以根据自己使用频率调整。
10.10横向对照:哪些产品有 Curator 类似机制
| 产品 | 类似机制 |
|---|---|
| Cursor | "@codebase" 索引会定期重建,但无 skill 概念 |
| Claude Code | 没有 skill 自动归档;CLAUDE.md 完全人工管理 |
| Voyager | skill library 不归档,但 iterative refinement 时会被改写 |
| Letta (MemGPT) | archival memory 有 paging,但无显式 review 工作流 |
| Hermes | 完整的"创建 → 使用 → 归档 → 备份 → 恢复"循环 |
Hermes 的 Curator 是"长寿 Agent 生态"的一个独特实践。如果你计划让 Agent 运行 数月甚至数年,必须有这种机制。否则 skill 库迟早爆炸。
10.11Curator 哲学:Pruning Is Caring
Curator 体现了一个深刻的工程理念:
An agent that only learns but never forgets is a hoarder. Real intelligence requires pruning.
三个学习闭环组件的协作:
flowchart TB S["Skill
创建"]:::pillar M["Memory
回忆"]:::pillar C["Curator
修剪"]:::pillar S --> SA["把新经验写成 SKILL.md"] M --> MA["搜索相关历史
注入 volatile"] C --> CA["定期审查 · 归档 · 合并
防止技能库膨胀腐烂"] SA --> Short["短期收益
这次任务能用"]:::good MA --> Short SA --> Long["长期健康
半年后仍能用"]:::good CA --> Long classDef pillar fill:#f5ede0,stroke:#8b1538,color:#8b1538,font-weight:bold classDef good fill:#ecf3eb,stroke:#2f5d3a,color:#2f5d3a
没有 Curator,前两件事会让你的 Agent 慢慢被自己的"经验"压垮。 这就是为什么 Hermes 这么强调"create + recall + prune"是同一个闭环。
10.12本章带走的
- Agent 创建的 skill 会腐烂:过时、重复、未用、schema 漂移、注意力稀释。
- Curator 是后台图书管理员。 只动 agent-created skill;不删除只归档;pinned 免疫;用 auxiliary model 跑。
- 三态生命周期 active → stale → archived。reactivate 自动发生。
- 触发方式是 inactivity-based,不是 cron—— 双门槛 (interval_hours + min_idle_hours)。
- 状态机部分纯 Python 规则,不用 LLM。智能 review 才启动 auxiliary agent。
- 每次 review 前打快照到
.backups/,4 周保留。 - 用户用
hermes curator <verb>完全控制:pin / archive / restore / rollback。 - 三件事的协作:Skill 创建 + Memory 回忆 + Curator 修剪 = 完整学习闭环。
章末练习
- Easy 为什么 Curator 必须用 auxiliary client,不能用主 session 的 LLM?
- Easy 默认 stale = 30 天、archive = 90 天。如果你的 Agent 用户每天用一次,这些值合适吗? 如果一年用一次呢?讨论怎么调。
- Medium Curator 现在只动 agent-created。如果用户也希望 "user-authored" 的旧 skill 被自动归档怎么办?设计一个 opt-in 机制让用户允许 Curator 管理他自己的 skill。
- Medium Curator 用 LLM review 时,可能误判——把有用的 skill 标为 stale。 设计一个反馈循环:用户 reactivate 一个被错判的 skill 时,怎么让 Curator "学到"这个 skill 不该标 stale?
- Hard 把 Curator 的思想扩展到 memory:写一份"Memory Curator"设计文档。 考虑:什么数据该 forget?怎么 forget(向量库还是 SQLite)?什么时候 forget? 约 500 字。
-
Hard
读
agent/curator_backup.py。它用tarfile打快照。 讨论:如果用户在 Curator review 跑到一半时强制重启 Hermes,会发生什么? backup 完整吗?skill 状态一致吗?需要什么 transactional 保证?