编程 SKRT:技能库超过 100 个后,先本地路由再读 SKILL.md

2026-09-05 00:05:01

SKRT:技能库超过 100 个后,先本地路由再读 SKILL.md

Claude Code、Gemini CLI、Codex、Cursor 这类编码 Agent 都靠 SKILL.md 技能文件扩展能力。技能一旦装到 100+,每个会话开始都要把「技能名 + 描述」全列一遍,上下文预算大量花在数清单上,真正干活的 token 反而被挤掉。

SKRT(github.com/LittlePeter52012/skill-router)这个 Skill Router 给的解法:给定一条用户请求,先在本地技能库里挑出最相关的几个,只把那几个 SKILL.md 正文读进来执行。纯 Go CLI、一次性进程,不引入重型依赖,没配 API Key 时退化成纯本地关键词匹配也能跑。

本文记录它的落地方式与取舍。它与站内 #6388 讲的是两件事:那篇讲 SKILL.md 的 progressive disclosure 三级加载机制本身,这篇讲技能多了之后怎么做「路由」这层。

它解决的是什么问题

输入:用NotebookLM查找资料 → 输出:nlm-skill(score: 92, 50ms)

不是所有 Agent 框架都像 Claude Code 那样天然渐进式按需加载技能。很多工作流(尤其是多 Agent 编排、Codex/OpenCode 这类跑在命令行的一次性场景)倾向于把候选技能尽量都暴露给模型,否则它不知道该调用哪个。技能一多,这个「全暴露」策略就开始吃上下文,路由错误也会变多。

SKRT 的做法是把它前置成一个一次性 CLI 查询:skrt query "..." 返回 JSON,Agent 拿到 top-N 技能名与路径,再去读对应 SKILL.md。

本地 7 层打分:不依赖网络也能路由

核心匹配器是七条策略按权重叠加打分,纯本地毫秒级(README 标称 200+ 技能约 50ms):

  1. 技能名精确命中(100 分)
  2. 名称/查询包含关系(90 分)
  3. description 子串匹配(最高 95 分)
  4. 关键词 token 重叠(最高 80 分)
  5. 单 token 检索(最高 92 分)
  6. Levenshtein 模糊匹配(最高 40 分)
  7. CJK bigram 匹配(最高 75 分)——中文/日文/韩文的整词切分

第 7 条对中文用户是关键:英文技能名对中文查询,光靠 token 重叠基本命中不了,双字组(bigram)能处理「小红书运营」这类中文字面。

CJK/西里尔文查询还有一个 Layer 0:先经 Gemini API 翻译成英文再匹配。没 Key 时这层跳过,只靠本地 CJK bigram。

两层可选:本地快 + API 准

Provider速度准确度依赖
api(默认)~3-5sAPI key
local~3ms够用

新装默认 provider_first:先试 API(embedding 检索 + 重排),请求失败或无 Key 时自动回落到本地关键词匹配,不会中断。

支持的 embedding 模型:Gemini gemini-embedding-001(推荐)/ gemini-embedding-2-preview、OpenAI text-embedding-3-small / text-embedding-3-large,本地走任意 Ollama / LM Studio / vLLM 的 /embeddings 端点。

落地步骤(Go 1.21+)

go install github.com/LittlePeter52012/skill-router/cmd/skrt@latest

# 交互式配 Gemini API key(存 ~/.skrt/credentials,0600 权限,config 只记环境变量名不记 key)
skrt provider setup

# 索引技能(扫描本地各 Agent 的技能目录)
skrt index

# 查询
skrt query "PDF merge"
skrt q "用NotebookLM查找" --verbose   # CJK + 调试输出

# 强制本地匹配
skrt query "protein structure prediction" --provider local

扫描的技能目录通过 ~/.skrt/config.json 配置,默认覆盖 Gemini、Antigravity、Codex、OpenCode、Qwen、cc-switch 等:

{
  "skill_dirs": [
    "~/.gemini/antigravity/skills",
    "~/.agents/skills",
    "~/.config/opencode/skills",
    "~/.qwen/skills",
    "~/.cc-switch/skills",
    "~/.codex/skills",
    "~/.codex/vendor_imports/skills",
    "./.agent/skills"
  ],
  "provider": "api",
  "provider_mode": "provider_first",
  "providers": {
    "api": {
      "endpoint": "https://generativelanguage.googleapis.com/v1beta",
      "api_key_env": "GEMINI_API_KEY",
      "model": "gemini-embedding-001"
    }
  }
}

查询输出是给程序吃的 JSON:

{"query":"PDF merge","elapsed_ms":2.3,"total":3,"provider":"api","results":[{"rank":1,"name":"pdf","score":90,"path":"~/.gemini/antigravity/skills/pdf/SKILL.md","summary":"Use this skill for anything with PDF files...","match_reason":"name_in_query"}]}

几个实用命令与注意点

  • 屏蔽仓库镜像目录:有的工具把技能装到顶层、同目录还留一份源码 checkout,不排除会把索引撑胖。用 ignore_dir_names 跳过 .git.agency-agents-repo 这类目录。
  • pinned 技能skrt pin add brainstorming 让常用技能保持可见,但精确/名称命中仍然压过通用 pin,不会把用户特意挂起的工具顶掉。skrt smart-pin --apply 会扫聊天历史自动建议该 pin 哪些。
  • managed source:把本地技能仓库登记为受管源,skrt update --reindex 拉取并按各自 install 命令重建索引,--dry-run 先看要动什么。

它不是什么(边界)

  • 不是 MCP server——MCP 要常驻 JSON-RPC 进程,SKRT 是一次性 CLI。
  • 不做技能编排/多 Agent 分发——只做「选出技能并返回路径」,选中后由 Agent 自己读技能正文执行。
  • 不是某个 IDE 插件,跨所有终端 Agent 通用。

一句话总结

技能库大到你不想把全量 description 塞进每个会话时,Skill Router 的思路是「先用廉价本地匹配圈出 top-N,再读那几份 SKILL.md」。SKRT 的意义在于默认不依赖网络也能路由,CJK 场景下有兜底,且输出是结构化 JSON 方便接进编排。如果你已经在多个 Agent 里铺了几百个技能,值得把技能路由单独摘出来跑一次,而不是继续让每个子 Agent 各自扫同一份技能目录。

仓库:github.com/LittlePeter52012/skill-router

复制全文 生成海报 Skill Router Agent LLM SKILL.md Gemini CLI Claude Code

推荐文章

程序员茄子在线接单