skill(hermes-self-improvement): v4.7.0 补上「任务开始前先扫技能库」硬约束 0 + 新增检索器 skill-find.py

根因(牧尘 09-11 指出『技能本来都有,你却不去使用』):
  体系里有「创建前必查重」硬约束,但整条链路只防『重复创建』,
  没有任何一处防『不去使用』。实测:3 小时会话只主动加载了 2 个技能。

本次落地:
  1) scripts/skill-find.py —— 任务→技能 检索器(name/trigger/description 加权 + 中文滑窗切词)
     实测 6/6 命中正确技能(obsidian/密钥清历史/vault整理/看板/验收/2B下沉)
     无命中时给降级阶梯提示(web_search 找现成 → skill-library-porting → 自建)
  2) hermes-self-improvement 新增「硬约束 0」+『搜不到=不存在』可发现性纪律
     + 说明为何后台 curator 不能替代这一步(它写它的,我不回读=白写)
  3) description 补『任务开始前先扫技能库』语义

配套修正(可发现性):git-repo-hygiene / notes-vault-curation 的
description+triggers 补入任务的自然表述(原文搜『密钥泄露』命中不了)
This commit is contained in:
小唯 2026-09-11 01:42:05 +08:00
parent 5db634f42b
commit 03e8a1d7d7
4 changed files with 150 additions and 10 deletions

105
scripts/skill-find.py Executable file
View File

@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""skill-find —— 任务→技能 检索器2026-09-11 建,牧尘"技能本来都有你却不用"的机械解法)
为什么存在规则写了 N 遍我没执行因为"扫技能库"这件事在脑子里是**可选动作**
把它变成一条命令**任何任务动手前先跑它**命中即 skill_view 加载
用法
skill-find.py "obsidian 同步 配置" # 关键词
skill-find.py "密钥泄露了怎么清历史" # 整句任务描述也行
skill-find.py --top 15 "整理笔记库"
skill-find.py --list-cats # 看技能分类
"""
import os, re, sys, glob
SKILLS = os.path.expanduser("~/.hermes/skills")
QSTOP = set("的了和与及怎么如何要要不要是否有我你他她它把被给对为在从到就都也很呢吗个这那注意事项问题办法方法请问帮我一下做个")
def load_skills():
out = []
for p in glob.glob(os.path.join(SKILLS, "**", "SKILL.md"), recursive=True):
try:
t = open(p, encoding="utf-8", errors="ignore").read()
except Exception:
continue
fm = t.split("---", 2)
fm = fm[1] if len(fm) > 2 else t[:2000]
def get(key):
m = re.search(rf'^{key}:\s*(.+?)(?=\n[a-z_]+:|\Z)', fm, re.M | re.S)
return m.group(1).strip().strip('"').replace("\n", " ") if m else ""
name = get("name") or os.path.basename(os.path.dirname(p))
desc = re.sub(r"\s+", " ", get("description"))
trig = get("triggers") + " " + get("trigger")
cat = p.replace(SKILLS + "/", "").rsplit("/", 2)[0] if "/" in p.replace(SKILLS + "/", "") else ""
out.append({"name": name, "desc": desc, "trig": trig, "path": p, "cat": cat})
return out
def tokens(q):
q = q.strip()
parts = [p for p in re.split(r"[\s,,、/|]+", q) if p]
toks = set(parts)
# 中文长词再切 2-3 字滑窗,提高召回("密钥泄露了怎么清历史" → 密钥/泄露/清历史…)
for p in parts:
cjk = re.findall(r"[\u4e00-\u9fff]+", p)
for c in cjk:
for n in (2, 3, 4):
for i in range(0, max(1, len(c) - n + 1)):
toks.add(c[i:i + n])
return {t for t in toks if t and t not in QSTOP and len(t) >= 2}
def main():
argv = sys.argv[1:]
top = 8
if "--top" in argv:
i = argv.index("--top")
top = int(argv[i + 1])
del argv[i:i + 2]
args = [a for a in argv if not a.startswith("--")]
sk = load_skills()
if "--list-cats" in sys.argv:
cats = {}
for s in sk:
cats[s["cat"]] = cats.get(s["cat"], 0) + 1
for c, n in sorted(cats.items(), key=lambda x: -x[1]):
print(f" {c:44s} {n}")
return
if not args:
print(__doc__); return
q = " ".join(args)
toks = tokens(q)
scored = []
for s in sk:
sc, why = 0, []
blob = (s["name"] + " " + s["desc"] + " " + s["trig"]).lower()
for t in toks:
tl = t.lower()
if tl in s["name"].lower():
sc += 10; why.append(f"name:{t}")
if tl in s["trig"].lower():
sc += 6; why.append(f"trigger:{t}")
if tl in s["desc"].lower():
sc += 4
if q.lower() in blob:
sc += 8; why.append("整句命中")
if sc:
scored.append((sc, s, why))
scored.sort(key=lambda x: -x[0])
if not scored:
print(f"❌ 技能库无匹配:「{q}")
print(" → 下一步web_search 找现成技能库 → 装skill-library-porting都没有 → 按方法论自建hermes-self-improvement")
return
print(f"🔍 「{q}」→ {len(scored)} 个候选,取前 {min(top,len(scored))}\n")
for sc, s, why in scored[:top]:
flag = "" if sc >= 12 else " "
print(f"{flag} [{sc:>3}] {s['name']} ({s['cat']})")
print(f" {s['desc'][:110]}")
if why:
print(f" 命中: {', '.join(sorted(set(why))[:5])}")
print(f"\n▶ 加载skill_view(name='{scored[0][1]['name']}') (可组合多个)")
if __name__ == "__main__":
main()

File diff suppressed because one or more lines are too long

View File

@ -1762,16 +1762,16 @@
"archived_at": null,
"created_at": "2026-05-13T12:27:05.593125+00:00",
"created_by": null,
"last_patched_at": "2026-08-12T05:13:32.138839+00:00",
"last_patched_at": "2026-09-10T17:41:59.620307+00:00",
"last_reused_patch_generation": 1,
"last_used_at": "2026-09-06T03:33:05.748011+00:00",
"last_viewed_at": "2026-09-06T03:33:05.735224+00:00",
"patch_count": 81,
"patch_generation": 1,
"last_used_at": "2026-09-10T17:41:51.254700+00:00",
"last_viewed_at": "2026-09-10T17:41:51.249893+00:00",
"patch_count": 84,
"patch_generation": 4,
"pinned": false,
"state": "active",
"use_count": 154,
"view_count": 139
"use_count": 155,
"view_count": 140
},
"hermes-venv-dependency-safety": {
"archived_at": null,

View File

@ -1,8 +1,8 @@
---
name: hermes-self-improvement
description: "当完成复杂任务、发现新工作流、或被用户纠正时将模式保存为skill。含技能创建规范、质量标尺、curator流程。"
version: 4.6.0
date: 2026-09-03
description: "当完成复杂任务、发现新工作流、或被用户纠正时将模式保存为skill;以及【任务开始前先扫技能库】。含技能创建规范、质量标尺、curator流程。"
version: 4.7.0
date: 2026-09-11新增。硬约束 0任务开始前先扫技能库 skill-find.py补"搜不到=不存在"可发现性纪律)
tags: [workflow, skill-management, curator, quality]
牧尘_usage_notes: >
复杂任务完成/发现新工作流/被牧尘纠正时 → 创建/更新 skill。
@ -19,6 +19,38 @@ tags: [workflow, skill-management, curator, quality]
> 版本 3.9 | 2026-07-10opencode代码审查/daemon注入架构/时区bug修复/工具理解纠正)
## ⚠️ 硬约束 0任务开始前先扫技能库2026-09-11 牧尘重申,此前屡次不执行)
> 牧尘原话:「技能本来都有,你却不去使用。我一直强调,任何任务都要加载技能去做,
> 没有就去搜索安装或者根据方法论新建,甚至可以组合技能使用,你总是不去执行。」
**根因(别再归因于"忘了"**:现有体系有「创建前必查重」硬约束(见下节),但 **整条链路只防「重复创建」,没有任何一处防「不去使用」**
默认我会自然去看——实测不会:一个 3 小时的 Obsidian+密钥处置会话里,我只主动加载了 **2 个**技能。
### 机械动作(不可跳过)
```bash
python3 ~/.hermes/scripts/skill-find.py "<任务关键词或整句描述>"
# → 输出候选技能 + 命中理由 + 建议加载的命令
```
- 命中 → `skill_view(name=...)` 加载,**可组合多个**(如 vault 整理 + 密钥扫描 + git 治理)
- 无命中 → `web_search` 找现成技能库 → 装(`skill-library-porting`);都没有 → 按本 skill 方法论自建
- **报告里必须写明加载了哪些 skill**(不写 = 无法自证执行过)
### 搜不到 = 不存在(可发现性纪律)
技能写了但**描述用的是同义词以外的词**等于没写。实例2026-09-11 修):
`git-repo-hygiene` 原描述只有「推送积压/.git 膨胀/忽略规则」——搜「密钥泄露怎么办」**命中不了**。
- description 与 triggers 必须包含**任务的自然表述**(用户/我自己会怎么问),而不是只有书面术语
- 同族技能之间的交叉引用要指向**真实存在的章节**(改完用 `grep -n "^## "` 体检)
### 为什么后台 curator 不能替代这一步
curator 会把会话实时写成 skill但**它写它的,我不回读就是白写**。
闭环是「任务→查库→用/写→下次查库」,断在「下次查库」等于闭环没闭合。
## 核心理念
技能是**程序化记忆**——不是笔记,是可复用的工作流。每次创建一个技能,就是在为未来的自己铺路。