JavaGuide - Skill
Skill
Agent Skills 是什么?和 Prompt、MCP 到底差在哪?
Skill
本质
本质:一串 Prompt,告诉 LLM 某个任务该怎么做(经过哪些步骤、怎么才算合格等),相当于一个用自然语言描述的 Workflow。
一个 Skill 通常采用文件夹存储,其结构如下:
1 | skill-name/ |
SKILL.md 是正文,每次调用时加载到 Context 中。
元数据
Skill.md 的内容包含元数据和正文,元数据举个例子 matt 的 teach 如下:
1 | \--- |
元数据是用来一句话告诉 LLM 这个 skill 是干嘛的、什么时候用。
正文
正文则是很常规的 markdown 格式,定义了工作流和注意事项。
正文的长度,A 畜建议不超过 500 行,多余的可以引用独立文件,用渐进式披露按需加载。比如:
1 | ## 高级功能 |
Skill 加载
Skill 的加载分为两个阶段,第一个阶段是向 Agent 报道注册,让 Agent 知道有这么个 Skill;第二个阶段则是真正调用 Skill 时,将正文加载到 Context 内;还有一个额外阶段,渐进式披露地将 Skill 正文中的引用文段按需加载到 Context。
以 Pi agent 和 claude code 为例子,两者在第一阶段的加载类似,都是在构建 Context 前扫描所有 skill 的元数据,加入元数据表单然后拼接到 Context,不会直接把正文塞进去,依靠渐进式披露来按需加载 Skill。不过,pi 的 skill 表单明确是拼接到 System prompt 的;而 cc 的 skill 表单具体怎么加入的则未知,cc 没有开源,相关只给了一个 注入上下文 这个描述。
当 Skill 被调用,无论是被 LLM 渐进式披露,还是用户手动调用,就顺着 Skill 元数据表单里面的 path 找 SKILL.md,将其加入 Context。
最后,是额外阶段中的引用文段,也可以说是 Resource,和第二阶段没啥区别,都是渐进式披露,然后到引用的 path 找原文,加载到 Context。
Skill 路由
Skill 路由是什么
Skill 路由是什么?就是 LLM 判断自己需要加载哪个 Skill 的机制,一般来说 Skill 路由并没有固定的形态,因为从实践来看,LLM 自行判断调用哪个 Skill 是一种路由方式;由服务端逻辑根据 query 强制加载 Skill 也是一种路由方式……总而言之,我们不能机械地将 Skill 路由与 LLM 自行判断划等号。
Skill 太多怎么提高召回率
尽管 Skill 可以通过 LLM 自行判断,但是这只适用于 Skill 较少、Skill 之间功能较为独立时的状况,若 Skill 数量多、功能之间有交叉时——一般说明这是较大型的项目,LLM 利用渐进式披露来自行判断的路由方法很有可能失效,这个时候就有必要在服务端做 Skill 路由了。
目前行业内的普遍做法是:候选集生成 → 召回 → 重排 → 约束过滤 → 激活少量 Skill → LLM 执行。
候选集生成+召回,候选集可以是所有是 Skill,也可以是任何规则的 Skill 聚类。召回就是根据 query 查询候选集内的相关性 top-k 的 Skill,这一步可以选用不同的查询技术,比如 elasticsearch 倒排索引;或者更古早的关键词索引;现在用得多的也有向量查询等。
重排,不过为啥还要有这一步?前面的召回中,不是应该已经按照概率输出了吗?这就是目前主流的 retrieval 和 routing 分开。
约束过滤,这一步主要是防止向量检索出现的向量上相近,语义上却无关的问题,好比哈希,不同的对象有可能计算得到相同的哈希值,而约束过滤就是在检索结果之后再加一层校验,滤除语义无关的候选。
架构:
1 | User Query |
多层召回
就是将普遍做法多层执行,比如 1000 个 skill,不要直接从 1000 里面找 1 或多个,而是先从 1000 个找 50 个,再从 50 个找 1 个……
Skill Bundle / Skill Pack
这是 A 畜自己推崇和使用的一种做法,不要把成千上万的 Skill 都放在同一个文件夹下,而是根据不同的领域,分为多个 package,依旧凭借 LLM 的渐进式披露,自己去找。
然后,package 里面又可分 package……
Skill 注意事项
Skill ≠ Readme
Skill 是自然语言定义的 Workflow,不要写成 Readme。
一个 Skill 解决一个问题
最理想的情况是,每个 skill 都能解决一个独立的问题,这是为了减少 skill 之间的责任重叠,利于 LLM 选择 Skill。
不过,很多情况下,上层 Skill 需要利用 下层 Skill 的能力,这个时候不要耦合式地在前者复制粘贴后者的正文,而是仍然使用引用式。这里给个例子,matt 的 grill-with-docs:
1 | Call the Skill tool twice, for "grilling" and "domain-modeling". |
Skill 里面可选分支太多
什么叫可选分支太多?就是让 LLM 酌情选择的选项,比如:
1 | 你可以使用 pypdf、pdfplumber、PyMuPDF 或 pdf2image 处理 PDF。 |
这是不对的,因为 LLM 会额外耗费注意力在这些组件的选择上。我们要使用明确的分支,有条件的分支,例如:
1 | 首先判断该 pdf 是不是扫描版: |
专有名词保持上下一致
例如前文使用“API 端点”后,后文不再改写为 URL、API 路由或路径。
否则,LLM 会额外耗费注意力。
