Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • OpenClaw
  • Hermes Agent
  • Claude Code
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • OpenClaw

  • Hermes-Agent

    • Hermes Agent 概述
    • Hermes Agent 实战 01|架构总览:用一个 Agent 管一整个机房
    • Hermes Agent 实战 02|多 Profile 与超管模型:一个 Agent 安全地管十几台机器
    • Hermes Agent 实战 03|Gateway 运维:systemd、裸进程,和一个 Telegram token 撞车
    • Hermes Agent 实战 04|模型路由实战:config 全解、thinking 注入与 401/503 源码级根因
    • Hermes Agent 实战 05|技能工程:写、去重、pin,与每周自我审计
      • 1. 技能长什么样
      • 2. 真正的问题:技能会失控增殖
      • 3. 单一真相源:.curated_manifest.json
        • 3.1 一个 pin 的暗坑
      • 4. 让它每周自己审计自己
        • 4.1 三个把我教育了的坑
        • 4.2 正确的形态:一个自给自足的脚本
      • 5. 接到 cron 上
        • 5.1 完整的 jobs.json 条目
        • 5.2 危险命令拦截:为什么 cron 里 python3 -c '...' 必失败
      • 6. 可复现 checklist
      • 7. Agent 可直接解析的元数据块
      • 7. Agent 可直接解析的元数据块
    • Hermes Agent 实战 06|让 Agent 自己上班:cron 驱动的无人值守巡检
    • Hermes Agent 实战 07|数据库实战:可直接抄走的 SQL Server 巡检脚本
    • Hermes Agent 实战 08|量化交易助手:持仓盈亏、网格减仓,与「没开单」的真相
    • Hermes Agent 实战 09|接入 OpenWebUI:把每个 Profile 暴露成一个「模型」
    • Hermes Agent 实战 10|升级不翻车,与给上游提 PR:一个被冲掉三次的修复
    • Hermes Agent 实战 11|踩坑合集:当「手动 rm」从来不是真正的修复
    • Hermes Agent 实战 12|工具链外延:用 AI 运维 AI,与这个系列的诞生
    • Hermes Agent 实战 13|旗舰篇:让 Agent 从零部署并灾难恢复一个 7 节点生产集群
  • Claude-Code

  • AI-Agent
  • Hermes-Agent
Carry の Blog
2026-06-22
目录

Hermes Agent 实战 05|技能工程:写、去重、pin,与每周自我审计原创

# 技能工程:写、去重、pin,与每周自我审计

系列第 05 篇。技能(Skill)是 agent 能力的「说明书」——把一组工具调用打包成「怎么做某件事」。但技能会失控增殖:agent 自己造、curator 自动囤,几个月后你会有一堆重复、过期、谁也不敢删的技能。这篇讲我怎么把一个 Profile 砍到 9 个精选技能,并让它每周自己审计自己。文末给可照抄的技能模板和审计脚本骨架。

# 1. 技能长什么样

一个技能就是磁盘上的一个目录,核心是 SKILL.md:

~/.hermes/skills/<category>/<name>/
├── SKILL.md              # 元数据 + 「什么时候用、怎么做」
└── references/           # 可选:脚本、长文档、模板
1
2
3

SKILL.md 头部是 frontmatter(name/description/触发条件),正文是给 agent 看的操作步骤。description 写得好不好,直接决定这个技能会不会在该用的时候被检索到——它是 agent 决定「相关性」的唯一依据。

# 2. 真正的问题:技能会失控增殖

Hermes 有个 curator(管理员)会自动整理技能,但它的倾向是不断累积。再加上 agent 在对话里会即兴造技能,跑几个月后我的某个 DBA Profile 出现了:重复的技能(daily-report-summary 是 dba-daily-report 的山寨版,用了 0 次)、内容已被别处吸收的孤儿技能、以及**三份互相打架的「精选清单」**散落在不同 prompt 和文档里。

我的治理原则:

一个 Profile 只保留一小撮「精选技能」,其余统统归档;精选清单只有一个权威来源。

那个 DBA Profile 最后被砍到 9 个:3 个锚点(监控、日志检索、出站机器人)、3 个工作流(巡检、日报、多机指标对比)、1 个能力(Grafana 审查)、1 个文档中枢、1 条规则(技能合并规则本身)。

# 3. 单一真相源:.curated_manifest.json

「精选清单」散落多份是万恶之源。解决办法是立一个唯一权威文件,别的地方一律不许硬编码清单:

~/.hermes/profiles/<name>/skills/.curated_manifest.json   ← 唯一真相源
1

增减一个精选技能的正确顺序:先改 manifest,再 curator pin / archive:

# 1. 编辑 .curated_manifest.json,加/删条目
# 2. 再同步 pin/archive
hermes curator pin <skill>     --p<PASSWORD> <name>
hermes curator archive <skill> --p<PASSWORD> <name>
1
2
3
4

# 3.1 一个 pin 的暗坑

curator pin 这个 CLI 跟随当前激活的 Profile(粘性 active_profile),不是跟随你 --profile 的本意。如果你的工作 Profile 是 default,想 pin 到 default,curator pin 可能 pin 错地方。绕过办法:直接编辑 skills/.usage.json,把对应技能的 pinned 置 true。

另外提醒:.usage.json 里可能残留一堆孤儿记录(磁盘目录早没了,记录还在)。审计的真相是「磁盘 + manifest」,永远别信 .usage.json 的 active 列表。

# 4. 让它每周自己审计自己

人工审计技能库不可持续。我让 Profile 每周自动跑一次审计并把结果发到 Telegram。但这里踩了三个深坑,每个都值得你记住——它们对任何无人值守的 agent 定时任务都成立:

# 4.1 三个把我教育了的坑

  1. cron 跑的时候没有人能点「批准」——所以 agent 在 cron 里临时执行的 python3 -c '...' / bash -c / execute_code 会被危险命令防护直接拦掉。 → 必须跑一个已提交的文件(python3 audit.py),而不是内联命令。
  2. 「最终回复」不等于「送达」——这个 Profile 的任务是 deliver: local、而且它没有 Telegram 平台,于是 agent 把心跳当成「最终响应」吐出来,根本到不了 Telegram。 → 送达必须由脚本自己显式做(脚本内 urllib 直接调 Telegram API),不能指望 agent。
  3. agent 被要求 curl,它却跳过了——因为「把这行命令回显出来」对模型来说感觉就像已经发了。 → 同上,把送达逻辑焊死在脚本里,不交给模型自由发挥。

# 4.2 正确的形态:一个自给自足的脚本

最终设计是一个已提交、自包含、只读的脚本,cron 的 prompt 简化成一句「运行这个脚本,它会自己发送」:

# references/skill_audit.py(骨架)
import json, os, urllib.request, glob

# 1) 对账:manifest ∩ 磁盘目录(忽略 usage.json 的孤儿)
manifest = json.load(open(".curated_manifest.json"))
on_disk  = {os.path.basename(os.path.dirname(p))
            for p in glob.glob("*/*/SKILL.md")}
curated  = set(manifest["skills"])
missing  = curated - on_disk      # 清单里有、磁盘没有
extra    = on_disk - curated      # 磁盘有、清单没收录

# 2) 自己把结果发出去(读 .env 里的 token,别等 agent)
def send(text):
    token = os.environ["TELEBOT_TOKEN"]; chat = os.environ["OWNER_CHAT_ID"]
    data  = urllib.parse.urlencode({"chat_id": chat, "text": text}).encode()
    urllib.request.urlopen(
        f"https://api.telegram.org/bot{token}/sendMessage", data=data)

# 3) 永远发一行,哪怕「干净」——这样「没消息」就能等于「任务死了」
status = "✅ skill-audit clean" if not (missing or extra) else \
         f"⚠️ missing={sorted(missing)} extra={sorted(extra)}"
send(f"{status}(curated {len(curated)})")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

三个设计要点,每条都对应上面一个坑:

  • 永远发心跳(哪怕 clean)——这样「群里没动静」就是一个可观测的告警信号(任务挂了),而不是「大概没事吧」。
  • 送达在脚本里,不在模型嘴里。
  • 只读——审计绝不改东西,铁律。

# 5. 接到 cron 上

把它挂成每周一早上 5 点(内置 curator 本来就每 7 天自动整理一次,每周审计足矣):

# cron 表达式 0 5 * * 1 ,主机时钟 CST/+08:00
# 编辑 ~/.hermes/profiles/<name>/cron/jobs.json 后,重启该 Profile 网关
systemctl --user restart hermes-gateway-<name>.service
1
2
3

注意:改 cron 的 jobs.json 需要重启对应网关;但改技能本身不需要重启。别搞混。

# 5.1 完整的 jobs.json 条目

这是挂上去的 skill_audit 那一条(去掉了无关字段):

{
  "name": "weekly-skill-audit",
  "schedule": "0 5 * * 1",
  "prompt": "运行 references/skill_audit.py,它会自己把结果发到 Telegram。不要内联 python3 -c。",
  "deliver": "local",
  "tools": ["execute_command"],
  "timeout_sec": 120
}
1
2
3
4
5
6
7
8

三个看着不起眼但少了会爆的字段:

  • deliver: local — 这个 Profile 没接 Telegram,agent 回复的心跳只写到 state.db,根本不到你手机。所以送达必须由脚本自己调 Telegram API。
  • tools: ["execute_command"] — 把可用工具收敛到最小集合。cron 上下文里给 40+ 工具纯属作死。
  • timeout_sec: 120 — 没有它,agent 会拿满 max_iterations=90 轮,把 cron 进程拖到天荒地老。

# 5.2 危险命令拦截:为什么 cron 里 python3 -c '...' 必失败

Hermes 的「危险命令防护」是命令前缀+正则匹配,命中即拒:

危险模式 拦截理由
python3 -c '...' / python -c 单行注入,agent 经常塞随机字符串
bash -c '...' 同上
execute_code {...} 工具调用 不留审计痕迹
eval / exec 同上
rm -rf / / mkfs / dd if= 破坏性
curl ... \| bash 远程代码执行

绕过的正确办法:把命令写成脚本提交到仓库,cron 只调文件路径。错误的办法(看到群里有人这么干):给 Profile 加一个白名单 unsafe-commands,绕过防护——这等于把整个防护体系卸了。

# 6. 可复现 checklist

  1. 每个技能一个 SKILL.md,description 写清楚「何时用」——这是被检索到的关键。
  2. 立一个 manifest 当唯一真相源,禁止在 prompt/文档里散落第二份清单。
  3. 增减技能先改 manifest,再 pin/archive;pin 错 Profile 时直接改 .usage.json。
  4. 审计对账「磁盘 + manifest」,无视 usage.json 孤儿。
  5. 无人值守任务三铁律:跑提交过的文件(不是内联命令)、送达焊死在脚本里、永远发心跳。

# 7. Agent 可直接解析的元数据块

{
  "_meta": {
    "doc_version": "2025-01-15",
    "article_id": "hermes-05-skills",
    "profile_context": "any",
    "executable_examples": true,
    "dependencies": ["hermes-cli", "curator"],
    "estimated_setup_time": "30min"
  },
  "quick_start": {
    "create_skill": "mkdir -p ~/.hermes/skills/category/name && cat > SKILL.md",
    "pin_skill": "hermes curator pin <skill> --p<PASSWORD> <name>",
    "audit_now": "python3 references/skill_audit.py --p<PASSWORD> <name>"
  },
  "safety_rules": [
    "cron job 必须引用磁盘文件,禁止内联 python3 -c",
    "危险命令防护会自动拦截 python3 -c / bash -c / execute_code",
    "送达必须脚本自己调 Telegram API,不要依赖 agent 的 deliver"
  ],
  "verification": {
    "manifest_exists": "test -f ~/.hermes/profiles/<name>/skills/.curated_manifest.json",
    "skill_count": "jq '.skills | length' ~/.hermes/profiles/<name>/skills/.curated_manifest.json",
    "pinned_skills": "jq -r '.skills[] | select(.pinned) | .name' ~/.hermes/profiles/<name>/skills/.usage.json 2>/dev/null | wc -l"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

AI Agent 使用指南:

  • 当用户说"我要建一个技能" → 执行 quick_start.create_skill 模板
  • 当用户说"帮我审计技能" → 先跑 verification.skill_count 确认 manifest 存在,再执行 quick_start.audit_now
  • 当用户想在 cron 里加任务 → 先检查是否违反 safety_rules 前三条(特别是禁止内联 python3 -c)

# 7. Agent 可直接解析的元数据块

{
  "_meta": {
    "doc_version": "2025-01-15",
    "article_id": "hermes-05-skills",
    "profile_context": "any",
    "executable_examples": true,
    "dependencies": ["hermes-cli", "curator"],
    "estimated_setup_time": "30min"
  },
  "quick_start": {
    "create_skill": "mkdir -p ~/.hermes/skills/category/name && cat > SKILL.md",
    "pin_skill": "hermes curator pin <skill> --p<PASSWORD> <name>",
    "audit_now": "python3 references/skill_audit.py --p<PASSWORD> <name>"
  },
  "safety_rules": [
    "cron job 必须引用磁盘文件,禁止内联 python3 -c",
    "危险命令防护会自动拦截 python3 -c / bash -c / execute_code",
    "送达必须脚本自己调 Telegram API,不要依赖 agent 的 deliver"
  ],
  "verification": {
    "manifest_exists": "test -f ~/.hermes/profiles/<name>/skills/.curated_manifest.json",
    "skill_count": "jq '.skills | length' ~/.hermes/profiles/<name>/skills/.curated_manifest.json",
    "pinned_skills": "jq -r '.skills[] | select(.pinned) | .name' ~/.hermes/profiles/<name>/skills/.usage.json 2>/dev/null | wc -l"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

AI Agent 使用指南:

  • 当用户说"我要建一个技能" → 执行 quick_start.create_skill 模板
  • 当用户说"帮我审计技能" → 先跑 verification.skill_count 确认 manifest 存在,再执行 quick_start.audit_now
  • 当用户想在 cron 里加任务 → 先检查是否违反 safety_rules 前三条(特别是禁止内联 python3 -c)

下一篇把这套「无人值守」推到极致——让 agent 真正自己上班:cron 驱动的数据库巡检、指标对比、自动清理,以及怎么让它在出问题时主动喊你。

#AI Agent#Hermes#Skill#工程化
上次更新: 6/21/2026

← Hermes Agent 实战 04|模型路由实战:config 全解、thinking 注入与 401/503 源码级根因 Hermes Agent 实战 06|让 Agent 自己上班:cron 驱动的无人值守巡检→

最近更新
01
Hermes Agent 实战 13|旗舰篇:让 Agent 从零部署并灾难恢复一个 7 节点生产集群 原创
06-22
02
Hermes Agent 实战 12|工具链外延:用 AI 运维 AI,与这个系列的诞生 原创
06-22
03
Hermes Agent 实战 11|踩坑合集:当「手动 rm」从来不是真正的修复 原创
06-22
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式