Anthropic Skills 如何渐进加载并评测能力

核验范围:本文以 agentskills.io 规范页anthropics/skills 仓库main 分支为主要来源,重点读了规范正文、skills/pdf 目录(SKILL.mdforms.mdreference.mdscripts/)以及官方工程文章。文中给出的字段约束、token 量级和脚本文件名均来自这两处;blog-md-preflight 是我按同一格式写的示例,不是仓库里的现成 Skill。我没有在本地跑完整的 skill-creator 评测,只讨论从规范和公开示例中读到的设计方式。

假设用户给了我一份 30 页的 PDF,要求提取其中的表格、识别表单字段,再填回一份新文件。模型当然能读 PDF,但要稳定地完成这件事,还需要知道该用哪些库、何时调用脚本、遇到扫描件怎么办,以及怎么校验填好的表单。这几个问题本文都会具体回答,而不是停在”模型可以处理 PDF”这句话上。

Anthropic 的 Agent Skills 想解决的就是这类问题。它把一套可复用的操作经验放进一个目录:先让 Agent 看到简短的任务说明,确定相关后再读取具体步骤,最后按需要读取参考资料或执行脚本。本文以 Anthropic 的 pdf skill 为例,记录我理解这套机制的过程,并且尽量把每个概念都落到一份能看到的文件或一段能读的代码上。

先说明三个边界,后文会反复用到。Anthropic 最初提出并开源了 Agent Skills;anthropics/skills 是 Claude 的实现和示例仓库;现在跨平台的格式规范以 agentskills.io 为准。仓库 README 也把”Claude 的实现”和”标准”分开说明。凡是涉及”skill 怎么被发现、怎么被加载”的部分,标准只规定组织方式,具体行为取决于宿主 Agent,我会在对应位置标出。

这篇能解决什么问题

如果你想知道 建议阅读
Skill 和 prompt、工具、MCP、RAG 有什么区别 先厘清概念
一个 Skill 最少要写什么、正文长什么样 最小结构
Agent 到底怎么”发现”并选中一个 Skill description 与匹配机制
为什么几十个 Skill 不会一起塞满上下文 渐进式披露与一笔账
为什么有些操作应该写成脚本 PDF 示例
怎么从零写出并验证自己的第一个 Skill 动手写一个最小 Skill
Skill 是否真的带来提升 评测闭环
skill 放在哪、怎么被 Claude Code 加载 安装与使用
强约束型 Skill 应该怎么写 我的分类框架

先厘清概念:Skill 是什么,不是什么

初学者最容易把 Skill 和它周围一堆相似的东西混在一起。先用一张表把边界划开,后面的内容才不会悬空。

概念 它是什么 和 Skill 的关系
一次性 prompt 你在对话里临时写的指令 Skill 是把这套指令沉淀成可复用、按需加载的文件
CLAUDE.md / 全局指令 每次会话都常驻上下文的项目约定 Skill 默认不常驻,只有相关时才载入,省上下文
工具 / MCP 让模型能”做”某件事(调 API、读文件、查数据库) Skill 是”怎么做”的知识,可以指挥模型去调用工具或脚本
斜杠命令 用户手动触发的固定入口 Skill 既可由用户点名,也可由模型按任务自动选中
RAG 运行时按相似度检索文档片段喂给模型 Skill 是作者预先组织好的操作手册,不依赖向量检索来命中

一句话概括:Skill 是一份可以被 Agent 按需翻开的操作手册。它不新增模型的能力上限,而是在合适的时刻,把”这件事该怎么做”的经验精确地送到模型面前,同时避免不相关的经验一直占着上下文。

理解这一点,就能明白为什么 Skill 的价值不在”内容多”,而在”组织得当”。同样一段 PDF 处理经验,塞进一份三千行的超长提示词里,每次对话都得付出全部上下文成本,而且模型要在无关内容里找相关内容;拆成一个 Skill,则平时只露出一行 description,真正处理 PDF 时才展开正文,需要填表单时才进一步读 forms.md。差别不在写了什么,而在什么时候让模型看到什么。

一个 Skill 的最小结构

按当前规范,一个 Skill 是包含 SKILL.md 的目录。scripts/references/assets/ 都是可选的:

1
2
3
4
5
6
7
8
9
pdf/
├── SKILL.md # 元数据与核心操作说明(必需)
├── forms.md # 仅填表单时才读的专项说明
├── reference.md # 高级用法与排错,按需读取
├── scripts/ # 可执行代码,可直接运行
│ ├── fill_fillable_fields.py
│ ├── check_fillable_fields.py
│ └── convert_pdf_to_images.py
└── LICENSE.txt

上面是 anthropics/skillspdf skill 的真实布局(文件名照抄仓库)。可以先记住这个形状:一份必需的 SKILL.md、若干按需读取的 Markdown、一批可执行脚本。

SKILL.md 以 YAML frontmatter 开始。真正必填的只有 namedescription,最小的一份长这样:

1
2
3
4
---
name: pdf-processing
description: Extract text and tables from PDFs, fill forms, and merge files. Use when the task involves PDF files, forms, or document extraction.
---

各字段的约束如下,数字都来自规范:

字段 是否必填 约束与要注意的点
name 1–64 个字符,只能用小写字母、数字和连字符,不能以连字符开头或结尾、不能出现连续连字符,且必须和目录名一致
description 1–1024 个字符,非空,要同时写清”做什么”和”什么时候用”
license 许可名称,或指向一份打包在目录里的许可文件
compatibility ≤500 字符,有特殊依赖、系统包、网络要求或目标产品时再写
metadata 任意字符串键值对,供实现方保存额外属性
allowed-tools 空格分隔的预授权工具列表,仍是实验字段,不同 Agent 支持情况不同

其中 allowed-tools 值得举个具体例子,规范给的写法是空格分隔的工具签名:

1
allowed-tools: Bash(git:*) Bash(jq:*) Read

它的意思是”这个 Skill 运行时预先批准 gitjq 命令和读文件”,好处是执行时少几次授权打断。但因为是实验字段,跨平台时不要依赖它,把它当锦上添花即可。

正文该写什么

规范对正文没有格式限制,只给了建议:写清分步骤、给输入输出示例、列常见边界情况。有一句提醒很关键:一旦 Skill 被激活,整份 SKILL.md 正文会被完整读入上下文,所以正文要克制,长的东西拆到引用文件里去。

拿真实的 pdf/SKILL.md 来看,它的正文骨架大致是这样(我按仓库里的分节还原):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# PDF processing

## Quick start
(一段最短的 pypdf 用法,让模型立刻能上手)

## Python libraries
### pypdf —— 合并、拆分、旋转、读元数据
### pdfplumber —— 按版面提取文本、提取表格
### reportlab —— 从零创建 PDF

## Command-line tools
pdftotext / qpdf / pdftk 各自擅长什么

## Common tasks
- 扫描件 OCR(让扫描 PDF 可搜索)
- 加水印
- 提取图片
- 加/解密码

## Quick reference
一张"任务 → 用哪个工具"的对照表

## Next steps
需要填表单看 forms.md;高级用法和排错看 reference.md

这个骨架本身就是一堂课:它没有把所有 PDF 知识铺开,而是先给一条最短路径(Quick start),再按”库/命令行/常见任务”分类,最后用一张对照表收口,把两块重内容(表单、排错)明确指向别的文件。初学者写自己的 Skill 时,照这个”最短路径 + 分类 + 指针”的结构走,通常不会太差。

写完后可以用 skills-ref validate ./my-skill 检查 frontmatter 和命名是否符合规范。

description 是触发器:怎么写,以及匹配是怎么发生的

这里最容易被低估的是 description。它不只是给人看的简介,而是 Agent 判断要不要激活这个 Skill 的唯一线索。先看规范给的一组好坏对照:

1
2
3
4
5
6
7
# 差:模型没法判断什么时候该用它
description: Helps with PDFs.

# 好:动作 + 触发场景 + 具体关键词都写清了
description: Extracts text and tables from PDF files, fills PDF forms, and
merges multiple PDFs. Use when working with PDF documents or when the user
mentions PDFs, forms, or document extraction.

差的那句问题不在”短”,而在没有可供匹配的信号:既没说清能做哪些具体动作,也没说什么场景下该被选中。好的那句把”做什么(extract/fill/merge)”和”什么时候用(提到 PDF、表单、文档提取)”都写出来了,模型才有依据。

匹配到底是怎么发生的

初学者常把这一步想成某种玄学,或以为背后有向量检索。就 Claude 的实现而言,机制其实很朴素:启动时,所有已安装 Skill 的 namedescription 会被放进模型的上下文,约每个 100 token 量级;当任务与某个 description 匹配时,模型自己决定去激活它,这才触发正文的加载。没有 embedding,没有相似度阈值,就是模型读着这份清单做选择。

这个心智模型一旦建立,两条写法上的推论就很自然:

  • description 写得过于笼统(”帮你处理文档”),模型在清单里看不出它和当前任务的关系,Skill 很可能不被选中。
  • 把无关场景全塞进 description 想”多覆盖一点”,又会让它在不该出现的时候被误选,白白展开正文占上下文。

所以 description 的目标不是”介绍得全面”,而是”让模型在一屏清单里一眼认出这是不是当前该用的那个”。真实的 pdf skill 就把 description 写得很直白:从”读取/提取文本表格、合并、拆分、旋转、加水印、创建、填表单、加解密、提取图片、扫描件 OCR”一路列到”只要用户提到 .pdf 文件或要求产出一个 PDF,就用这个 skill”。它宁可把触发条件写满,也不含糊,因为这一行就是它被发现的全部依据。

渐进式披露:按需付出上下文成本

Skills 的核心不是多放几份 Markdown,而是把信息拆到不同加载时机。规范把它分成三层:

规范把第一层约束在约 100 token 的量级,建议 SKILL.md 正文控制在 5000 token 和 500 行以内,更长的说明拆到引用文件里。对 Claude 这类具备文件系统和代码执行能力的 Agent,脚本还可以直接执行,不必先把脚本全文读进上下文。

把这笔账算出来

抽象的”省上下文”不如一笔具体的账清楚。假设你装了 50 个 Skill,每个正文平均 4000 token:

如果不做分层,这 50 × 4000 = 20 万 token 就得一直常驻。别说很多模型的窗口放不下,就算放得下,模型每次都要在一堆无关内容里找相关内容,命中率反而下降。

做了渐进式披露就不一样:平时常驻的只有 50 × 100 = 5000 token 的 description 清单;当前任务真正激活的可能就 1 个 Skill,展开 4000 token 正文;用到脚本时才把某个脚本读进来。常驻成本从 20 万降到 5000,降了约 40 倍。

这就是为什么 Skill 强调”正文要短、重资料要拆”:省下来的不是磁盘,而是每一次对话都要重复付出的上下文预算。也正因如此,第一层的 description 才要写得精准,因为它是这套机制里唯一一直在花钱的部分。

需要强调一句边界:这套三层加载不是每个 Agent 产品都必然实现的,而是标准和 Claude 实现所倡导的组织方式。写跨平台 Skill 时,我会把”按需加载”和”能否直接执行脚本”分别写清,不假设每个宿主都有相同的权限与工具。

用 PDF Skill 看一次完整分工

Anthropic 的 pdf skill 很适合初学者拆读。它把常见操作放在 SKILL.md 中,把填写表单等专项说明放到 forms.md,把更详细的资料放在 reference.md,再把字段提取、填表、渲染或校验等确定性工作交给 scripts/

回到开头那份 30 页 PDF 的任务,整个流程可以理解为:

  1. Agent 从每个已安装 Skill 的 namedescription 中发现 PDF 相关能力,选中 pdf
  2. 它读取 pdf/SKILL.md,拿到常用操作、分类和分支条件。
  3. 任务涉及表单时,它再读取 forms.md,而不是预先加载所有高级说明。
  4. 对”提取表单字段””填字段””校验填写结果”这类可确定执行的操作,它调用现成脚本并检查产物。

第四步是很多人没意识到、却最能体现设计的地方。仓库的 scripts/ 里放的不是零散片段,而是一批各司其职的确定性脚本,比如:

1
2
3
4
5
extract_form_field_info.py      # 提取表单里有哪些字段
fill_fillable_fields.py # 按给定值填入可填写字段
check_fillable_fields.py # 检查字段是否真的被正确填上
convert_pdf_to_images.py # 把 PDF 每页转成图片
create_validation_image.py # 生成可供人眼核对的校验图

到这里,开头留下的两个问题就有了着落。先看”遇到扫描件怎么办”:SKILL.md 的 Common tasks 里专门有一条扫描件 OCR,把扫描 PDF 变成可搜索文本,这条分支模型读正文时就能看到。再看”怎么校验填好的表单”:不是让模型”自己看一眼觉得对”,而是跑 check_fillable_fields.py 做程序化检查,必要时再用 create_validation_image.py 渲染出图供人核对。填表单这种既要精确、又容易出错的操作,交给脚本比交给模型的自然语言推理稳得多。

这种分工有两个直接收益。第一,模型不用每次重新组织一套 PDF 库调用方式。第二,复杂规则不会挤占所有 PDF 任务的上下文。官方工程文章也用这个例子说明:代码既能作为 Agent 的工具,也能作为可复用的操作知识。(官方文章)

我从这里得到的写法是:如果某段工作可以被明确输入、稳定执行、方便验证,就优先把它做成脚本;如果它依赖任务判断和上下文取舍,留在 SKILL.md 里;只有少见的分支才放进引用文件。填字段、校验结果属于前者,”这份文档大概是什么类型、该走哪条路”属于后者。这条界线比”把所有经验写进一份超长提示词”更容易维护,也更容易解释每一部分为什么在那里。

动手写一个最小 Skill:博客发布前检查

只读官方仓库,很容易觉得概念都懂了,却不知道从哪下手。下面把前面讲的东西压到一个能跑的最小例子上:给我自己的 Hexo 博客写一个”发布前检查” Skill。它的目录只有两个文件:

1
2
3
4
blog-md-preflight/
├── SKILL.md
└── scripts/
└── check_frontmatter.py

先写 SKILL.md。注意 description 怎么按前面的原则写足触发关键词,正文怎么只放”顺序和判断”、把机械检查甩给脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
---
name: blog-md-preflight
description: Check a Hexo blog post's frontmatter before publishing — verifies
title, tags, categories, cover/top_img image paths, publish date format, and
broken Markdown links. Use before publishing or committing a Hexo article, or
when the user mentions frontmatter, cover image, or 发布前检查.
---

# 博客发布前检查

## 什么时候用
在提交或发布一篇 Hexo 文章前运行。目标是挡住几类低级但常见的错误:
封面路径写错、日期格式不对、frontmatter 少字段、正文里有坏链接。

## 检查顺序
1. 先跑脚本做机械检查:
scripts/check_frontmatter.py <文章路径>
它会检查必填字段、date 格式、cover/top_img 是否为可用 URL。
2. 再由你人工判断脚本查不了的部分:
- 标题是否有关键词、是否是"标题党但空洞"
- 正文里的站内链接是否指向真实存在的文章

## 异常处理
- 脚本报"缺字段":补齐后重跑,不要跳过。
- 脚本报"date 格式可疑"但你确认无误:在提交说明里写清原因,别默默改脚本。

再写那个被指向的脚本。它只做能确定判断的事,把需要人拍板的留给正文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env python3
"""检查一篇 Hexo 文章的 frontmatter,返回非零退出码表示有问题。"""
import re, sys, pathlib

REQUIRED = ["title", "tags", "categories", "cover", "top_img", "date"]
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$")

def main(path):
text = pathlib.Path(path).read_text(encoding="utf-8")
m = re.match(r"^---\n(.*?)\n---", text, re.S)
if not m:
print("✗ 没找到 frontmatter"); return 1
fm = m.group(1)
problems = []
for key in REQUIRED:
if not re.search(rf"^{key}:", fm, re.M):
problems.append(f"缺字段: {key}")
date = re.search(r"^date:\s*(.+)$", fm, re.M)
if date and not DATE_RE.match(date.group(1).strip()):
problems.append(f"date 格式可疑: {date.group(1).strip()}")
for field in ("cover", "top_img"):
v = re.search(rf"^{field}:\s*(.+)$", fm, re.M)
if v and not v.group(1).strip().startswith(("http://", "https://", "/")):
problems.append(f"{field} 不像可用路径: {v.group(1).strip()}")
for p in problems:
print("✗", p)
print("✓ frontmatter 通过" if not problems else f"共 {len(problems)} 处问题")
return 1 if problems else 0

if __name__ == "__main__":
sys.exit(main(sys.argv[1]))

这个例子小,但它把本文的几个点都串了起来:description 写足了触发关键词(Hexo、frontmatter、cover、发布前检查);正文只保留顺序和异常处理这类需要判断的内容;机械、可验证的检查(字段在不在、date 像不像、路径像不像)交给脚本,模型不必自己去数。

要验证它到底有没有用,就用三篇文章各跑一遍:一篇故意缺封面、一篇日期格式写错、一篇完全正确,比较有 Skill 和没 Skill 时模型漏检的情况。这个练习比照抄一个大型 Skill 更值,因为它逼你回答三个问题:Agent 为什么需要它、哪些内容必须进上下文、怎样证明它确实减少了错误。

用评测判断 Skill 是否值得保留

一个 Skill 看起来很完整,并不等于它真的有用。Anthropic 在公开建议中强调从评测开始:先拿代表性任务观察 Agent 在哪里失败,再把失败模式整理成 Skill,最后比较加 Skill 前后的效果。

我会用一个很小的闭环开始,而不是一上来搭复杂基准:

1
2
3
4
5
任务集:3 到 5 个真实任务
对照组:不加载 Skill 的 Agent
实验组:加载 Skill 的 Agent
检查项:结果正确性、步骤是否遗漏、耗时、工具调用失败率
复查:人工看一遍关键产物与失败样本

“失败模式 → 写成 Skill”这句话有点抽象,还是用上面的博客检查举个实的。假设我先不写 Skill,让 Agent 帮我发布五篇文章,观察到一个稳定的失败模式:它经常忘记检查 date 字段的格式,导致文章排序错乱。这就是一条值得沉淀的失败模式。我把”检查 date 格式”写进 Skill(既进 SKILL.md 的检查顺序,也进脚本的断言),然后用一条可判定的断言来验证它是否补上了这个洞:

1
2
3
断言:对一篇 date 写成 "2026/6/10" 的文章,
实验组必须报出"date 格式可疑",对照组允许漏掉。
跑 5 次,实验组命中 5/5 才算这条失败模式被 Skill 覆盖。

关键不在于凑够某个固定的运行次数或比例,而在于两点:一是每条 Skill 内容都能追溯到一个真实的失败模式,别写”感觉有用”的条款;二是始终保留 baseline(对照组)。没有对照,就很难分清”模型本来就会做”和”Skill 真正补上了什么”。skill-creator 把这件事做得更系统:准备代表性提示词、保留 baseline、对两组输出做断言或人工审阅、再根据失败样本回头调正文和 description,但它的内核就是上面这个闭环。

我的分类框架:能力型与纪律型

下面的分类不是 Agent Skills 规范中的官方术语,而是我用来阅读这个系列文章的一把尺。

类型 要解决的问题 常见内容 更适合的验证方式
能力型 Skill Agent 缺少某项程序、领域或工具知识 PDF 处理、生成文档、调用特定 API 输出正确性、脚本结果、边界案例
纪律型 Skill Agent 知道方法,但容易在压力下跳过步骤 TDD、调试流程、需求澄清、提交前检查 是否遵循流程、是否留下验证证据

Anthropic 仓库中的 PDF、文档编辑和工具使用示例,大多可以放进”能力型”。我之前拆过的 Superpowers、Matt Pocock Skills 中,则有不少内容偏”纪律型”。上面那个博客发布前检查其实两头沾:查字段是能力型,”不许跳过 date 检查”是纪律型。两者都用同一个 SKILL.md 格式,但写法不该完全照搬。

能力型 Skill 要先让模型知道怎样完成任务,脚本和可执行示例往往很重要。纪律型 Skill 则要把触发条件、验收证据和例外情况写清,避免模型用”这次很简单”跳过关键步骤。这个分类只是起点,实际 Skill 经常同时包含两类内容,所以仍然要回到具体失败样本来决定写法。

安装与使用:skill 放在哪、怎么被发现

写出来只是一半,还得让 Agent 真的能加载它。这一节是标准之外的实现细节,我只讲 Claude Code 的情况,别的宿主未必一样。

就 Claude Code 而言,Skill 通常来自三个位置:

来源 放在哪 作用范围
个人 Skill ~/.claude/skills/<name>/ 你所有项目都能用
项目 Skill <项目>/.claude/skills/<name>/ 只在这个项目里生效,可随仓库提交、团队共享
插件 Skill 通过插件安装 由插件作者维护、随版本更新,通常只读

放对位置后,启动时它的 namedescription 就进入前面说的第一层清单,剩下的就交给 description 去匹配。想让某个 Skill 只由人手动触发、不让模型自动选中,一些实现(比如 Matt Pocock 的仓库)用 disable-model-invocation: true 表示。这属于宿主的扩展约定,不在核心规范里,跨平台前要确认对方支不支持。

给初学者一个简单的定位:目录放在 .claude/skills/ 下,name 和目录名一致,description 写足触发词,一个 Skill 就算装好了,不需要额外注册步骤。

使用公开 Skill 前的安全检查

Skill 可以携带指令和脚本,也就可能改变 Agent 的行为边界。安装第三方 Skill 前,我会先读 SKILL.md、脚本依赖和网络请求,确认它不会把本地数据传往未知地址,也不会执行与任务无关的命令。前面那个 allowed-tools 字段在这里就有了双重意义:它一方面替你省授权打断,另一方面也意味着这些命令是被预先放行的,读别人的 Skill 时要特别看清它预授权了什么。官方同样建议只从可信来源安装,并在使用前审查代码与资源。(安全建议)

许可也要单独看。Anthropic 仓库中许多示例采用 Apache 2.0,但 docxpdfpptxxlsx 等文档类 Skill 是 source-available,pdf/SKILL.md 的 frontmatter 里就明确写着 license: Proprietary. LICENSE.txt has complete terms,并不等同于开源软件。将它们用于项目之前,应以目录中实际的许可说明为准。(仓库说明)

几个初学者常踩的坑

把前面的内容反过来说一遍,就是几条容易犯的错:

  • description 写成自我介绍(”这是一个很强大的 PDF 工具”),而不是触发线索。模型靠它做选择,写清动作和场景才有用。
  • 把所有内容堆进 SKILL.md 正文。正文一旦激活就整份进上下文,长说明和低频分支应该拆到引用文件。
  • 让模型用自然语言”校验”确定性结果。能写成脚本断言的检查(字段填没填对、格式合不合规),别指望模型自己看一眼。
  • 不留 baseline 就下结论。没有对照组,你分不清是 Skill 起了作用还是模型本来就会。
  • 假设别的宿主行为和 Claude 一样。加载时机、脚本执行、allowed-toolsdisable-model-invocation 这些都可能因平台而异。

我的结论

Anthropic Skills 让我看到的不是一种”万能提示词格式”,而是一种组织 Agent 经验的方式:用简短元数据做发现,用 SKILL.md 给出主路径,把重资料和确定性操作延后到需要时再使用。它省的不是你写字的功夫,而是每一次对话都要重复付出的上下文成本,以及模型在无关内容里找相关内容的精力。

如果要把这篇的结论落到实践上,我会按这个顺序做:先写一个只有 namedescription 和核心步骤的最小 Skill;拿真实任务和 baseline 比较;确认某一步重复且可验证后再补脚本;确认某段说明只在少数分支用到,再把它拆进引用文件;最后才扩充参考资料。这样写出来的 Skill 更容易解释,也更容易证明它给项目带来了什么。


参考资料