上一篇里, Hermes 自己写了个技能------它把我那套日报流水线的经验提炼成了 ai-daily-digest,149 行 SKILL.md 落盘。现在我们来看看自己写一个skill。
技能库是你的第二大脑。但大脑需要你自己整理:机器能帮你造技能,不会替你决定哪些经验值得沉淀、哪些已经过时该清掉。本篇两件事:在 Hermes 里亲手写一个技能,把技能库治理起来。
先看一眼技能库现在的样子。
技能库现状:两个技能,一个机器写的,一个你写的
hermes skills list,当前的样子:
text
┌─────────────────────┬────────────┬────────┬───────┬─────────┐
│ Name │ Source │ Trust │ Status │
│ ai-daily-digest │ local │ local │ enabled │
│ daily-report-rescue │ local │ local │ enabled │
└─────────────────────┴────────────┴────────┴───────┴─────────┘
0 hub-installed, 0 builtin, 2 local --- 2 enabled, 0 disabled
两个本地技能,来源都是 local、信任级 local、状态 enabled,名字看着像一家人。它们走的是两条完全不同的诞生路径:
- ai-daily-digest :机器从经验里提炼。系列第 5 篇《学习闭环》里,我告诉 Hermes「我刚把收集 AI 动态写成了一套 Python 脚本」,它用 skill_manage 的 create 落了盘。它写的是泛化版------把 collect → curate → render 三段式流水线、优雅降级、去重键稳定这些程序化流程提炼出来,甚至建议了它自己想象的目录结构(items.jsonl、seen.json)。
- daily-report-rescue:人从踩坑里补全。这是我今天手写的,42 行。它不讲怎么搭流水线------那是 digest 的活------它只讲一件事:日报断更了怎么办。
这俩是互补的。机器擅长把做过的流程提炼成可复用的程序化知识;人手里握着机器没有的东西------踩坑的上下文。系列第 3 篇《第一个真实任务》里 RSS 全量存档抓到 1156 条、系列第 4 篇《记忆系统拆解》里 FTS5 中文检索不命中、推理模型输出混 reasoning_content......这些坑不是流程,是判断。机器不会主动写「90% 的断更其实是跨天去重在正常工作」,因为它没踩过,我也没告诉它。
两个技能为什么是这么个组合,得先搞懂技能在 Hermes 里到底怎么被用起来。
技能怎么被 Hermes 用起来:索引注入系统提示
先回答一个你可能想问的问题:我把 SKILL.md 丢进 %LOCALAPPDATA%\hermes\skills\automation\daily-report-rescue\,Hermes 是怎么知道它存在的?
答案不是每次会话都去扫盘------那太贵。答案是索引进系统提示。
源码在 agent/prompt_builder.py 的 build_skills_system_prompt():启动时把整个技能库渲染成一个紧凑索引,每个技能一行------名字 + description------注入系统提示的 stable 区。stable 区的意思是这部分提示相对固定,每轮对话都在,模型每轮都能看到它。

索引注入不是每次重新扫盘。它有两层缓存:
- 进程内 LRU:key 包含 skills 目录、工具集、平台、disabled 列表。同一进程里重复构建直接命中。
- 磁盘快照
.skills_prompt_snapshot.json:按 mtime/size 做 manifest 校验。重启后快照没变就复用,不用重新扫。
三层 fallback:快照 miss 才全量扫盘。这就是为什么技能落盘后立即可用------你加一个 SKILL.md,快照的 mtime/size 校验失效,下次构建自动重扫,不需要重启。
还有一个设计值得单独说:索引永不隐藏 。compact_categories 会把整类技能降级成「名字行」来省 token------automation 分类下如果挤十几个技能,全展开 description 太贵,就只留名字。但即便降级,每个技能名也始终可见、可被 skill_view / skills_list 加载。技能可能被压扁,但永远不会从索引里消失、不会被静默忽略。
目录约定一句话:%LOCALAPPDATA%\hermes\skills\<分类>\<技能名>\SKILL.md。配套工具三个:skills_list 列技能、skill_view 看 SKILL.md 全文、skill_manage 创建/编辑/打补丁/写文件。
现在看索引里每个技能到底带了什么。一个技能常驻索引的信息,只有名字和 description 两样。description 还被卡在 60 字符内------系列第 5 篇《学习闭环》讲过,/learn 的创作规范硬编码了这条,超过就被截断。为什么一个 60 字符的字段是技能的命根子?先按住,第 5 节你亲眼看到技能被加载一次,自己就懂了。
手写一个排障手册:daily-report-rescue 解剖
机制讲完了,动手。我手写的这个技能,动机很简单:digest 是机器写的泛化版,它管「怎么把日报搭起来、跑起来」,但真正干活的人知道------最常发生的不是「搭」,是「断」。
断更的时候你最需要什么?不是一份完整流水线文档,是一张快查表:先看哪个字段、怎么判断是不是故障、每个分支怎么办。这就是我写 daily-report-rescue 的定位。完整文件,42 行,逐字:
text
---
name: daily-report-rescue
description: 各家 AI 动态日报「断更自救」排障手册------collect 抓取 0 条 / 汇总报错 / 页面不更新时的分步处置。配合 ai-daily-digest 使用。
---
# 日报断更自救(rescue)
## 何时使用
- collect.py 输出「共 0 条」(跨天去重后全部重复,或数据源结构变了)
- curate.py 汇总报错 / 输出空 / 卡住
- report.html 内容没更新、看起来像「昨天的」
## 分步处置
### 1. 先看 items.json 的 filtered_reported
```
python -c "import json; d=json.load(open('items.json',encoding='utf-8')); print('count',d['count'],'| filtered',d.get('filtered_reported'))"
```
- filtered_reported > 0 且 count == 0:**不是故障**,是跨天去重正常工作(今天无新动态)。正常出页,digest 显示「今日无新动态」即可。
- filtered_reported == 0 且 count == 0:真的没抓到东西 → 进第 2 步。
### 2. 逐个源探测可达性
```
python -c "import urllib.request; [print(u, urllib.request.urlopen(u, timeout=15).status) for u in ['https://openai.com/news/rss.xml','https://blog.google/technology/ai/rss/','https://www.anthropic.com/news','https://www.kimi.com/blog/']]"
```
- 全挂:断网或源集体反爬。等恢复再跑,别改代码。
- 个别 403/404:该源结构变了,跑 collect.py 看 notes 里哪个源 failed。
- 源返回 200 但解析 0 条:HTML 选择器失效(JS SPA 化),需要换适配器或加浏览器抓取。
### 3. 汇总环节报错
- 看 curate.py 原始输出里有没有 `session_id:`(Hermes 正常返回的标志)。
- 没有 session_id:模型调用失败(超时/额度),重试 1-2 次;仍失败用「原样列表」降级出页。
- 有 session_id 但 digest.md 是空的:截取逻辑没匹配到「今日概览」,检查输出里正文标题变了没。
### 4. 页面没更新
- 确认 report.html 的 mtime 是今天。
- 确认 render.py 输出「已生成 report.html」。
- 确认 items.json 里 count 和生成时间对得上。
## 铁律
- **先诊断再动手**:90% 的「断更」是跨天去重在正常去重,不是故障。
- 改一行代码前,先跑第 1 步,把 filtered_reported 数字贴出来。
解剖一下,这个文件的结构和机器写的 digest 完全是两种思路:
- 何时使用:三行,全是触发判断。什么时候该打开这个技能,一眼看清。这是技能索引之外的第一道筛子------模型读完 description 决定要不要加载,加载后第一件事就是核对「现在是这种情况吗」。
- 分步处置:四步,从最便宜、最能区分「故障 vs 正常」的动作开始。第 1 步先看 filtered_reported------这一条就拦掉了九成的「断更」。每一步都带分支判断和可复制的命令。
- 铁律:两条,压在最后。第一条是这条技能的魂:先诊断再动手,90% 的断更不是故障。第二条是把诊断动作具象化------改代码前先贴数字。
手写和系列第 5 篇《学习闭环》里 Hermes 自动生成的差异,到这里就清楚了。机器写的 digest,是「程序化流程」的提炼------它没有逐字复刻 collect.py,而是把分阶段、优雅降级、去重键稳定、失败兜底这些流程骨架泛化出来。人写的 rescue,是「踩坑判断」的补全------我知道 filtered_reported 这个字段是先查的、session_id 是 Hermes 正常返回的标志、JS SPA 源是 HTML 选择器失效,这些全是系列第 3、4 篇真实踩过的坑,机器提炼流程时不会主动写这些。
验证技能被加载:一次真实会话
技能写好了,关键是验证它真的能被用起来。开一个全新会话,让 Hermes 自己去看。Prompt 逐字:
text
请用技能工具查看你有哪些技能(skills_list),然后用 skill_view 打开 daily-report-rescue 技能,告诉我它的核心步骤是什么。
回答(节选,逐字):
text
已查看。当前共有 2 个技能(都在 automation 分类)... daily-report-rescue ------ 日报「断更」时的排障手册 ... 第 1 步 · 先看 items.json 的 filtered_reported 命令:python -c "import json; d=json.load(open('items.json',encoding='utf-8')); print('count',d['count'],'| filtered',d.get('filtered_reported'))" ... 先诊断再动手:90% 的「断更」是跨天去重在正常工作,不是故障
三件事值得停下来看:
- 它调了
skills_list,看到了 2 个技能,都是 automation 分类------这正是指索引里的内容。 - 它用
skill_view打开了 daily-report-rescue,把核心步骤复述出来了:第 1 步先看 filtered_reported,铁律是先诊断再动手。复述基本准确------4 步定位它抓住了第 1 步,2 条铁律它抓住了最核心那条。 - 我全程没有喂它技能内容。它能看到、能打开、能复述,靠的全是索引 + skill_view 两条路。

description 为什么是命根子?
你看到模型是怎么找到 daily-report-rescue 的吗?索引里每个技能只有名字和 description 两行信息,模型全凭 description 判断「这是不是我该打开的那个」。「断更自救、排障手册、配合 ai-daily-digest 使用」------这句话把触发场景和配套关系都说清了,模型一眼锁定。反过来说,如果 description 写「AI 动态日报相关」这种空话,或者触发词埋在 60 字符截断线之后,技能就躺在索引里,模型看到了也不会去打开。
技能能不能被用起来,description 是唯一的门把手。这就是为什么 learn_prompt 把 ≤60 字符硬编码成创作标准:索引里它每轮常驻、它是模型唯一常驻的决策依据,写不好,整个技能就是死的。
技能库治理:命令面全景
技能多了,库就需要治理。Hermes 的技能治理命令,本机实测长这样:
text
hermes skills list # 列已装技能(名称/分类/来源/信任/状态)
hermes skills list-modified # 改了哪些内置技能 → No user-modified bundled skills --- everything tracks upstream.
hermes skills diff <name> # 单技能差异(需要 name 参数)
hermes skills search "news" # 从注册表搜技能 → 本机仅输出 "Searching for: news",无返回结果(注册表不可达或无线索)
hermes skills audit # 重扫 hub 技能
逐条说结论:
skills list:库的入口,上节已经见过。skills list-modified:查你改过哪些内置技能。本机输出No user-modified bundled skills --- everything tracks upstream.------内置技能一个都没动过,全部跟着上游走。这是干净的库该有的样子。skills diff <name>:单技能差异。注意它需要 name 参数,不带名字会报用法错误。我用它核对过 rescue 和上游没有可比对象(它是纯本地技能),diff 的价值主要在「内置技能被本地改过时,看跟上游差在哪」。skills search "news":从注册表搜技能。本机只输出了Searching for: news,然后没有返回结果。注册表不可达,或者根本没有线索,如实写------hub 技能生态本机没有接入。skills audit:重扫 hub 技能的安全审计。本机没有 hub 技能可扫。
加上命令面里还有 install / update(从 hub 安装、更新已装技能),完整的治理面是:list / list-modified / diff / audit / search / install / update。但本机一个 hub 技能都没装,install / update 没有可操作对象,行为标「待核实」------不装 hub,命令面就先收在本地这几条。
治理流程画成图,是这个形状:
治理的核心就一句话:让技能进得来、找得到、改得动、清得掉。进得来靠目录约定和索引自动收录,找得到靠 description,改得动靠 skill_manage patch,清得掉靠人定期过一遍 list。
人机共创:机器做流程提炼,人做经验补全
两个技能摆在一起,人机分工就显形了。对照着看:
| 维度 | ai-daily-digest(机器生成) | daily-report-rescue(人手写) |
|---|---|---|
| 诞生路径 | /learn 从经验提炼,skill_manage create | 人直接写 SKILL.md 落盘 |
| 内容结构 | 何时使用 → 流水线总览 → 三段详解 → 端到端调度 → 常见坑 7 条 → 新增源 checklist | 何时使用 → 分步处置 4 步 → 铁律 2 条 |
| 知识类型 | 程序化流程(分阶段 / 降级 / 去重 / 验证命令) | 踩坑判断(filtered_reported 先查 / session_id 定位 / 选择器失效) |
| 触发场景 | 「要搭 / 要扩展 / 要排障日报流水线」 | 「日报断更了」 |
| 视角 | 把脚本泛化成可复用流程 | 把踩过的坑固化成可执行步骤 |
人机分工画成图,两条线汇进同一个库:

机器做流程提炼:它没复刻 collect.py 的实现,而是把流程骨架抽出来,甚至抽象出通用版本------这就是技能区别于脚本备份的地方。人做经验补全:我知道 filtered_reported 是第 1 步该看的字段、session_id 是 Hermes 正常返回的标志、JS SPA 源是选择器失效,这些机器不知道,也不会主动写。
这个分工,跟我之前写过的一篇《自己动手编写 skills》是同一条思路。那篇讲怎么写一个能被 agent 反复调用的技能,核心结论是:反复照做才值得写,description 是激活开关。写作路径也一样------经验 → 程序化流程 → SKILL.md。差异在 Hermes 上放大了两点:
- 技能进系统提示索引,agent 能自己发现和加载,不靠人显式点名。这在 Claude Code 里要弱一些------技能更多是「被命令触发」。
- 多了
skill_manage工具,agent 自己能创建、改进技能,人机共创有了写入通道------机器提炼出流程,人补全经验,再让机器把补全 patch 回去。
人机共创不是「人和机器各写一半」,是机器做流程提炼、人做经验补全,然后互相喂。机器生成的 digest 是骨架,人写的 rescue 是血肉,curator 后台还会定期 review agent 创建的技能------这个机制系列第 5 篇《学习闭环》讲透了,本篇不重复。
载体项目落地:日报技能库成型
两个技能落进载体项目,日报这条线就有了一个完整的技能库:
- digest 管日常:每天跑 collect → curate → render,出日报。它把流水线流程固化成技能,任何时候要搭新的、加数据源、排查故障,agent 都能照着做。
- rescue 管断更:一旦日报「断更」,agent 有了一本排障手册。先看 filtered_reported、再探源可达性、再看汇总环节、最后看页面------四步走完,八成情况不是故障,不用动代码。
值得单说的是 digest 技能里那个「新增一个公司源(checklist)」节。它是可执行的操作知识------第 1 步确认有没有 RSS,第 2 步往 config/sources.json 加记录,第 3 步只跑该源验证,第 4 步跑 curate + render 确认分组,第 5 步连跑两天确认去重生效。五步全是动作,不是描述。这种知识机器写不出来,它来自系列第 3 篇实际加源时的操作顺序。技能库的每一节,都是某个真实时刻的沉淀。
到这里,日报技能库成型了:一个泛化流程 + 一个排障手册 + 一张操作 checklist,各自有触发场景、各自能按需加载。这就是技能和「文件夹里的一堆脚本」的区别------脚本是死的,技能是活的,能被 agent 发现、加载、复用、修改。
结论:技能库是第二大脑,需要治理
回到开头那两件事,收一遍账。
手写技能落地,路径完整可用:skills/<分类>/<名字>/SKILL.md 落盘 → 索引自动收录 → skill_view / skills_list 立即可用,快照 mtime/size 校验失效后自动重扫,不需要重启。技能库治理,命令面完整:list / list-modified / diff / audit / search / install / update。本机只有 local 技能,hub 生态未接入------search 无返回结果,install / update 没有可操作对象,如实写。
三个真实发现,是整篇的骨架:
- 索引进系统提示,是技能能被用起来的机制核心。技能库渲染成名字 + description 的紧凑索引,注入 stable 区,两层缓存(进程内 LRU + 磁盘快照)把重建成本压到最低,compact_categories 降级但永不隐藏。
- description ≤60 字符不是形式主义。它是索引里每个技能唯一的门把手,模型全凭它决定要不要打开。写不好,技能就是死的。
- 人机共创是技能库治理的正解。机器做流程提炼,人做经验补全------digest 是机器从经验里提炼的泛化流程,rescue 是人从踩坑里补全的排障手册,两条腿互补。
技能库是你的第二大脑,但大脑需要你自己整理。机器能帮你造技能、能按需加载、能后台维护,但它不会替你回答一个问题:这段经验还值不值得留。系列第 5 篇说技能会腐烂,这个判断在这篇被验证了------我的 description 就超了线,如果哪天触发词被截断,这个技能就废了。治理不是洁癖,是让技能库持续可用的日常。
下一篇,技能写好了,让它跑起来------多渠道推送,把日报推到你的飞书、Telegram 上。技能跑起来,才是第二大脑真正开始工作的时候。
你的技能库里,现在沉淀了什么?哪些经验是你反复手动在做、其实早该写成技能的?