【skill进化】Hermes-Agent Skill 自进化
Hermes-Agent Skill 自进化
总览(一句话结论)
hermes-agent 确实实现了 skill 自进化,且体系完整:它设计了“运行时本地 skill + 远程 Skills Hub”两层 skill 系统,通过“渐进式加载”让大语言模型(LLM)自主发现并加载 skill;同时靠“即时学习(background_review)、周期整理(curator)”两条自动回路,加上“人工提炼(/learn)”一条人工入口,驱动 skill 自进化。一个 skill 从创建、使用、修正、合并到归档的全生命周期,都有对应的机制支撑。
核心设计原则:自动回路仅执行“小修、标记、归档”这类可逆操作,不可逆动作(如合并重写、删除)需手动触发或由人工确认。
目录
- Skill 发现(Discovery)
- Skill 加载(Loading)—— 三种模式
- Skill 执行(Execution)—— 三个工具各做什么
- 问题发现与自进化——双回路 + 人工入口
- 一个 skill 的完整修改生命史(全景)
- 常见疑问解答
- 借鉴价值
一、Skill 发现(Discovery)
1.1 先分清两套系统(读代码最大的坑)
hermes-agent 的 skill 系统分“远程”和“本地”两套,作用和核心文件完全不同,先理清这一点能避免理解偏差:
| 系统 | 作用 | 关键文件 |
|---|---|---|
| Skills Hub(远程注册中心) | 相当于 skill 的“应用市场”,可通过 hermes skills search/install 命令,从 Anthropic、OpenAI 等第三方 marketplace 拉取现成 skill |
tools/skills_hub.py(核心逻辑)、skills/index-cache/*.json(HTTP 缓存)、scripts/build_skills_index.py(构建索引) |
| Runtime skills(本地运行时) | Agent 实际运行时能调用的 skill 集合,是真正参与业务逻辑的 skill | agent/skill_utils.py(发现/过滤)、agent/prompt_builder.py(构建清单)、tools/skills_tool.py(工具封装) |
⚠️ 注意:skills/index-cache/*.json(比如 anthropics_skills_skills_.json)只是 Hub 抓取远程仓库时的 HTTP 缓存(缓存有效期 1 小时,见 skills_hub.py:60),并不是 Agent 运行时的 skill 清单。Agent 运行时找 skill,只看本地文件系统。
1.2 运行时发现机制:扫描本地文件系统
Agent 启动后,会遍历 ~/.hermes/skills/ 目录(加上配置的 external_dirs 外部目录)下所有名为 SKILL.md 的文件,这个遍历动作由 iter_skill_index_files() 函数完成(见 agent/skill_utils.py)。
之后,skills_list 工具(tools/skills_tool.py:689,内部调用 _find_all_skills:604)会解析每个 SKILL.md 文件开头的 YAML 配置块(frontmatter),最终返回结构化数据:
# 示例返回格式
{
"skills": [
{"name": "arxiv", "description": "Search arXiv papers by keyword...", "category": "research"},
{"name": "test-driven-development", "description": "TDD: enforce RED-GREEN-REFACTOR...", "category": "software-development"}
],
"categories": ["research", "software-development"],
"count": 2
}
1.3 关键设计:匹配由 LLM 完成,过滤只做“剔除”不做“排序”
Python 代码只负责“过滤掉不该出现的 skill”,不会给 skill 打分、排序,也不会做语义匹配或向量检索——判断“哪个 skill 适配当前任务”,完全靠 LLM 读取过滤后的清单自主决策。
一个 skill 要进入最终清单,需依次通过五道“过滤门”,每道门只做“剔除”动作:
门 1:平台门控(硬兼容门)—— skill_matches_platform(skill_utils.py:163)
作用:判断 skill 是否适配当前操作系统(比如 macOS、Linux),是“硬门槛”——不兼容就直接剔除,没有例外。
- 读取
SKILL.mdfrontmatter 里的platforms:列表(比如[macos]、[macos, linux]); - 若字段缺省,默认兼容所有平台;
- 特殊处理:Android 上的 Termux(Linux 用户态),无论 Python 识别的
sys.platform是linux还是android,都会判定linux标签的 skill 兼容(见skill_utils.py:199),也支持显式的termux/android标签。
门 2:环境门控(软相关门,offer-time only)—— skill_matches_environment(skill_utils.py:268)
先解释核心名词:
- offer-time:指“向 LLM 推荐 skill 的时机”,也就是构建 skill 清单、自动补全列表、slash 命令列表的时机;
- 清单:给 LLM 看的所有可用 skill 汇总(系统提示里的压缩清单),是 LLM 选 skill 的核心依据;
- 自动补全列表:Agent 交互时,输入
/skill等命令后弹出的“可选 skill 列表”,方便快速选择; - slash 列表:以
/开头的快捷命令列表(比如/arxiv、/learn),本质是“skill 快捷调用入口”。
这道门的作用:根据 Agent 运行的环境,控制 skill 是否出现在“清单、自动补全、slash 列表”里(软门槛,可被绕开)。
- 读取 frontmatter 的
environments:列表,只识别三个固定标签(_KNOWN_ENVIRONMENTS,见skill_utils.py:217):
| 标签 | 匹配条件 | 检测方式 |
|---|---|---|
kanban |
看板调度器在运行(比如 dispatcher worker 启动,或 profile 开启了 kanban 工具集) | 检查 HERMES_KANBAN_TASK/HERMES_KANBAN_BOARD 环境变量,或调用 _profile_has_kanban_toolset()(skill_utils.py:239) |
docker |
Agent 运行在 Docker 容器内 | 调用 hermes_constants.is_container()(skill_utils.py:252) |
s6 |
Agent 运行在 s6-overlay 监管的容器里(Hermes Docker 镜像的 PID 1 是 s6) | 检查 /run/s6 或 /package/admin/s6-overlay 目录是否存在(skill_utils.py:260) |
- 关键规则:
- 这是“软门”——只控制“是否显示”,不影响显式加载:比如一个标记
environments: [kanban]的 skill,在非看板环境下不会出现在清单里,但手动执行skill_view(该skill名)仍能读取; - 语义为“或”:只要声明的环境有一个激活,就判定匹配;
- 未知标签“容错”:遇到不认识的标签,不会隐藏 skill(避免误屏蔽)。
- 这是“软门”——只控制“是否显示”,不影响显式加载:比如一个标记
代码注释(skill_utils.py:282)原话:“an explicit load is explicit consent”(显式加载就是显式同意),核心是“不限制用户主动选择,只优化默认推荐”。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
门 3:禁用门控 —— get_disabled_skill_names(skill_utils.py:353)
作用:读取用户配置,剔除显式禁用的 skill。
- 读取
config.yaml里的skills.disabled(全局禁用)和skills.platform_disabled.<平台>(按平台禁用); - 只要在禁用列表里,直接剔除,是用户/配置层的显式关闭。
门 4:条件激活(随 toolset 自适应)—— _skill_should_show(prompt_builder.py:1386)
作用:让 skill 清单随当前会话激活的工具集(toolset)自动调整,避免显示“用不了的 skill”。
- 读取
metadata.hermes下的四个条件字段:fallback_for_toolsets/fallback_for_tools:“备用 skill”——当它兜底的主工具集/工具可用时,隐藏这个 skill(有正经工具就不用备用的);requires_toolsets/requires_tools:“依赖 skill”——当它依赖的工具集/工具不可用时,隐藏这个 skill(依赖缺失,用不了)。
- 示例:若某 profile 没开浏览器工具,依赖浏览器的 skill 会自动从清单消失;若主工具齐全,备用的 fallback skill 不会显示。
- 注意:这道门是“硬过滤”(清单里绝对不显示),但
skill_view仍能绕过(只作用于清单构建,不影响显式读取)。
门 5:编码姿态降级(永不隐藏)—— compact_categories(prompt_builder.py:1608)
作用:在编码场景下,节省 token(模型上下文容量有限),但绝不隐藏 skill 名字。
- 非编码类 skill:从“名字 + 描述”降级为“仅名字”(比如
research: arxiv),砍掉描述; - 核心原则:“可以省描述,绝不藏名字”——因为 Agent 自建的 skill 是项目记忆,名字消失后,模型基本不会再想起它。
1.3 小结
五道过滤门的核心逻辑:
- 门 1(平台):真兼容,不兼容就剔除;
- 门 2(环境):软相关,不匹配就不推荐,但可手动选;
- 门 3(禁用):配置层,显式关就剔除;
- 门 4(条件):随工具集自适应,用不了就不显示;
- 门 5(降级):省 token,只砍描述不藏名。
所有过滤只做“剔除不该出现的”,“选哪个用”完全交给 LLM 决策。
1.4 SKILL.md frontmatter schema(实际解析的字段)
SKILL.md 开头的 YAML frontmatter 是 skill 的“配置说明书”,下面结合真实示例(skills/research/arxiv/SKILL.md),讲清楚每个字段的作用,重点解释易混淆的 metadata.hermes.config:
---
name: arxiv # 必填,小写连字符格式,最长64字符,全局唯一
description: "Search arXiv papers by keyword, author, category..." # 必填,核心说明
version: 1.0.0 # 可选,版本号
author: Hermes Agent # 可选,作者
license: MIT # 可选,开源协议
platforms: [linux, macos, windows] # 可选,门1平台门控(缺省=全平台)
environments: [kanban] # 可选,门2环境门控(仅kanban/docker/s6)
metadata:
hermes:
tags: [Research, Arxiv, Papers] # 可选,搜索标签
related_skills: [ocr-and-documents] # 可选,关联skill(skill_view时展示)
# 门4条件激活字段(四选零到多)
fallback_for_toolsets: [] # 主工具集可用时,隐藏此备用skill
requires_toolsets: [] # 依赖的工具集不可用时,隐藏此skill
fallback_for_tools: [] # 同上,针对单个工具
requires_tools: [] # 同上,针对单个工具
# 重点:metadata.hermes.config 字段
config:
- key: wiki.path # config.yaml 里的配置键(逻辑路径)
description: Path to the wiki # 给用户看的配置说明
default: "~/wiki" # 配置默认值
prompt: Wiki directory path # 引导用户配置的提示语
---
重点解释:metadata.hermes.config 的作用
这个字段的核心是“声明 skill 依赖的配置项,让模型不用手动查配置文件”,分两步理解:
- 声明依赖:告诉 Agent“这个 skill 要正常工作,需要用到
config.yaml里的wiki.path配置”; - 预加载注入:当用“预加载模式”(后文 2.4 模式 C)加载这个 skill 时,Agent 会自动读取
config.yaml里wiki.path的当前值,以[Skill config: wiki.path = ~/wiki]的格式注入到对话上下文里。
举个实际例子:
- 若
config.yaml里写了wiki: path: /Users/xxx/my-wiki; - 预加载
arxivskill 时,Agent 会自动在上下文里加一行:[Skill config: wiki.path = /Users/xxx/my-wiki]; - LLM 看到这行,就知道“用 arxiv skill 时,wiki 路径是这个”,不用再调用
read_file去翻config.yaml,也不用问用户“wiki 路径在哪”。
简单说:这个字段是“skill 和配置文件的桥梁”,既明确 skill 依赖哪些配置,又能在预加载时自动把配置值塞给模型,提升效率。
其他关键字段说明
- description 截断规则:
- 系统提示的压缩清单里:截到 60 字符(超长则前 57 字符 + “…”,见
skill_utils.py:716),目的是省 token; skills_list工具返回里:最长 1024 字符(MAX_DESCRIPTION_LENGTH,skills_tool.py:98),能看完整描述。
- 系统提示的压缩清单里:截到 60 字符(超长则前 57 字符 + “…”,见
- name 唯一性:
_create_skill创建时会跨所有目录查重(skill_manager_tool.py:724),重名直接拒绝,避免冲突; - 无 triggers 字段:触发条件写在
description里(比如 “Use this when searching academic papers”),靠 LLM 阅读理解,而非关键词匹配; - 配套文件不算独立 skill:
references/、templates/、scripts/、assets/子目录里的文件(比如references/api.md),不会被当作独立 skill,而是通过skill_view(name=arxiv, file_path=references/api.md)按需读取。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
二、Skill 加载(Loading)—— 三种模式
Skill 加载是 hermes-agent 的核心设计,核心逻辑是“渐进式加载”:不是“要么全加载,要么不加载”,而是“先给目录(压缩清单),再按需读详情(完整正文)”。下面从“何时加载、加载到哪、加载什么”三个维度,讲清楚三种加载模式。
2.1 核心逻辑:渐进式加载(Progressive Disclosure)
先明确两个核心概念:
- 内容分层:
- 压缩清单:只有“名字 + 60 字短描述”,轻量,每轮对话都在模型上下文里;
- 完整正文:整个
SKILL.md+ 配套文件(references/templates 等),量大,仅按需加载。
- 触发模式:分“常驻、按需、预加载”三种,覆盖“自动、模型主动、用户主动”三类场景。
2.2 模式 A:常驻模式(Resident)—— 自动加载,每轮都在
何时加载:会话启动时自动触发,且每轮对话都保留
Agent 启动一个新会话(比如执行 hermes chat)时,会立刻调用 build_skills_system_prompt(agent/prompt_builder.py:1417)构建压缩清单,之后每轮对话,这份清单都会留在系统提示里。
加载到哪:系统提示(System Prompt)
系统提示是 LLM 每轮都会读取的“基础指令集”,压缩清单会被注入到系统提示的固定位置(agent/system_prompt.py:282),比如:
# Available Skills
research:
- arxiv: Search arXiv papers by keyword, author, category...
software-development:
- test-driven-development: TDD: enforce RED-GREEN-REFACTOR...
系统提示里还会加强制指令:“回复前先扫一遍下面的 skill,如果有匹配的,必须用 skill_view(name) 加载它并遵循其指令。”
加载什么:所有通过过滤的 skill 的“名字 + 60 字描述”
加载内容是 1.3 节过滤后的 skill 清单,每个 skill 只保留名字和截断后的短描述,比如 arxiv skill 的描述只留前 60 字符。
优化:两层缓存保证效率
遍历目录构建清单可能慢,所以设计了两层缓存:
- 进程内 LRU 缓存:键包含“skills 目录、外部目录、可用工具集、平台、禁用列表”等,同进程内重复请求直接读缓存;
- 磁盘快照缓存:
~/.hermes/.skills_prompt_snapshot.json,靠每个SKILL.md的“修改时间 + 大小”校验——文件没改就读快照,改了才重扫。
衔接逻辑:当
skill_manage(修改 skill 的工具)成功修改 skill 后,会调用clear_skills_system_prompt_cache(clear_snapshot=True)(skill_manager_tool.py:1271)清空两层缓存,保证下一轮对话能看到最新清单。
2.3 模式 B:按需模式(On-demand)—— LLM 主动加载,对话中途触发
何时加载:LLM 看到常驻模式的压缩清单后,判断需要用某个 skill 时
比如用户问“怎么搜索 arXiv 论文”,LLM 扫到压缩清单里的 arxiv skill,会主动调用 skill_view(name=arxiv),触发按需加载。
加载到哪:工具调用结果(Tool Result)
skill_view 是一个工具函数(tools/skills_tool.py:864),调用后返回的完整内容,会以“工具执行结果”的形式注入到对话上下文里(不是系统提示),比如:
# Tool: skill_view
# Result:
content: [完整的 arxiv SKILL.md 内容]
tags: [Research, Arxiv, Papers]
related_skills: [ocr-and-documents]
linked_files:
references: [api.md, usage-notes.md]
templates: [search-query-template.txt]
readiness_status: ACTIVE
加载什么:单个 skill 的完整正文 + 配套文件清单
- 第一次调用
skill_view(name=arxiv):返回SKILL.md全文(预处理后) + 配套文件清单(references/templates 等); - 若需要读配套文件:再调用
skill_view(name=arxiv, file_path=references/api.md),读取具体文件内容。
额外动作:更新使用计数
每次 skill_view 成功调用,会执行 _skill_view_with_bump 函数,更新这个 skill 的“查看/使用计数”——这些计数是后续自进化的核心依据(比如 curator 会根据使用次数判断是否归档)。
预处理规则
加载的正文会做简单预处理:
${HERMES_SKILL_DIR}模板变量:自动替换为实际的 skill 目录路径;!`cmd`内联 shell 命令:仅当skills.inline_shell: true时展开(最长 4000 字符),比如!echo ~/wiki`` 会替换为实际路径。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
2.4 模式 C:预加载模式(Preload)—— 用户主动加载,会话启动/中途触发
何时加载:用户明确指定时,分两种场景
- 会话启动时:执行
hermes --skills arxiv,test-driven-development,指定要预加载的 skill; - 对话中途:输入 slash 命令(比如
/arxiv)或 bundle 命令(/bundle-name,见agent/skill_bundles.py)。
加载到哪:标记为 IMPORTANT 的对话消息
预加载的 skill 内容,会以 [IMPORTANT: Preloaded Skill - arxiv] 为标记,作为一条独立消息注入到对话上下文里(build_preloaded_skills_prompt,skill_commands.py:564),比如:
[IMPORTANT: Preloaded Skill - arxiv]
content: [完整的 arxiv SKILL.md 内容]
[Skill config: wiki.path = /Users/xxx/my-wiki]
加载什么:完整正文 + 配置块(核心区别)
和按需模式相比,预加载模式多了“配置块注入”:
- 先调用
skill_view读取完整正文(和模式 B 底层函数相同); - 再调用
_inject_skill_config(skill_commands.py:206),读取metadata.hermes.config声明的配置项,把config.yaml里的当前值以[Skill config: key = value]格式注入。
比如 arxiv skill 声明了 wiki.path,预加载时就会自动加 [Skill config: wiki.path = /Users/xxx/my-wiki],模型直接用,不用查配置。
2.5 三种模式对照表(清晰版)
| 维度 | 模式 A 常驻 | 模式 B 按需 | 模式 C 预加载 |
|---|---|---|---|
| 触发者 | 系统自动 | LLM 自主决策 | 用户(命令/参数) |
| 触发时机 | 会话启动,每轮保留 | 对话中途,LLM 调用 skill_view |
会话启动(–skills)/ 对话中途(/skill) |
| 加载位置 | 系统提示(全局) | 工具结果(当前轮) | 标记为 IMPORTANT 的消息(全局) |
| 加载内容 | 名字 + 60 字描述(压缩清单) | 完整正文 + 配套文件清单(预处理) | 完整正文 + 配套文件清单 + 配置块 |
| Token 代价 | 极小(每 skill 一行) | 中等(单个 skill 正文) | 较大(全程常驻,多 skill 更甚) |
| 核心函数 | build_skills_system_prompt |
skill_view |
skill_view + _inject_skill_config |
2.6 常见误解澄清
- 误解一:skill 太多会爆上下文
不会。模式 A 把每个 skill 压缩到一行,token 消耗极低;只有模式 B/C 会加载完整正文,且只加载“需要用的”,不会全量加载。 - 误解二:编码模式下非编码 skill 会被隐藏
不会。非编码类 skill 只会被降级为“仅名字”(比如research: arxiv),名字一定保留——因为名字消失后,模型就不会再想起这个 skill 了。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
三、Skill 执行(Execution)—— 三个工具各做什么
3.1 本质:指令注入改变模型行为
hermes-agent 的 skill 不是“可执行代码”(虽然能带 scripts/ 脚本),核心作用方式是“指令注入”:
skill_view读取SKILL.md全文,作为工具结果进入对话上下文;- 系统提示强制 LLM“加载了 skill 就必须遵循其指令”;
- LLM 后续的回答/操作,会被这份
SKILL.md的步骤、命令、注意事项约束。
简单说:skill 是“结构化的操作手册”,通过“塞进上下文”改变模型行为。
3.2 三个核心工具(分工明确)
整个 skill 系统对外只暴露三个工具,都属于 skills 工具集,分工是“找得到 → 读得到 → 改得动”:
工具 1:skills_list —— 浏览/检索(只读)
- 作用:列出当前可用的 skill,相当于“翻目录”;
- 返回内容:
{skills: [{name, description, category}], categories, count}(见 1.2 示例); - 关键区别:和系统提示的压缩清单同源,但 description 不截断(最长 1024 字符),还支持按
category过滤(比如skills_list(category=research)); - 引导逻辑:返回结果里会明确提示“Use skill_view(name) to load full content”,引导 LLM 下一步调用
skill_view。
工具 2:skill_view —— 读取完整内容(只读,触发遥测)
- 作用:加载 skill 完整正文 + 配套文件,是“渐进式加载”的核心;
- 两段式读取:先读
SKILL.md正文 + 配套文件清单,再按需读具体配套文件; - 遥测动作:每次成功调用,更新 skill 的“查看/使用计数”(
_skill_view_with_bump),为自进化提供数据。
工具 3:skill_manage —— 创建/修改/删除(写,自进化唯一写入口)
- 作用:所有 skill 的变更(创建、修改、删除、写配套文件等),都通过这个工具完成;
- 核心动作:
create / patch / edit / delete / write_file / remove_file(后文第五节详细讲); - 关键设计:工具描述里直接写“自进化触发规则”,比如“如果用了一个 skill 遇到没覆盖的问题,立刻 patch 它”,让 LLM 知道“什么时候该改 skill”;
- 后置动作:改完后清空清单缓存,保证模型下一轮能看到更新。
3.3 工具协作逻辑
skills_list 让 LLM 知道“有哪些 skill 可用” → skill_view 让 LLM 读到“具体 skill 的内容” → skill_manage 让 LLM/用户能“修改 skill 内容”。前两个工具支撑“加载”,后一个支撑“进化”。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
四、问题发现与自进化——双回路 + 人工入口
hermes-agent 的 skill 自进化,核心是“两条自动回路 + 一条人工入口”:自动回路负责“持续零碎学、定期整理”,人工入口负责“主动系统化提炼”。下面讲清楚每个回路的“执行时机、修改内容、核心名词”。
4.1 回路①:background_review(即时学习,每轮触发)
核心定位:每轮对话结束后,自动“复盘学习”的后台线程
何时执行:每个对话轮结束后立刻触发
具体逻辑:run_agent.py:1493 的 _spawn_background_review 函数会启动一个守护线程,fork 一个独立的 AIAgent 进程,重放本轮对话的快照(agent/background_review.py:839)——简单说,每和 Agent 聊一轮,这个复盘线程就跑一次。
执行主体:background_review 分身(独立的 AIAgent 进程)
这个分身和主对话完全隔离,权限极低,避免“学错东西影响主对话”。
核心目标:问自己“这一轮有什么值得固化成 skill 的东西?”
prompt 里明确要求:“大部分对话都至少能提炼出一个小改动,什么都不做是错过的学习机会”——也就是“尽量学,别空过”。
关注的“学习信号”(命中任一就动手)
- 用户纠正了表达方式(比如“别啰嗦,直接给步骤”)→ 改进对应 skill 的正文;
- 用户纠正了做法/步骤(比如“先查 API 再写代码,不是反过来”)→ 补充到 skill 的步骤里;
- 出现值得记的技巧/坑位(比如“这个命令要加 --force 参数”)→ 记到 skill 的 Pitfalls 章节;
- 本轮用的 skill 有问题(写错、漏步骤、过时)→ 立刻修改。
优先级(从上到下选,尽量不新建)
- 优先改“本轮用过的 skill”(比如本轮用了 arxiv skill,就改它);
- 没有就改“同类现成 skill”(比如改 research 分类下的其他 skill);
- 再没有就“给现成 skill 加配套文件”(比如把技巧写到
references/notes.md); - 实在没有才“新建 skill”(名字必须是“类级别”,比如
research-paper-search,不能是“修-PR-123”这种临时名字)。
改什么:只做“小修小补”,不做不可逆操作
- 允许改:patch 正文、加配套文件、标记问题;
- 禁止改:删除 skill、合并 skill、重写整篇 skill。
安全约束(避免闯祸)
- 与主对话隔离:改动直接写磁盘,不碰主对话的上下文/缓存;
- 工具白名单:只开放 memory + skill_manage(且仅允许小改),不能跑终端、发请求、改文件;
- 只改 Agent 自建的 skill:bundled(内置)、hub-installed(远程安装)的 skill 是“受保护的”,碰都不碰;
- 权限比前台低:比如 pinned(锁定)的 skill,前台能改内容,background_review 连改都不能改(后文第五节讲 pinned)。
一句话总结
background_review 是“每轮对话结束后,跑一个权限极低的后台学徒,从本轮对话里学小技巧,补到对应的 skill 里”。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
4.2 回路②:curator(周期整理,7 天一次)
核心定位:定期“整理 skill 库”的“图书管理员”,解决“skill 碎片化”问题
先解释核心名词
- cron:Linux/UNIX 系统的定时任务工具,这里是“定时执行”的泛指(但 hermes-agent 不用 cron,靠空闲触发);
- pin/pinned:“锁定”,给 skill 加“锁定标记”后,curator 不会删除/归档/合并这个 skill(但允许改内容);
- protected:“受保护的”,指 bundled(内置)、hub-installed(远程安装)的 skill,curator 只读不写;
- umbrella skill:“伞状 skill”,指“大类 skill”(比如
research),能容纳多个小技巧/小节,是 curator 整理的核心目标。
何时执行:满足两个条件才触发
- Agent 处于空闲状态(至少 2 小时没交互);
- 距离上次 curator 运行超过 7 天(
DEFAULT_INTERVAL_HOURS=24*7,见curator.py)。
触发函数:maybe_run_curator(curator.py:1958),由 cli.py:12691 调用——不是靠 cron 定时,而是“Agent 闲了且到时间了”才跑。
执行主体:fork 出来的辅助模型进程
和 background_review 一样,curator 跑在独立进程里,不影响主对话,且权限更低。
核心目标:合并碎片化 skill,整理成“伞状 skill”
prompt 里明确要求:扫描 skill 库,找“前缀簇”(比如 pr-*、security-*),判断“人类会写成 N 个独立 skill,还是 1 个带 N 小节的大 skill”——优先合并成大 skill。
具体做法(四种手法)
- 并进现成 umbrella:簇里有大类 skill,就把小 skill 的内容 patch 进去,然后归档小 skill;
- 新建 umbrella:簇里没大类 skill,就新建一个类级 skill,吸收所有小 skill,然后归档小 skill;
- 降级成配套文件:小 skill 有价值但没必要独立,就把内容挪到 umbrella 的
references//templates/里,归档原 skill; - 保留:仅当 skill 已是类级 umbrella,且合并不改善使用体验时才保留。
额外动作:自动状态流转(生命周期管理)
curator 会执行 apply_automatic_transitions(curator.py:307),按“最后活动时间”给 skill 打状态,且状态机是“确定性逻辑”(默认开启):
90天以上没动 → ARCHIVED(归档,目录挪进 .archive/)
30~90天没动 → STALE(陈旧,只改状态标记,内容不动)
30天内动过 → ACTIVE(活跃,正常使用)
状态规则:
- STALE → ACTIVE:只要 STALE 的 skill 被再用一次,自动变回 ACTIVE,倒计时重置;
- 新建不到 30 天的 STALE skill(use=0):也拉回 ACTIVE(“还没等到使用场景”);
- ARCHIVED 恢复:需手动执行
hermes curator restore <skill名>,不能自动恢复。
改什么:只做“合并、归档、标记状态”,不删内容
- 允许做:合并小 skill 到 umbrella、归档 skill(挪目录)、改状态标记;
- 禁止做:删除 skill(永不删除,只归档)、改 protected skill 的内容。
安全约束
- 只改 Agent 自建的 skill:protected skill 只读;
- 永不删除:归档可恢复,避免误删;
- pinned skill 跳过:锁定的 skill 不参与归档/合并;
- 支持 dry-run:执行
hermes curator --dry-run可预览修改,不动实际文件。
一句话总结
curator 是“7 天跑一次的图书管理员,把碎片化的小 skill 合并成大类 skill,长期不用的 skill 归档,且永不删除内容”。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
4.3 自进化的核心入口:skill_manage
background_review 和 curator 的所有修改,最终都通过 skill_manage 工具落盘(tools/skill_manager_tool.py:1200),它支持六个核心动作:create / patch / edit / delete / write_file / remove_file。
关键设计:工具描述自带“自进化规则”
工具的 description 里直接写:
“Create when: complex task succeeded (5+ calls), errors overcome, user-corrected approach worked… Update when: instructions stale/wrong, OS-specific failures, missing steps… If you used a skill and hit issues not covered by it, patch it immediately.”
翻译过来就是:“复杂任务成功了就创建 skill;指令过时/错了就更新;用 skill 遇到没覆盖的问题就立刻 patch”——把“什么时候该改 skill”直接教给 LLM。
改后记账(支撑生命周期管理)
每次成功修改,会记三笔账:
bump_patch(name):修改计数 +1(patch/edit/write_file/remove_file 触发);forget(name):硬 delete 时销账(但归档只改状态,不销账);mark_agent_created:background_review 创建的 skill 打标记(用户建的不打,但仍算“Agent 自建”)。
加上 skill_view 的 use_count(使用次数),每个 skill 有完整的“生命数据”:谁创建的、用了多少次、改了多少次、最后一次动是什么时候——这是 curator 决策的核心依据。
4.4 人工入口:/learn(人主导,单次提炼)
核心定位:用户主动触发,把“整块知识”做成规范的 skill
与自动回路的区别
- 自动回路:系统自动触发,碎片化学习、整理;
- /learn:用户主动按下,系统化提炼、规范创作。
核心逻辑:/learn 本身不干活,只是“给 Agent 派活”
/learn 没有独立的提炼引擎,只是调用 build_learn_prompt(learn_prompt.py:99)拼一段严格的提示词,让 Agent 用已有工具(read_file/search_files)抓素材,再用 skill_manage(create) 写 skill。
比如:
- 输入
/learn https://docs.python.org/3/library/os.html:Agent 会先调用web_extract抓取文档内容,再按规范写一个python-os-moduleskill; - 输入
/learn(空参):Agent 会提炼本轮对话的步骤,做成 skill。
核心价值:强制套用“严格的写作规范”
/learn 的提示词里包含 _AUTHORING_STANDARDS(learn_prompt.py:30),强制 skill 满足:
- description 必须 60 字符(因为系统提示清单只截 60 字,多了白写);
- 正文固定 8 段:标题+简介 → When to Use → Prerequisites → How to Run → Quick Reference → Procedure → Pitfalls → Verification;
- 必须用 Hermes 工具名(比如写
read_file不写 cat); - author 填
Hermes(避免泄露主机用户名); - 不准编造(命令/URL 必须和源材料一致)。
一句话总结
/learn 是“用户点一下,Agent 按严格规范把知识做成 skill”——自动回路负责“零碎学”,/learn 负责“系统建”。
4.5 问题发现的三个源头
| 发现时机 | 发现主体 | 发现内容 | 反馈速度 |
|---|---|---|---|
| 使用当场 | LLM 自身 | 这个 skill 没覆盖当前场景 → 立刻 patch | 即时 |
| 每轮结束 | background_review 分身 | 本轮对话有值得学的技巧 → 补到 skill 里 | 每轮一次 |
| 定期全局 | curator | skill 库碎片化/长期不用 → 合并/归档 | 7 天一次 |
| 【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】 |
五、一个 skill 的完整修改生命史(全景)
一个 skill 从创建到归档/删除,所有操作都收敛到 skill_manage 工具,核心区分维度是“谁在改(前台/后台)”和“改的粒度(小修/重写/归档)”。下面按“出生 → 打磨 → 收纳 → 合并 → 锁定/保护 → 删除/恢复”六站,讲清楚“为什么做、何时做、谁来做、怎么做、过哪些校验”。
第一站:出生 —— create(创建 skill)
为什么做:需要把“有用的知识/步骤”固化成 skill,方便后续复用
何时做:
- 用户主动:前台让模型创建(比如“帮我建一个查天气的 skill”),或用
/learn提炼知识; - 后台自动:background_review 复盘时,发现没有现成 skill 能承接本轮学到的内容;
- 周期整理:curator 合并时,发现某簇 skill 没有“伞状大类”,需要新建。
谁来做:
- 前台执行者:用户 + 主对话的 LLM;
- 后台执行者:background_review 分身 / curator 进程。
怎么做(核心流程 + 校验):
_create_skill 函数(skill_manager_tool.py:703)是创建的核心,流程如下:
- 参数校验:
- 名字:小写连字符、≤64 字符;
- 类目:必须是合法类目(比如 research、software-development);
- frontmatter:必须是合法 YAML;
- 内容:大小不超上限;
→ 任一不通过,直接返回错误,不创建。
- 唯一性查重:遍历所有 skill 目录(本地 + external),检查名字是否重复 → 重名拒绝。
- 原子创建:
- 先创建 skill 目录(比如
~/.hermes/skills/weather); - 用
_atomic_write_text写SKILL.md(先写临时文件,再 rename 成正式文件)→ 避免进程崩溃/断电导致文件残缺。
- 先创建 skill 目录(比如
- 安全扫描:扫描新写的内容是否有恶意模式 → 命中则删除整个目录,回滚创建。
- 打标记:只有 background_review 发起的创建,才执行
mark_agent_created打标(用户创建的不打,但仍算“Agent 自建”)。 - 清缓存:调用
clear_skills_system_prompt_cache,让下一轮对话能看到新 skill。
最终状态:新建的 skill 状态为 ACTIVE(活跃),30 天陈旧倒计时启动。
第二站:在用中打磨 —— patch / write_file / remove_file / edit(修改 skill)
为什么做:skill 内容有遗漏、错误,或需要补充配套文件(比如脚本、参考资料)
何时做:
- 前台:用户让模型改(比如“给天气 skill 加一个查未来 7 天的步骤”),或 LLM 用 skill 时发现问题,当场 patch;
- 后台:background_review 复盘时,发现 skill 有可优化的地方。
谁来做:
- 前台执行者:用户 + 主对话的 LLM;
- 后台执行者:background_review 分身(权限受限)。
核心操作详解:
操作 1:patch(局部小改,最常用)
- 作用:找到 skill 里的某段文字,替换成新内容(比如把“查今天天气”改成“查今天/明天天气”);
- 关键细节:
- 模糊匹配:调用
fuzzy_find_and_replace(skill_manager_tool.py:865),容忍空白、缩进、转义字符差异 → 避免“差一个空格就改失败”; - 匹配失败兜底:返回文件前 500 字预览 + 自纠提示 → 让模型看到实际内容后重试;
- 结构校验:改完后检查 frontmatter 是否完整(比如没少
---)→ 破坏结构则拒绝; - 支持配套文件:带
file_path参数可改references/等目录下的文件(无需校验 frontmatter)。
- 模糊匹配:调用
操作 2:write_file(加配套文件)
- 作用:往 skill 的
references//templates//scripts//assets/目录写新文件(比如给天气 skill 加scripts/query_weather.sh); - 注意:改完后通常要 patch
SKILL.md,加一行指向新文件(比如“参考脚本:scripts/query_weather.sh”),否则模型不知道有这个文件。
操作 3:remove_file(删配套文件)
- 作用:删除配套文件,常和 write_file 配合(比如替换旧脚本)。
操作 4:edit(整篇重写)
- 作用:用完整的新
SKILL.md替换旧的 → 仅用于结构性大改(比如重排所有章节); - 注意:小修优先用 patch,edit 容易引入回归(比如漏写旧步骤)。
所有修改的“安全网”:
- 写前备份:把原文存到内存(
original_content); - 原子写入:新内容写临时文件,再 rename;
- 安全扫描:命中恶意内容则用备份回滚;
- 后置动作:清缓存 +
bump_patch(修改计数 +1)。
关键守卫:前台 vs 后台的权限差
先解释“前台/后台”:
- 前台:用户直接和 Agent 交互的会话(主会话),有用户在场授权;
- 后台:background_review/curator 启动的独立进程,无人值守。
权限差异核心规则(_background_review_write_guard,skill_manager_tool.py:238):
| 操作 | 前台权限 | 后台(background_review)权限 |
|---|---|---|
| 修改 pinned skill | 允许(只挡删除,不挡编辑) | 禁止(连 patch/edit 都不行) |
| 修改 external_dirs 里的 skill | 允许 | 禁止(外部目录,只读) |
| 修改 protected skill(内置/远程安装) | 允许(用户授权) | 禁止(只读) |
| 修改 Agent 自建的非 pinned skill | 允许 | 允许 |
用户自建 skill 的修改规则:
用户手动创建的本地 skill,属于“Agent 自建 skill”:
- 前台:可随意改(patch/edit/delete 都可以);
- 后台:background_review 可改(非 pinned 情况下);
- curator:可合并/归档(非 pinned 情况下)。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
第三站:长期不用收纳 —— 状态流转 + 归档
为什么做:skill 长期不用,会占用清单空间,需要“收纳”(归档),但不删除内容
何时做:curator 运行时(7 天一次,Agent 空闲时),执行 apply_automatic_transitions 函数
谁来做:curator 进程(独立辅助模型)
怎么做:
- 判断最后活动时间:
- 90 天以上没动 → 标记为 ARCHIVED,目录挪进
.archive/(比如~/.hermes/skills/.archive/weather); - 30~90 天没动 → 标记为 STALE(仅改状态,不挪目录);
- 30 天内动过 → 保持 ACTIVE。
- 90 天以上没动 → 标记为 ARCHIVED,目录挪进
- 特殊规则:
- STALE skill 被再用一次 → 自动变回 ACTIVE,倒计时重置;
- 新建不到 30 天的 STALE skill(use=0)→ 拉回 ACTIVE。
- 校验:
- pinned skill:跳过归档/状态修改;
- protected skill:只读,不修改状态/目录。
注意:归档只改“状态 + 目录位置”,不改 SKILL.md 内容,可恢复。
第四站:碎片化合并 —— curator 合并
为什么做:多个小 skill 内容重叠,合并成“伞状大类 skill”,提升复用效率
何时做:curator 运行时(7 天一次),扫描到“前缀簇”skill(比如 pr-check、pr-merge、pr-review)
谁来做:curator 进程
怎么做:
- 判断合并方式:
- 有现成 umbrella → 把小 skill 内容 patch 进 umbrella,然后归档小 skill;
- 无现成 umbrella → 新建 umbrella skill,吸收小 skill,归档小 skill。
- 校验:
- pinned/protected skill:不参与合并;
- 合并后写账本:记录
consolidations: from→into(比如pr-check → pr-management),供后续追溯。
第五站:锁定/保护 —— pinned / protected / cron
先解释核心名词:
- pinned(锁定):用户手动给 skill 加“锁定标记”(比如
hermes curator pin weather); - protected(保护):系统给“内置(bundled)、远程安装(hub-installed)”的 skill 加的标记;
- cron:这里指“skill 配置里的
cron: yes”,表示这个 skill 用于定时任务,不能归档。
为什么做:
- pinned:防止重要 skill 被 curator 归档/合并(比如核心业务 skill);
- protected:防止系统级 skill 被误改/删除;
- cron:防止定时任务用的 skill 被归档,导致定时任务失效。
何时做:
- pinned:用户手动执行
hermes curator pin <skill名>时; - protected:skill 被安装/内置时,系统自动标记;
- cron:用户创建 skill 时,在 frontmatter 加
cron: yes。
谁来做:
- pinned:用户;
- protected:系统(安装/内置时);
- cron:用户(创建 skill 时配置)。
核心规则:
| 标记 | 能否改内容 | 能否删除 | 能否归档/合并 |
|---|---|---|---|
| pinned | 能(前台/后台都能 patch/edit) | 不能 | 不能 |
| protected | 前台能改(用户授权),后台不能改 | 前台能删(用户授权),后台不能删 | 不能 |
| cron: yes | 能 | 能 | 不能(不会被归档) |
| 【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】 |
第六站:删除/恢复 —— delete / restore
子站 6.1:delete(删除)
为什么做:skill 完全没用,需要彻底移除
何时做:用户前台主动发起(比如“删除 weather skill”)
谁来做:用户 + 主对话的 LLM
怎么做 + 校验:
- 权限校验:
- pinned skill:拒绝删除;
- protected skill:前台可删(用户授权),后台(background_review/curator)拒绝删;
- 执行删除:
- 调用
_delete_skill(skill_manager_tool.py:950),删除 skill 目录及所有文件; - 执行
forget(name),销除该 skill 的所有遥测记录(use_count、patch_count 等);
- 调用
- 清缓存:调用
clear_skills_system_prompt_cache,更新清单。
注意:curator 不会执行 delete,只会归档(ARCHIVED),delete 只能由用户前台发起。
子站 6.2:restore(恢复)
为什么做:误删/误归档的 skill,需要恢复使用
何时做:用户发现 skill 被删/归档后,手动发起
谁来做:用户
怎么做:
- 恢复归档的 skill:执行
hermes curator restore <skill名>→ 把.archive/里的目录挪回原位置,状态改回 ACTIVE; - 恢复删除的 skill:无直接恢复功能(需重新创建,或从备份恢复)。
六站总结
一个 skill 的生命周期,核心是“创建后持续打磨,长期不用归档,碎片化则合并,重要的锁定/保护,没用的手动删除”:
- 自动操作(background_review/curator):只做可逆的小修、归档、合并;
- 手动操作(用户/前台):负责不可逆的删除、锁定、重写;
- 所有操作都过“权限/结构校验”,避免误改/误删,且改后清缓存,保证模型能看到最新状态。
【我应该与skill有关的背景、操作执行者、执行内容写清楚,应该把复杂步骤用代码案例展示出来,应该把文章写得更通俗易懂】
六、常见疑问解答
1. skill 太多会爆模型上下文吗?
不会。常驻模式的压缩清单把每个 skill 压到一行,token 消耗极低;只有按需/预加载模式会加载完整正文,且只加载“需要用的”,不会全量加载。
2. 编码模式下非编码 skill 会被隐藏吗?
不会。非编码类 skill 只会被降级为“仅名字”(比如 research: arxiv),名字一定保留——因为名字消失后,模型就不会再想起这个 skill 了。
3. 用户自建的 skill 会被后台自动修改吗?
非 pinned 的用户自建 skill,background_review 可小修,curator 可合并/归档;pinned 的用户自建 skill,后台只能改内容,不能归档/合并/删除。
4. curator 靠什么触发?
不靠 cron 定时,靠“Agent 空闲(≥2 小时) + 距上次运行≥7 天”触发,避免占用业务资源。
5. /learn 和 background_review 有什么区别?
- /learn:用户主动触发,按严格规范提炼“整块知识”,创建/修改 skill;
- background_review:每轮自动触发,碎片化学习“本轮对话的小技巧”,只做小修。
七、借鉴价值
- 渐进式加载:先给“目录”再给“详情”,平衡 token 消耗和使用体验,值得大模型工具加载借鉴;
- 可逆自动回路:自动操作只做小修、归档,不可逆操作留手动入口,避免自动进化“闯祸”;
- 状态化生命周期:ACTIVE/STALE/ARCHIVED 三态管理,既收纳闲置 skill,又不丢失内容;
- 权限分层:前台/后台权限差异,无人值守的后台进程权限最小化,提升安全性;
- 规范驱动创作:/learn 的严格写作规范,保证 skill 质量和一致性,避免碎片化。
更多推荐

所有评论(0)