一次手动重复,让我决定用技能系统

周三下午,我正在处理当天的 Nginx 日志排查任务。跑完 `journalctl --since "today" -u nginx` 拿到 2 万行输出后,我按老规矩把状态码 5xx 的条目过滤出来,统计出 47 次 502、13 次 499,再手动打开 Grafana 对比同一段时间的 CPU 和内存曲线,最后把这些信息拼成一段日报发给同事。这套动作前前后后花了 40 分钟。第二天同样的时间点,又是同样的流程、同样的命令、同样的步骤,我看着终端上重复滚动的那串 `grep`,突然意识到一件事:我的经验明明已经固化成了一套固定动作,为什么每次还要手动执行一遍?

这就是我使用 Hermes 技能系统的出发点。Hermes 的安装方式很简单,`pip install hermes-agent` 之后,默认配置落在 `~/.hermes/config.yaml`,技能文件放在 `~/.hermes/skills/` 下,第一次启动时如果这个目录不存在,Hermes 会自动创建。我当时花了一个下午把日志分析流程写成了第一个技能,然后运行 `hermes skill run nginx-daily-check`,一条命令跑完所有分析步骤,输出结果直接贴到群里。从那天起,我意识到技能系统本质上是一种"经验固化"工具:把你已经踩过的坑、验证过的命令、确认过的流程保存下来,以后再遇到同一类任务,不需要回忆,只需要调用。

我把自己的第一个技能拆解成三部分:入口动作、执行步骤、结果输出。入口动作就是一条 shell 命令,执行步骤包含了过滤、统计、对比三个环节,结果输出则直接把最终报告送到 IM 工具。这三个部分在 Hermes 技能系统里对应三个概念:动作(Action)、流程(Workflow)、通知(Notify)。我后面使用过程中,发现这个模型基本能覆盖大多数运维和开发场景。所以这篇教程不打算讲抽象概念,就用我真实的技能文件来拆解,想看系统长什么样,直接看代码最直观。

技能目录与初始化流程

先看你机器上的目录结构。执行 `hermes skill list`,如果是从零开始,这个命令会输出空列表,同时在 `~/.hermes/skills/` 下生成基础框架。我建议你还是手动看一眼目录树,这样后面出问题了排查比较快。

~/.hermes/skills/
├── nginx-daily-check/
│   ├── skill.yaml
│   └── run.sh
└── backup-log/
    └── skill.yaml

我创建第一个技能时用的是 `hermes skill create nginx-daily-check`,这个命令会在 `skills` 目录下生成一个同名文件夹,并放一个 YAML 模板。模板里默认写了 `name`、`description`、`trigger` 和 `actions` 四个字段。在这里我要强调一个踩过的坑:模板里的 `description` 是给 Hermes 做语义匹配用的,不是给你自己看的备注,所以最好写清楚触发场景。我一开始图省事写了"nginx 相关检查",结果后面执行别的技能时,Hermes 也会把这个技能匹配进去,导致动作冲突。

技能目录创建好之后,下一步就是拆解你手动操作的步骤。以我那个 Nginx 日报技能为例,手动操作包含:拉取当天日志、统计 5xx 状态码数量、从后往前排序、汇总输出。我直接把这些步骤写成脚本,放到 `run.sh` 里。这个脚本有 40 多行,里面用到了 `awk`、`sort`、`uniq -c` 这些基础命令,最后把结果追加到一个当日报告文件。我并不建议把脚本内容直接塞进 `skill.yaml` 的 `actions` 里,因为 YAML 里写多行脚本会非常难维护,而且引号转义很容易出错。让 `skill.yaml` 只负责描述动作入口,脚本单独放文件,维护成本会低很多。

初始化阶段还有一件事值得做,就是给目录设置可执行权限。我遇到过一种情况:`run.sh` 没有 `x` 权限,导致技能执行时报 `Permission denied`,我排查了半天才发现。之后我在 `skill.yaml` 的 `actions` 里补上了一个 `pre_check`,在脚本执行前检查文件权限,如果不对就直接给出提示并退出。这种细节看起来小,但在自动化场景里非常影响体验。毕竟技能系统最大的价值是"无人值守",如果每个技能都要人盯着跑,那就失去意义了。

编写 skill.yaml:把动作写清楚

技能系统的核心配置就写在 `skill.yaml` 文件里,这个文件写得好不好,直接影响技能能不能稳定跑起来。我先展示一个浓缩版的真实文件。

name: mysql-slow-check
description: 分析 MySQL 慢查询日志,输出最耗时的 Top 10 SQL
trigger:
  type: manual
  keywords:
    - "慢查询"
    - "mysql"
    - "slow log"
actions:
  - type: shell
    command: "sh ~/.hermes/skills/mysql-slow-check/analyze.sh"
    timeout: 120
  - type: notify
    plugin: telegram
    channel: "@ops-alert"
    message: "慢查询分析完成,共发现 {{ result.count }} 条慢 SQL"
env:
  MYSQL_HOST: "127.0.0.1"
  MYSQL_USER: "monitor"

这里每个字段都有讲究。`name` 是唯一标识,至少我用的时候不建议起太通用的名字,不然语义匹配会乱。`trigger` 里我配置了 `type: manual`,意思是这个技能只手动触发,不会因为消息里的关键词自动执行。想自动触发的话,把 `type` 改成 `auto`,然后配 `keywords` 列表,Hermes 会按关键词权重匹配。我经过多次调整,得出来一个小经验:关键词数量控制在 3 到 5 个之间效果最好,太少容易误命中,太多反而漏掉。

`actions` 是执行序列,按顺序从上到下执行。我在这个技能里配了两个动作:第一个是 shell 动作,执行分析脚本;第二个是 notify 动作,跑完把结果发到 Telegram 频道。`timeout: 120` 这个参数我调过一次,最初默认值是 30 秒,我的 MySQL 慢日志文件 2GB,第一次执行超时,导致整个技能直接报错中断。后来我把 timeout 改成 120 秒,问题解决。这里补充一个细节:Hermes 的动作序列是同步执行的,前一个动作如果没有返回成功,后面的动作不会执行。所以如果你的脚本需要很长时间,一定要把 timeout 值设得足够大。

还要注意 `env` 字段。我在配置里声明了 `MYSQL_HOST` 和 `MYSQL_USER`,这几个环境变量在执行时会注入到脚本进程里。这样做的好处是,脚本本身不硬编码数据库地址。一个踩坑案例:我一开始把数据库密码写在脚本里,结果有一次给同事演示技能,不小心把脚本内容贴到了群里,密码当场泄露。之后我改成从环境变量读取,并且把敏感信息存到 Hermes 的 memory 模块里。这个建议很实用:不要在任何技能脚本里明文存放凭据。

触发方式:手动、定时与关键词匹配

Hermes 技能系统支持的触发方式有三种:手动执行、定时任务、关键词自动匹配。三种触发各有各的适用场景,我依次说我用过的真实情况。

手动触发最直接,命令就是 `hermes skill run mysql-slow-check`,我要看慢日志的时候敲一下,跑完结果自动发到 Telegram。这种方式适合"偶尔想起来才做"的任务,比如排查数据库性能、磁盘占用分析、临时拉取某个报表。手动触发的好处是可控,不会因为误匹配执行不该执行的技能。但它的缺点也明显:必须有人记着去跑。我是不会记这种事情的,所以更多技能我交给定时任务。

定时触发用 `hermes cron create` 命令创建,比如我的一条命令:`hermes cron create --name nightly-backup --skill backup-log --at "0 2 * * *"`。这条命令的意思是每天凌晨 2 点执行 `backup-log` 技能。需要注意的地方是 cron 表达式遵循标准五段格式,但我第一次部署时踩过一个时区坑:Hermes 默认按照系统时区执行,我的服务器是 UTC 时区,我定的 `0 9 * * *` 本意是早上 9 点跑,结果实际是北京时间下午 5 点才执行。排查过程也比较曲折,最后在 `config.yaml` 里加了 `timezone: Asia/Shanghai` 才解决。这个问题很多人都会遇到,强烈建议在配置技能前先确认时区。

关键词自动匹配是最有"智能感"的触发方式。它的机制是把用户输入的内容与技能描述和关键词做语义匹配,命中后自动执行。我写过一个 `deploy-frontend` 技能,关键词设置了"部署前端""前端发布""deploy"三个。第一次测试时,我在聊天里跟同事说"部署环境可能有问题",结果机器直接执行了部署技能,幸好我之前设置了 `require_confirm` 参数,所有执行动作之前必须确认。这个参数在 `skill.yaml` 里配置为 `require_confirm: true`,否则误触发的后果可能很严重。如果你要批量操作生产环境,强烈建议保留这个确认机制。

用 memory 命令沉淀上下文

技能文件解决的是"动作步骤"的复用,但现实中很多任务还依赖"上下文信息",比如服务器 IP、账号 ID、之前处理过的问题记录、某个服务的启动方式。这些信息如果每次都要往技能脚本里填,那自动化就没意义了。Hermes 专门有 `memory` 命令来管理这类上下文信息。

我举一个实战例子。我维护的三台应用服务器的部署目录各不相同,第一台在 `/data/app1/`,第二台在 `/srv/www/`,第三台挂在 `/opt/service/`。最初我写部署技能时,把这些路径硬编码进脚本里,结果每次新增服务器就要改一次技能文件,维护得很痛苦。后来我用 `memory` 把这些变量存起来:

hermes memory save server_1_dir="/data/app1/"
hermes memory save server_2_dir="/srv/www/"
hermes memory save server_3_dir="/opt/service/"

然后在技能脚本里通过 `{{ memory.server_1_dir }}` 这样的模板语法引用。改路径的时候只需更新 memory,不需要动技能文件。

除了存简单的键值对,`memory` 还支持带标签和权重的存储。比如我对某个线上故障的事件记录,可以执行 `hermes memory save incident_20250315="负载均衡后端频繁连接重置" --tags "故障,nginx,network"`,这样以后搜"负载均衡故障"的时候,这条记录就能被检索出来。我在配置文件中看到 `memory` 的数据默认存放在 `~/.hermes/memory.json`,手动编辑也可以,但用命令更安全——至少格式不会写错。

关于 memory 还有一个我踩过的坑:查找时尽量用精准的 key。有一次我在技能里用 `{{ memory.nginx_path }}` 引用一个变量,那时候还没保存过这个 key,Hermes 不会报错,只是替换成空字符串。我的脚本拿到空的路径变量,直接去删了一个不确定的目录,差点搞出大事。所以我现在每个技能脚本开头会做一次变量存在性校验,不存在就直接退出并提示。这个习惯后面帮我避免了不少低级错误。

定时任务与参数化技能

定时任务是技能系统里利用率最高的功能。我通过 `hermes cron create` 把大量重复性的巡检工作自动化了,包括每天早上 9 点检查服务健康状态、每周五下午生成周报、每天凌晨把 MySQL 慢查询日志汇总后发送到分析群。如果纯粹靠记忆去执行这些任务,我至少需要浪费 30% 的精力在琐事上。

创建定时任务的完整命令格式是这样:`hermes cron create --name weekly-report --skill generate-report --at "0 17 * * 5"`。`--name` 是任务名,`--skill` 指定要执行的技能,`--at` 后面跟 cron 表达式。这里有一段实操经验想分享:技能可以被参数化,同一个技能适配不同任务场景。比如我的 `generate-report` 技能接收一个 `--period` 参数,值为 `daily` 或 `weekly`,通过 `hermes skill run generate-report --param period=weekly` 调用。这样一份脚本就能复用两套逻辑,避免写两个几乎一样的技能。

参数化还可以结合 memory 实现更细粒度的控制。我之前在配置环境变量时发现,不同服务器上执行同一技能,需要的 `JAVA_HOME` 路径不同。后来我在 memory 里给每台服务器保存了各自的 `java_home` 值,然后技能脚本启动时通过 `{{ memory.java_home }}` 读取。为了让脚本知道当前是哪台机器,我让脚本调用 `hostname` 命令,再根据主机名从 memory 里取对应配置。这套方案上线后,部署失败率从原来的 14% 降到了 3% 以下,主要原因是很多人为参数错误消失了。

定时任务还有一个我一开始忽略的地方:日志输出。`hermes cron create` 创建的任务执行完,如果技能本身没有配置通知,输出只会写到本地日志文件。我有一次排查任务有没有正常执行,发现 `~/.hermes/logs/cron.log` 里完全没有当天记录,还以为是 cron 坏了。后来发现是任务创建成功,但服务器重启后 Hermes 进程没启动,cron 自然没跑。解决办法是在 `config.yaml` 里设置 `auto_start: true`,让 Hermes 随系统启动自动运行。这个细节看似简单,却是整个定时任务系统可靠运行的基础。

💬 你用过哪些AI工具?

看到这里,相信你已经把 Hermes 的技能系统上手了。不过说真的,AI 工具的世界变化太快,我从最初用命令行工具到现在把 Hermes 部署在工作流里,中间折腾了不少时间,也踩了不少坑。我想知道你们平时都在用哪些 AI 工具?是像 Hermes 这样的自动化框架,还是写文案用的对话模型,或者是别的什么冷门工具?

我自己最近也在收集各种 AI 工具的使用体验,特别是一些开源项目和小众工具,往往比大厂产品更灵活。如果你有觉得好用的,或者正在纠结要不要入坑的,不妨去 AI House 排行榜(aibunkhouse.com/rankings/)看看最新排名,上面有大量真实用户给各种模型打分、写评价。你可以先看看大家都在用什么,再决定要不要给某个模型投一票。说不定你会发现一个比现在工具效率高一倍的选择。

也欢迎你分享自己的踩坑经历——遇到哪些工具坑、或者做过什么奇怪的操作,评论区聊起来。咱们下次继续折腾。

常见问题 / FAQ

一次手动重复,让我决定用技能系统

周三下午,我正在处理当天的 Nginx 日志排查任务。跑完 `journalctl --since "today" -u nginx` 拿到 2 万行输出后,我按老规矩把状态码 5xx 的条目过滤出来,统计出 47 次 502、13 次 499,再手动打开 Grafana 对比同一段时间的 CPU 和内存曲线,最后把这些信息拼成一段日报发给同事。这套动作前前后后花了 40 分钟。第二天同样的时间...

技能目录与初始化流程

先看你机器上的目录结构。执行 `hermes skill list`,如果是从零开始,这个命令会输出空列表,同时在 `~/.hermes/skills/` 下生成基础框架。我建议你还是手动看一眼目录树,这样后面出问题了排查比较快。 ~/.hermes/skills/ ├── nginx-daily-check/ │ ├── skill.yaml │ └── run.sh └── back...

编写 skill.yaml:把动作写清楚

技能系统的核心配置就写在 `skill.yaml` 文件里,这个文件写得好不好,直接影响技能能不能稳定跑起来。我先展示一个浓缩版的真实文件。 name: mysql-slow-check description: 分析 MySQL 慢查询日志,输出最耗时的 Top 10 SQL trigger: type: manual keywords: - "慢查询" - "mysq...

触发方式:手动、定时与关键词匹配

Hermes 技能系统支持的触发方式有三种:手动执行、定时任务、关键词自动匹配。三种触发各有各的适用场景,我依次说我用过的真实情况。 手动触发最直接,命令就是 `hermes skill run mysql-slow-check`,我要看慢日志的时候敲一下,跑完结果自动发到 Telegram。这种方式适合"偶尔想起来才做"的任务,比如排查数据库性能、磁盘占用分析、临时拉取某个报表。手动触发的好处...

用 memory 命令沉淀上下文

技能文件解决的是"动作步骤"的复用,但现实中很多任务还依赖"上下文信息",比如服务器 IP、账号 ID、之前处理过的问题记录、某个服务的启动方式。这些信息如果每次都要往技能脚本里填,那自动化就没意义了。Hermes 专门有 `memory` 命令来管理这类上下文信息。 我举一个实战例子。我维护的三台应用服务器的部署目录各不相同,第一台在 `/data/app1/`,第二台在 `/srv/www/...