关于 skill-retrieval
面向 Hermes Agent 的技能检索插件,通过 BM25 按每轮用户消息挑选相关技能描述。它将系统提示词中的完整描述列表压缩为名称索引,再注入少量匹配项,以减少提示词开销。
主要功能
- 01按 BM25 排序技能描述
- 02压缩系统提示词中的技能列表
- 03逐轮注入相关技能描述
- 04缓存索引并检测技能变更
- 05支持中文、日文和韩文检索
适用场景
- 01在大量技能中查找当前任务相关项
- 02减少每轮请求中的技能提示词占用
- 03让中文查询匹配非英语技能内容
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ moonlight-lupin/ agent-skills --skill skill-retrieval安装后在 Claude Code 中输入/skill-retrieval确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
__init__.pyLICENSEplugin.yamlREADME.mdrequirements-dev.txtrequirements.txtscripts/__init__.pybm25_retriever.pyjev_rerank.pyrerank_ab_report.pytests/conftest.pytest_bm25_retriever.pytest_cjk_tokenization.pytest_index_cache.pytest_inject_k_split.pytest_issue14_should_show_arity.pytest_issue8_round2.pytest_issue8_round3.pytest_jev_environ.pytest_jev_provider.pytest_jev_rerank.pytest_midscan_fallback.pytest_module_identity.pytest_profile_home_switch.pytest_public_surfaces.pytest_refactor_contract.pytest_regressions.pytest_rerank_hook_wire.pytest_round3_fixes.pytest_round4_fixes.py详细介绍
技能检索
这是一个 Hermes Agent 插件。它不是 Claude Code 插件,无法在 Claude Code 中加载——该运行时没有 pre_llm_call 事件,没有 Python register() 入口点,并且读取的是 .claude-plugin/plugin.json,而不是 plugin.yaml。需要 Hermes Agent >=0.21.1(在 plugin.yaml 中设置 requires_hermes)。
基于 BM25 的渐进式披露,适用于 Hermes Agent 技能。该插件不会将每项技能的描述全部塞入系统提示词(约 11.5K 个令牌),而是保留一个精简的纯名称索引,并在每轮对话中仅注入最相关的前 K 项描述。
功能
分两个阶段进行渐进式披露:
-
阶段 1 — 精简系统提示词(
llm_request中间件): 重写发出请求中的<available_skills>块,仅列出技能名称(移除描述)。所有技能仍可按名称检索(约 2K 个令牌,而非约 11.5K)。Hermes 构建的系统提示词不会被修改,因此不会影响提示词缓存。 -
阶段 2 — 每轮 BM25 检索(
pre_llm_call钩子):对用户消息进行分词,使用 BM25 Okapi 对启用的技能描述排序,并将前 K 个匹配项(约 300 个令牌)作为上下文注入到用户消息之后。
架构
Each turn (llm_request middleware)
│
▼
Phase 1: rewrite <available_skills> → names only (~2K tokens, request only)
Each turn (pre_llm_call)
│
▼
Phase 2: BM25Index.retrieve(user_message, top_k)
└── inject "## Retrieved Skills ..." into user message (~300 tokens)
BM25 索引会在首轮对话时,按需基于独立技能(~/.hermes/skills)和插件附带的技能(~/.hermes/plugins/*/skills)构建,然后按 Hermes 主目录、工具能力快照、会话平台和解析后的禁用技能集缓存(这些输入与 Hermes 为其自身技能提示词缓存建立索引的输入相同)。当 Hermes 清除其技能提示词缓存时(通过 skill_manage 创建、修补或删除技能,通过 hub 安装,或切换技能开关),或者磁盘上的轻量清单发生变化时(技能根目录和类别目录的修改时间、顶层 SKILL.md 文件、config.yaml),缓存会在无需重启的情况下重建。
检索使用纯标准库实现的倒排索引(词项 → 预计算的 BM25 权重列表);对于约 200 项技能,检索耗时不到 1 毫秒。
令牌节省
| 阶段 | 令牌(约) |
|---|---|
| 之前(系统提示词中包含完整技能列表) | ~11.5K |
| 之后 — 仅含名称的系统提示词 | ~2.0K |
| 之后 — 每轮注入的前 K 项描述 | ~0.3K |
| 每轮净值 | ~2.3K(节省约 9K) |
在包含约 300 项技能的 Hermes 安装环境中测得;节省量会随技能数量增加而增长。
安装
将此目录复制或符号链接到 Hermes 插件文件夹中:
# From this repo
ln -s "$(pwd)/plugins/skill-retrieval" ~/.hermes/plugins/skill-retrieval
# Or copy
cp -r plugins/skill-retrieval ~/.hermes/plugins/skill-retrieval
用户插件默认不启用:Hermes 会发现该目录,但在启用之前不会加载(这会将 skill-retrieval 添加到 ~/.hermes/config.yaml 中的 plugins.enabled):
hermes plugins enable skill-retrieval
重启 Agent 会话以运行 register()——它会注册 llm_request 中间件和 pre_llm_call 钩子。
依赖项(如果缺少,请安装到 Hermes Python 环境中):
pip install pyyaml
配置
| 设置 | 默认值 | 设置方法 |
|---|---|---|
TOP_K | 6 | 环境变量 SKILL_RETRIEVAL_TOP_K |
INJECT_K | TOP_K | 环境变量 SKILL_RETRIEVAL_INJECT_K;注入多少个检索候选项。候选池大于注入集(例如 TOP_K=12、INJECT_K=6)时,Jev 重排可以筛选候选项,而不仅仅是重新排序。超过 TOP_K 的值会被限制为 TOP_K。 |
| 系统提示词精简 | 启用 | 设置 SKILL_RETRIEVAL_COMPACT=0,可禁用精简,同时保留 BM25 检索注入 |
BM25 k1 | 1.5 | scripts/bm25_retriever.py 中的常量 |
BM25 b | 0.75 | scripts/bm25_retriever.py 中的常量 |
| Jev 重排 | 关闭 | 设置 SKILL_RETRIEVAL_RERANK=jev 启用;密钥位于 TYPESAFE_API_KEY 中(兼容旧版 TYPESAFE_KEY),从 os.environ 读取 |
| 重排提供商 | typesafe | SKILL_RETRIEVAL_RERANK_PROVIDER=openrouter 使用 https://openrouter.ai/api/v1/systemone 和 OPENROUTER_API_KEY |
| 重排日志路径 | $HERMES_HOME/data/jev-trial/rerank_ab_log.jsonl(超过 5 MB 后轮换) | 环境变量 SKILL_RETRIEVAL_RERANK_LOG |
export SKILL_RETRIEVAL_TOP_K=8
export SKILL_RETRIEVAL_INJECT_K=6
export SKILL_RETRIEVAL_COMPACT=0
export SKILL_RETRIEVAL_RERANK=jev
Jev 重排(可选,默认关闭)
当 SKILL_RETRIEVAL_RERANK=jev 时,BM25 的前 N 个候选项会由 Jev system-one 判断模型重排(POST https://api.typesafe.ai/v1/systemone,
模型 jev-latest)。行为如下:
- 失败时宽松处理 — 超时时间为 3.0 s,不重试;发生任何错误时,请求会记录警告,并保留 BM25 的原始顺序。检索不会因 Jev 而阻塞。
- 仅通过环境变量配置 — 所有设置均来自
os.environ(绝不读取文件):TYPESAFE_API_KEY(也接受旧版TYPESAFE_KEY),或使用SKILL_RETRIEVAL_RERANK_PROVIDER=openrouter时的OPENROUTER_API_KEY(相同约定,通过 OpenRouter 提供服务)。Hermes 会在启动时将当前配置文件的.env加载到 environ,因此绝不会读取其他配置文件。 - A/B 日志 — 每次查询都会将时间戳、会话 ID、200 字符的查询摘录、两种排序、概率、令牌数和延迟记录到上述日志路径;日志超过 5 MB 时会轮转;
scripts/rerank_ab_report.py可在本地汇总日志。 - 出站上限 — 每次调用最多发送
RERANK_CANDIDATES(默认值为 12)条描述,即使调用方的候选列表更长;其余条目保留在末尾,顺序遵循 BM25,且不会离开本机。
验证是否正常工作
在完整的 Hermes 运行环境之外,Phase 1 会静默地不执行任何操作;BM25 索引也可能静默为空。重启后,请检查 Hermes 日志。
正常启动 — 查找以下日志行:
BM25 index built: N docs …Skill retrieval plugin registered (top_k=…, compact=true)
运行降级 — 以下警告表示功能未正常工作:
Cannot locate prompt_builder — compaction skipped(Phase 1 失败;匿名会话仍会运行 Phase 2,但具名会话因未记录能力快照而会跳过注入)No active skills found for BM25 index(索引为空 — 未注入任何检索结果)
工作原理
- 分词器 — 将文本转换为小写、去除标点并按空格拆分。含有 CJK 文字的词元会拆分为二元组(针对 CJK 文字连续片段;长度为 3 个或更多字符的片段会替换为其二元组):CJK 统一表意文字(包括扩展 A 和 B)、日文假名(全角和半角)以及韩文音节 — 因此,中文、日文和韩文查询可以匹配非英文语料文本,而像
用python写这样的混合词元也会保留python词项。 - 语料 — 每个技能会转换为
"name: description",并附上其 frontmatter 中的triggers:列表和metadata.hermes.tags(如果存在);如果 SKILL.md 正文含有 CJK 文字,则还会附上正文前约 3000 个字符中的 CJK 连续片段(以空格连接,总长度上限为 200 个字符)。将正文纳入语料后,CJK 查询便可匹配技能内容,而不只是(通常为英文的)描述。触发短语、标签和 CJK 连续片段会参与检索评分,但只有描述会注入提示词。跳过~/.hermes/config.yaml中已禁用的技能。 - 索引 — BM25 Okapi TF 饱和度 + Lucene IDF
log(1 + (N-df+0.5)/(df+0.5))(始终为正,因此小型语料和常见词项仍会获得评分),以倒排索引形式存储:dict[str, list[tuple[int, float]]],将每个词项映射到一个倒排列表,其中包含(文档索引,预计算的 BM25 权重)。 - 检索 — 对索引中出现的每个唯一查询词元,遍历其倒排列表并累加评分;按评分降序排序(仅保留评分 > 0 的结果)。
性能
- 首次使用时构建索引并缓存(在仅配备 CPU 的虚拟机上,200 个技能约需 8 ms)。之后每轮只检查技能根目录、其一级子目录和
config.yaml,以验证缓存。零技能安装也会缓存空结果(仅发出一条警告,不会重新扫描)。 - 检索耗时不到 1 ms(200 个技能的平均耗时约为 0.03 ms)。倒排索引只访问与查询词共享词项的文档,不会扫描整个语料库。
- 无编译依赖。插件仅使用 Python 标准库(以及用于解析配置和 frontmatter 的 pyyaml)。这样可省去 154 MB 的 numpy/scipy 安装和约 573 ms 的导入耗时,这对需要生成子进程的路径(例如 Claude Code 的
UserPromptSubmit变体)很重要。 - Hook 出错时会返回
None(不注入),因此 Agent 可以继续工作。
依赖项
pyyaml
限制
- BM25 是词法检索,而非语义检索。即使意图匹配,与技能描述共享词项较少的改述查询也可能排名靠后。
- 注入块中会截断超过 200 个字符的描述;如需查看完整技能正文,请使用
skill_view(name)。 - 压缩使用 Hermes 只读的
agent.prompt_builder辅助函数;如果无法导入,则会跳过阶段 1,并回退到独立加载器进行检索。 - 只有在此进程中构建了系统提示词后,才会为具名会话注入内容(构建时会记录该会话的工具能力)。重启后恢复但未重新构建的会话不会获得注入内容,以免冒险注入 Hermes 对其隐藏的技能。
- 在 Hermes 外部(例如通过编辑器)对嵌套的
SKILL.md(category/skill/SKILL.md)仅修改内容,不会改变任何目录的修改时间,因此只有在 Hermes 清除技能提示词缓存或 Agent 重启后才会获取该修改。通过skill_manage进行的编辑,以及新增或移除技能、修改配置,都会在下一轮获取。 - 阶段 1 依赖 Hermes 内部组件(
agent.prompt_builder),Hermes 升级后可能会失效。 - BM25 的 top-1 精确率不稳定:最佳匹配技能往往不在第 1 位,不过通常会排在前几个结果中。排名完全取决于你自己的语料库及其描述措辞,因此不建议将
TOP_K设为低于约 5。 - 压缩会使用从
agent.prompt_builder和agent.skill_utils导入的只读函数重写请求中的<available_skills>块(README 中披露了全部 10 个函数);如果这些函数的签名发生变化,扫描过程中的任何异常都会触发回退,改用独立加载器,因此语料库绝不会在无提示的情况下变为空。 - 标准库索引使用 float64 进行计算(之前的 scipy 版本使用 float32)。得分相同的技能,其排序可能与之前不同。这不会造成问题——这些分数确实相同(相差约 1e-6)——但这与 scipy 版本的行为确有差异。