| name | skill-auditor |
| version | 1.0.0 |
| description | 系统审查并优化 Hermes Agent 技能。 |
| triggers | ["审查技能","审计技能","检查技能系统","全面审查","skill-auditor"] |
Skill Auditor - 技能系统审查技能
Skill Graph: 领域 → [[_index-meta|系统元技能领域]]
何时使用
- 用户说"审查技能"、"审计技能"、"检查技能系统"
- 用户说"全面审查"某几个技能
- 技能数量超过 60 个、需要系统性摸底时
- 大版本迭代后验证所有技能健康度
审查流程
Step 1: 扫描全量技能(execute_code)
import os, re, yaml
skill_dir = os.path.expanduser("~/.hermes/skills/acquisition")
EXCLUDE = {'.archive', '.claude', '.agents', '.trae', 'skills', 'references',
'templates', 'scripts', 'local', 'acquisition.bak.2.7',
'acquisition.bak.202604101033', 'acquisition.bak.202604131433',
'.cli', 'knowledge-base', 'smart-memory'}
active = sorted([s for s in os.listdir(skill_dir)
if os.path.isdir(os.path.join(skill_dir, s))
and s not in EXCLUDE and not s.startswith('.')])
print(f"活跃技能: {len(active)}")
issues = {}
for skill in active:
skill_md = os.path.join(skill_dir, skill, "SKILL.md")
if not os.path.exists(skill_md):
issues.setdefault(skill, []).append("SKILL.md 不存在")
continue
with open(skill_md, 'rb') as f:
raw = f.read()
try:
text = raw.decode('utf-8')
except UnicodeDecodeError:
text = raw.decode('latin-1')
skill_issues = []
if text.startswith('---'):
parts = text[3:].split('---', 1)
if len(parts) >= 2:
try:
fm = yaml.safe_load(parts[0])
if not isinstance(fm, dict):
skill_issues.append("frontmatter 非 dict")
else:
if 'name' not in fm: skill_issues.append("缺 name")
if 'description' not in fm: skill_issues.append("缺 description")
non_std = [k for k in fm if k not in ('name','description','license','metadata','compatibility','always','updated')]
if non_std:
skill_issues.append(f"非标准字段: {non_std}")
except Exception as e:
skill_issues.append(f"YAML解析失败: {e}")
else:
skill_issues.append("frontmatter 未闭合")
else:
skill_issues.append("缺 frontmatter")
unwanted = [f for f in os.listdir(os.path.join(skill_dir, skill))
if f in ('README.md','CHANGELOG.md','VERSION.md','HISTORY.md','INSTALL.md','TODO.md')
or (f.endswith('.md') and f != 'SKILL.md')]
if unwanted:
skill_issues.append(f"多余文档: {unwanted}")
if len(raw) > 10000:
skill_issues.append(f"SKILL.md过大 ({len(raw)//1024}KB)")
if raw.count(b'\r\n') > 10:
skill_issues.append(f"CRLF行结尾 ({raw.count(b'\r\n')}处)")
if skill_issues:
issues[skill] = skill_issues
critical = []
size_issues = []
doc_issues = []
crlf_issues = []
for skill, prob in sorted(issues.items()):
for p in prob:
if '不存在' in p or '缺 frontmatter' in p or '非 dict' in p:
critical.append((skill, p))
elif '过大' in p:
size_issues.append((skill, p))
elif '多余文档' in p:
doc_issues.append((skill, p))
elif 'CRLF' in p:
crlf_issues.append((skill, p))
print(f"\nP0 Critical: {len(critical)}"); [print(f" {s} - {p}") for s,p in critical]
print(f"\nP1 过大 (>10KB): {len(size_issues)}"); [print(f" {s} - {p}") for s,p in size_issues[:10]]
print(f"\nP2 多余文档: {len(doc_issues)}"); [print(f" {s} - {p}") for s,p in doc_issues[:10]]
print(f"\nP3 CRLF: {len(crlf_issues)}"); [print(f" {s} - {p}") for s,p in crlf_issues[:5]]
Step 2: 路由连通性检查(关键!)
教训:inquiry-response 有9KB完整内容(谈判策略+异议库+竞品情报),但未注册到ROUTING-TABLE,导致完全无法触发。技能存在 ≠ 技能可用。
import yaml, os
skill_dir = os.path.expanduser("~/.hermes/skills/acquisition")
rt_path = os.path.join(skill_dir, "ROUTING-TABLE.yaml")
with open(rt_path) as f:
rt = yaml.safe_load(f)
routing_skills = set()
for intent, config in rt.get('routing', {}).items():
markets = config if isinstance(config, dict) else {}
for market, skills in markets.items():
if isinstance(skills, list):
for s in skills:
if isinstance(s, dict) and 'skill' in s:
routing_skills.add(s['skill'])
indexed_skills = set(rt.get('skills_index', {}).keys())
active_skills = [s for s in os.listdir(skill_dir)
if os.path.isdir(os.path.join(skill_dir, s))
and os.path.exists(os.path.join(skill_dir, s, 'SKILL.md'))]
orphaned = []
for skill in sorted(active_skills):
in_routing = skill in routing_skills or skill in indexed_skills
if not in_routing:
size = os.path.getsize(os.path.join(skill_dir, skill, 'SKILL.md'))
orphaned.append((skill, size))
()
()
skill, size orphaned:
()
路由注册点(两个都要有才算连通):
routing.<intent>.<market>[]..skill — 激活路径(是否被意图触发)
skills_index.<skill_name>.path — 解析路径(skill:// URI能否解析)
P0孤儿 = 有内容但两边都没注册。必须修复。
Step 3: 问题优先级排序
P0(影响功能,立即修复):
- SKILL.md 不存在
- 缺失 frontmatter
- frontmatter 无法解析
P1(体积问题,降低效率):
- SKILL.md > 10KB
- frontmatter 冗余字段过多(如 250 行 triggers)
P2(违反规范,清理):
- 多余文档文件(README.md / CHANGELOG.md / GUIDE.md 等)
- skill-creator 原则明确:技能只需要 SKILL.md + scripts/references/assets
P3(工具可靠性):
Step 4: P0 立即修复
缺失 frontmatter — 最优先,直接补上:
for d in ~/.hermes/skills/acquisition/*/; do
f="$d/SKILL.md"
if [[ -f "$f" ]] && ! head -1 "$f" | grep -q '^---$' ; then
echo "缺失: $d"
fi
done
补 frontmatter 模板:
---
name: <skill-name>
description: <一句话说明技能功能 + 触发场景>
---
# <技能名>
SKILL.md 不存在 — 检查是否是被误删的空目录:
import os
skill_dir = os.path.expanduser("~/.hermes/skills/acquisition")
for s in os.listdir(skill_dir):
path = os.path.join(skill_dir, s)
if os.path.isdir(path) and not os.path.exists(os.path.join(path, "SKILL.md")):
files = os.listdir(path)
print(f"空技能目录(无SKILL.md): {s} — 内容: {files}")
Step 5: P2 批量删除多余文档
find ~/.hermes/skills/acquisition -name "*.md" ! -name "SKILL.md" \
! -path "*/.archive/*" ! -path "*/references/*" \
-type f 2>/dev/null | head -50
find ~/.hermes/skills/acquisition -name "README.md" -o -name "CHANGELOG.md" \
-o -name "VERSION.md" -o -name "INSTALL.md" -o -name "TODO.md" \
! -path "*/.archive/*" 2>/dev/null | xargs echo "将删除:"
Step 6: P3 修复 CRLF
find ~/.hermes/skills/acquisition -type f \( -name "*.sh" -o -name "*.md" -o -name "*.yaml" -o -name "*.py" \) \
-exec file {} \; 2>/dev/null | grep CRLF | head -20
find ~/.hermes/skills/acquisition -type f \( -name "*.sh" -o -name "*.md" -o -name "*.yaml" -o -name "*.py" \) \
-exec sed -i 's/\r$//' {} \;
Step 7: 大技能精简(渐进式)
对于 > 10KB 的技能,按以下顺序处理:
-
frontmatter triggers 字段:250+ 行关键词列表是最大浪费
- skill-creator 原则:触发机制唯一来源是
description
triggers 是非标准字段,删除
- 关键词合并进 description
-
内容重复:查找与 references/ 下文件重复的内容
grep -h "详细.*见.*skill://" ~/.hermes/skills/acquisition/*/SKILL.md 2>/dev/null \
| grep -oE "skill://[a-zA-Z0-9_/-]+" | sort -u
这些只保留 skill:// 引用,删除正文
-
过长章节拆分:
- 单一章节 > 80 行 → 考虑移入 references/
- 多个相似变体 → 拆为 references/VARIANT-A.md, references/VARIANT-B.md
-
精简后目标:
- 主入口技能:< 8KB
- 子技能:< 5KB
- 工具类技能:< 3KB
Step 8: 验证与推送
for f in ~/.hermes/skills/acquisition/*/SKILL.md; do
python3 -c "import yaml; yaml.safe_load(open('$f').read().split('---')[1].split('---')[0])" 2>/dev/null
if [ $? -ne 0 ]; then echo "YAML错误: $f"; fi
done
cd ~/.hermes/skills/acquisition && bash release-manager/scripts/sync-to-github.sh --skip-prereq -m "fix: skill audit P0-P3 fixes" 2>&1
常见问题速查
| 症状 | 原因 | 修复 |
|---|
| 技能无法触发 | frontmatter 缺失/格式错误 | 补 frontmatter |
| SKILL.md 过大 | 250行 triggers + 重复内容 | 删除 triggers,移入 references |
| patch 工具失效 | CRLF 行结尾 | sed -i 's/\r$//' |
| 推送后 GitHub 没更新 | 新技能未加入 RETAINED_SKILLS | 加到 sync-to-github.sh |
| 某个子技能找不到 | 目录存在但 SKILL.md 不存在 | 补 SKILL.md 或删除空目录 |
陷阱记录
- RETAINED_SKILLS 漏加:新增技能必须加入
release-manager/scripts/sync-to-github.sh 的 RETAINED_SKILLS 清单,否则下次 sync 会被删掉
- triggers 字段:全系统几乎所有技能都有,但 skill-creator 原则说它非标准。实际上
version 字段全系统都在用也不算问题。真正有影响的是:缺失 frontmatter、SKILL.md 不存在、过大
- acquisition 嵌套:有时会出现
acquisition/acquisition/ 嵌套目录,是 patch 误操作遗留,检查是否为空后删除
- description 过长:部分技能 description 超过 200 字,虽然不影响功能但影响 token 成本,应精简
版本: 1.0.0
updated: 2026-04-14