06 · 把重复的话写成文件
Skills 不是给 Claude 装新能力,而是把你反复说的流程写成文件。description 决定何时触发,正文决定怎么做。
翔宇有一段时间每天都跟 Claude Code 说同样的话:处理 PDF 先提取文本,再处理表格;扫描件走 OCR;表格结果转 CSV;最后给一份可复核摘要。说了十几遍以后突然意识到——这不是提示词技巧,这是一个应该写进文件的工作流。——翔宇
这一篇用 15 分钟换什么:前 5 篇分别讲了位置、上下文、记忆、提示词和思考深度。现在进入第一个"复用层":Skills(技能)。读完你会知道,什么该写进 CLAUDE.md,什么该写成 SKILL.md,什么时候让 Claude 自动触发,什么时候必须你手动输入 /skill-name。
1. 你每天重复的不是"知识",是"流程"
先看一个具体场景。
你让 Claude 处理一个 PDF:
👤 你:帮我把这个 PDF 里的表格提出来。
Claude 能做吗?能。第 1 篇讲过,Claude Code 住在你电脑上。你电脑装了 pdfplumber、OCR 工具、Python 脚本,它就能读文件、跑命令、改脚本。
但第一次结果常常不稳。它可能先尝试普通文本提取,漏掉扫描页;也可能把表格当段落处理;还可能直接给你总结,没有留下 CSV。
你只好补一句:
处理 PDF 时先判断是不是扫描件。普通 PDF 用 pdfplumber 提取文本和表格。
扫描件先 OCR。表格转 CSV,保留页码,最后输出摘要和异常页列表。第二天又来一个 PDF。你再说一遍。
第三天又说一遍。
这时要停下来判断:你重复的不是"PDF 是什么",也不是"pdfplumber 怎么安装"。Claude 大概率知道这些。你重复的是你的处理流程。
第一性原理:Skill 解决的不是"Claude 不会什么",而是"Claude 不知道你遇到某类任务时希望它按什么流程做"。
这句话很关键。很多人写 Skill 写偏,就是因为把 Skill 当成百科词条,开始解释 PDF、CSV、OCR 的定义。真正该写的是:
- 先判断什么
- 用哪个工具
- 遇到什么异常怎么分支
- 输出什么格式
- 做完怎么验收
这些才是你每天反复说的东西。
这一篇要回答的三个核心问题
写一个 Skill 的代码很容易,难的是理解它怎么被 Claude 看见 / 何时被加载 / 跟其它机制怎么分工。后面 12 节就是按这三个问题展开:
- description 写什么 Claude 才会真用它?——§3 看 description 怎么作为"触发索引"被模型在每轮决策前扫一遍;§5 看 frontmatter 控制面板的全部字段。
- 一篇 SKILL.md 几千字 Claude 不会被淹?——§6 看 supporting files 外置;§7 看渐进式加载怎么把"装很多"和"上下文不爆"同时做到。
- Skill 跟 CLAUDE.md / SubAgent / Plugin 边界在哪?——§9 看跟 CLAUDE.md / Hook / MCP 的 4 维对照;§11 看跟 SubAgent 的衔接;12 篇 Plugin 看怎么被打包分发。
读完每一节自检:这一节回答了哪个问题、用什么机制回答的、为什么不是另一种实现。
2. 写进 SKILL.md
最小 Skill 只有一个文件:SKILL.md。
目录长这样:
文件可以这样写:
---
description: 从 PDF 中抽取文本和表格。用户提到处理 PDF、抽表格、跑 OCR、把 PDF 数据转成 CSV 时使用。
---
## 工作流程
1. 先检查 PDF 本身,判断每页是文本型还是扫描图像。
2. 文本型页面用 pdfplumber 抽取文本和表格。
3. 扫描页面先跑 OCR 再抽取。
4. 提取出来的表格按页码存成 CSV。
5. 返回摘要:包含产出文件、跳过的页、置信度偏低的 OCR 段落。注意它没有写"PDF 是 Portable Document Format"。也没有写"OCR 是光学字符识别"。这些不是你的流程,是百科。
它写的是:遇到 PDF 任务时,Claude 应该怎么做。
一句话理解:SKILL.md 是"遇到这类任务时,请按这个流程处理"的说明书。它不是代码,也不是插件,更不是知识库百科。
这就是 Skills 和第 4 篇提示词的关系:提示词是本轮临时说明,Skill 是反复出现的说明。当你发现一段提示词第三次出现,就该考虑把它写成 Skill。
新手最常见的写法误区:把 Skill 当百科词条写。开篇先解释"PDF 是什么"、"OCR 是什么",然后才进流程。Claude 已经知道这些定义,正文应该直接进流程。把"概念解释"塞进 SKILL.md 等于让 Claude 每次触发都把百科再"读"一遍——既占上下文又稀释流程主线。
3. description 才是触发器
只写流程还不够。Claude 怎么知道什么时候读这个文件?
关键在 frontmatter(文件开头 --- 之间的元数据)。
官方文档说,每个 Skill 需要 SKILL.md,frontmatter 告诉 Claude 什么时候用,正文告诉 Claude 用的时候怎么做。description 帮 Claude 决定是否自动加载这个 Skill。完整说明见 Extend Claude with skills。
所以 description 不是简介文案,它是触发索引。
坏写法:
---
description: 处理文件相关的任务。
---这个描述太泛。PDF 是文件,图片是文件,代码也是文件。Claude 无法判断它什么时候该触发。
好写法:
---
description: 从 PDF 中抽取文本和表格。用户提到处理 PDF、抽表格、跑 OCR、把 PDF 数据转成 CSV 时使用。
---这里有四类触发词:PDF、抽表格、OCR、CSV。用户说"把这个扫描 PDF 里的发票表格转成 CSV",Claude 就容易匹配上。
flowchart LR
User["👤 用户请求<br/>把扫描 PDF 表格转 CSV"]
Index["📇 Skill 索引<br/>description 常驻上下文"]
Match{"匹配到<br/>PDF / OCR / CSV?"}
Load["📖 加载 SKILL.md 正文"]
Work["🛠️ 按流程执行"]
User --> Index
Index --> Match
Match -->|是| Load
Match -->|否| Work
Load --> Work
style Index fill:#dbeafe,stroke:#3b82f6
style Match fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style Load fill:#dcfce7,stroke:#22c55e
要写好 description,得先理解它什么时候被读、被谁读。
注册时机:会话启动时,Claude Code 把所有可用 Skill 的 name + description + frontmatter(不含正文)拼进 system prompt(system prompt = 启动时 Claude 看到的指令背景)注册,相当于给 Claude 摆了一份"工具菜单"。
决策时机:你每发一句话,Claude 在生成回答前会扫一遍菜单,决定要不要打开某个 Skill 的正文。这跟 09 篇 MCP 里 tool 的发现机制是同一类——区别是 Skill 描述的是"工作流",MCP tool 描述的是"动作"。
Token 经济学:每个 Skill metadata 大约 50-150 token。装 50 个 Skill 启动时常驻约 5-8 K token——只占 1M 上下文窗口的不到 1%,但占启动 system prompt 的相当一块。这就是为什么 description 写法直接决定 Skill 是不是真有用:
| 写法 | 后果 |
|---|---|
| 写得太宽("处理文件相关的任务") | 模型每轮都把它当候选,误触发率高 —— 用户说"打开 README" 也可能错触发,主对话被 SKILL.md 正文淹 |
| 写得太窄("处理 2026 Q4 发票 PDF") | 模型在用户说"提取这个 PDF"时识别不到,该用没用 |
| 写得精准 + 列触发词("从 PDF 抽取... 用户提到 PDF / OCR / CSV 时使用") | 模型只在真相关时拉正文,不污染主对话 |
为什么 Anthropic 选"模型决策"而不是"keyword 索引":keyword 匹配(grep)会被语义近义、跨语言、措辞变体打败——用户说"扫描发票转表格"包含"发票"、"扫描"、"表格",但没"PDF"也没"CSV",keyword 匹配就漏了。让模型自己读 description 决策能跟人类自然语言对齐,代价是占 system prompt token——这是 Anthropic 在"准确率 vs 上下文成本"之间选了准确率。
类比:description 像菜单上的菜名 + "什么时候点"。菜名太泛("美食")服务员(Claude)不知道你想吃什么;菜名太窄("周三限定×××套餐")你点了别的相似菜它也认不出。
这里有一个设计细节:Claude 会自动判断,但你也可以手动调用。
如果 Skill 叫 pdf-workflow,你可以直接输入:
/pdf-workflow 处理 invoice.pdf官方文档也明确说,custom commands 已经合并进 Skills:.claude/commands/deploy.md 和 .claude/skills/deploy/SKILL.md 都能创建 /deploy,现有 .claude/commands/ 还可用,但 Skills 支持 supporting files、frontmatter 控制自动触发等更多能力。
关键点:不要把 Skill 理解成"只能自动触发"。它同时有两种入口:Claude 觉得相关时自动加载;你也可以用 /skill-name 手动调用。
4. 什么时候自动,什么时候手动
这一步很容易混。
不是所有 Skill 都应该让 Claude 自动触发。
PDF 提取这种流程,自动触发没问题。用户说 PDF、OCR、表格,Claude 自动用它,符合预期。
但 PDF 自动覆盖原文件 / 处理完直接发邮件给客户这种有副作用的流程,不能让 Claude 自己看起来"时机合适"就执行。
这时用 disable-model-invocation: true:
---
name: pdf-archive-and-email
description: 处理 PDF 后用结果覆盖原文件并发邮件给收件人。
disable-model-invocation: true
---意思是:Claude 不会自动调用,只有你输入 /pdf-archive-and-email 才会触发。
另一个方向也存在:有些 Skill 只是背景知识,不适合人手动调用。比如:
---
name: pdf-legacy-format-notes
description: 说明 2020 年以前归档 PDF 的特殊格式约定与解析陷阱。
user-invocable: false
---它可以让 Claude 在相关任务中自动加载(用户处理老 PDF 时自动参考),但不出现在你日常要手动执行的命令入口里。
可以这样判断:
- PDF 提取、读取分析、生成摘要:默认自动即可,Claude 能安全自动触发。
- PDF 自动覆盖、自动发邮件、自动归档删源文件:
disable-model-invocation: true,必须由你决定时机。 - PDF 历史格式说明、领域词典、旧系统约定:
user-invocable: false,给 Claude 读,不给人当命令用。
关键原则:只要一个 Skill 会产生真实外部副作用(覆盖文件 / 发消息 / 调用付费 API / 删数据),就不要让它自动触发。自动触发适合"指导 Claude 怎么思考和处理",不适合"替你决定何时发布、提交或通知别人"。
为什么 Anthropic 设计成"默认允许 + 高危 opt-out"而不是反过来:
如果默认全部禁止自动触发,所有 Skill 都要 /skill-name 手动调——失去 Skill 最大价值"按需自动加载"。如果默认全部允许自动,副作用 Skill 危险。Anthropic 选了"默认允许 + 高危显式 opt-out"——把"什么算危险"的判断权交给 Skill 作者,因为:
- 平台不知道每个 Skill 实际做什么(覆盖文件?只读分析?)
- 静态扫描判断"是否有副作用" 太弱(脚本里调外部 API 难自动识别)
- 让作者声明意图,比让平台猜更准确
代价:坏作者可以写"自动覆盖"还不加 opt-out。这就是 12 篇 Plugin 强调"插件是 high-trust 组件"的根因——Skill 系统的安全模型依赖作者诚信 + 安装者审查,不靠平台兜底。
新手最常踩的坑:把"PDF 自动归档" Skill 默认 auto,description 写得宽("处理 PDF 文件")。当用户说"这个 PDF 太长帮我看看" 时,Claude 误识别成归档场景,把原文件覆盖成提取摘要——真实数据没了。修复方式不是改提示词,是改 Skill 配置:副作用 Skill 必须 disable-model-invocation: true。
5. frontmatter 是控制面板
到这里,Skill 已经不是一段纯文字了。frontmatter 让你控制它的工作方式。
常用字段一张表看清:每个字段的作用 + 什么时候用 + 不写会怎样:
| 字段 | 作用 | 什么时候用 | 不写会怎样 / 默认 |
|---|---|---|---|
name | 显示名 / 命令名 | 想固定 /skill-name 调用时 | 默认用目录名(如目录叫 pdf-workflow 就是 /pdf-workflow) |
description | 触发索引 | 几乎每个 Skill 都该写 | 不写 = Claude 完全不知道何时用,等于装了等于没装 |
when_to_use | 追加触发场景 | 触发条件复杂、单条 description 写不完时 | 不写 = 仅靠 description;写了 = 跟 description 叠加增强匹配 |
argument-hint | / 自动补全显示参数提示 | 手动命令型 Skill | 不写 = 手动调用时用户不知道传什么参数(如 /pdf-workflow 不知道传不传路径) |
allowed-tools | 预批准某些工具 | 确认安全、想减少重复确认时 | 不写 = 用户全局权限规则约束(11 篇 Permissions);列了 = 这些工具在 Skill 激活时无需逐次确认 |
model | Skill 激活时临时切模型 | 某类任务固定需要 Opus / Haiku 时 | 不写 = 沿用主会话模型 |
effort | Skill 激活时覆盖思考深度 | 审计类深想、格式类低 effort | 不写 = 沿用主会话 effort(详见 05 篇) |
context | fork 时在子代理上下文运行 | 不想污染主对话时 | 不写 = 在主对话执行;写 fork = 启动 SubAgent(07 篇) |
disable-model-invocation | 禁止自动触发 | 副作用 Skill | 默认 false(允许自动) |
user-invocable | 用户能否手动调用 | 后台知识 Skill | 默认 true(用户可 /cmd) |
第 5 篇刚讲过 effort。这里它开始进入工程配置。
比如 PDF 审查 Skill 需要深度推理:
---
name: pdf-content-review
description: 审查 PDF 中的合同条款是否存在风险点。
effort: xhigh
allowed-tools: Read Grep Glob
---这说明:这个 Skill 主要读文件、搜文本,不应该随便改文件;同时合同审查需要更深推理,所以 effort 提高。
再比如 PDF 格式润色 Skill 路径很直,不需要高 effort:
---
name: pdf-extracted-text-polish
description: 润色已抽取的 PDF 文本格式(断行 / 表头 / 标点),不改事实。
effort: low
---边界要讲清:allowed-tools 是预批准工具,不是唯一可用工具。官方文档说明,它让列出的工具在 Skill 激活时无需逐次确认;其它工具仍受你的 11 篇权限设置 约束。不要把它当成完整沙箱。
新手最常漏写的字段是 argument-hint:写了一个 /pdf-workflow Skill 但不写 hint,用户在终端打 /pdf-workflow 后不知道下一步该输什么——是空格加路径?还是带引号?还是直接回车?hint 一行就解决:argument-hint: "[PDF 路径]"。
6. 支持文件:别把正文写成仓库
很多人第一次写 Skill,会把所有东西都塞进 SKILL.md:
- 20 个示例
- 详细 API 文档
- 项目模板
- 完整检查清单
- 大段背景说明
这样很快失控。3000 行 SKILL.md 触发后会一次性注入主对话——主任务上下文直接被挤压,Claude 还没干活就已经"满桌子",跟 02 篇上下文 讲的"桌子要干净"原则反着干。
官方建议 SKILL.md 控制在 500 行以内。这不是协议硬限,是经验阈值:
- 一行 markdown 平均 8-12 token,500 行 ≈ 5-7 K token
- Skill 触发后正文一次性注入主对话,5-7 K token 是"显著占空间但不挤压主对话"的临界点
- 超过 1 万 token 的 Skill 正文会让 Claude 在每轮都得重读这一大段——上下文经济崩了
- 低于 200 行又意味着流程描述不够细,Claude 仍然要靠猜
所以"500 行" 的本质是:在 Claude 当前 1M 上下文 + 多 Skill 同时启用 + 每轮重读全文的约束下,给单个 Skill 留约 0.5-0.7% 的上下文配额。
大型参考资料应该放到同目录的 supporting files。比如:
SKILL.md 只写导航:
## 附加资源
- 表格抽取边界情况详见同目录 `reference.md`。
- 最终报告格式见 `templates/extraction-report.md` 模板。
- 辅助脚本见 [scripts/extract_tables.py](scripts/extract_tables.py),需要时直接运行。这样 Claude 先读流程。只有当它真的需要处理表格边界、生成报告或执行脚本时,才去看对应文件。
为什么这种"分层"特别划算:你写 100 个 PDF 处理示例放 examples/,单次任务可能只用到 1-2 个发票类。如果全塞 SKILL.md,触发后 100 个示例全进上下文;放 supporting files 只在 Claude 真要参考时 Read 进来——上下文成本差几十倍。
7. 渐进式加载:装很多,但只读当前需要的
Skills 能规模化,靠的是渐进式加载。
它不是启动时把所有 Skill 正文都塞进上下文。它更像三层索引:
flowchart TB
Level1["第 1 层:Skill metadata<br/>name / description 常驻"]
Level2["第 2 层:SKILL.md 正文<br/>触发后加载"]
Level3["第 3 层:supporting files<br/>需要时再读"]
Work["当前任务上下文"]
Level1 -->|匹配用户请求| Level2
Level2 -->|正文提示需要| Level3
Level2 --> Work
Level3 --> Work
style Level1 fill:#dbeafe,stroke:#3b82f6
style Level2 fill:#dcfce7,stroke:#22c55e
style Level3 fill:#fef3c7,stroke:#f59e0b
style Work fill:#f3e8ff,stroke:#a855f7
这解决了一个实际矛盾:
- 你希望装很多 Skill,让 Claude 懂你的各种工作流
- 你又不希望上下文被无关 Skill 塞满
渐进式加载让两件事同时成立。
| 加载层级 | 加载什么 | 为什么这样设计 |
|---|---|---|
| 启动时 | name / description 等元数据 | Claude 需要知道有哪些 Skill 可用 |
| 触发时 | SKILL.md 正文 | 只把当前任务相关流程放进上下文 |
| 执行中 | reference.md / examples/ / scripts/ | 详细资料按需读取,不常驻 |
把"渐进式加载"摊到一次真实会话的时间线上,更直观:
flowchart TB
T0["🚀 <b>T0 启动会话</b><br/>注册全部 Skill metadata<br/>≈ 50-150 token / 个 · <b>常驻整场</b>"]
T1["💬 <b>T1 用户输入</b><br/>把这个 PDF 表格转 CSV<br/>Claude 扫菜单 → 命中 pdf-workflow"]
T2["📖 <b>T2 触发加载正文</b><br/>SKILL.md ≤ 500 行 / 5-7K token<br/>进主对话 · <b>常驻直到 /compact 或 /clear</b>"]
T3["📂 <b>T3 执行中按需读</b><br/>Read reference.md / 跑脚本<br/><b>单次读完不常驻</b>,除非被引入对话"]
T4["✅ <b>T4 完成回报</b><br/>摘要 + 输出文件列表给你"]
T0 --> T1 --> T2 --> T3 --> T4
style T0 fill:#fef3c7,stroke:#f59e0b,stroke-width:2px
style T1 fill:#dbeafe,stroke:#3b82f6
style T2 fill:#dcfce7,stroke:#22c55e,stroke-width:2px
style T3 fill:#f3e8ff,stroke:#a855f7
style T4 fill:#fee2e2,stroke:#ef4444
读一遍这条时间线,几个反直觉的点会变直观:
- metadata 永远在 system prompt:装了 50 个 Skill,不管你今天用不用,启动时这 50 份 metadata 都在线——这是"自动触发"的代价。
- 正文进了主对话就留在主对话:触发后 Skill body 不会"用完释放"。如果同一会话连续触发 5 个 Skill,主对话上下文会累积 5 份正文。这是为什么"不要在一个会话里调一堆 Skill",跟 02 篇 的桌子原则一致。
- supporting files 是真的"按需":只有 Claude 显式 Read 才进上下文,这就是为什么 500 行的硬数据外置最划算——你写 100 个示例放 examples/,Claude 大多数任务只读 1-2 个。
一句话理解:description 像书架目录,SKILL.md 像当前打开的书,references/ 和 scripts/ 像书里的附录。你不会把整座图书馆都摊在桌上。
这也解释了为什么 description 要写得准。metadata 常驻,但正文不常驻。Claude 是否能打开正确那本书,首先取决于目录条目写得清不清楚。
8. 放在哪里:个人、项目、插件
Skill 的位置决定作用范围。
官方列了几类位置,日常最常用的是个人级和项目级:
| 位置 | 路径 | 适合放什么 |
|---|---|---|
| 个人级 | ~/.claude/skills/<skill-name>/SKILL.md | 你自己跨项目都用的 PDF 通用流程 |
| 项目级 | .claude/skills/<skill-name>/SKILL.md | 这个项目独有的 PDF 处理约定(如发票格式 / 业务字段映射),可提交 git |
| 插件级 | <plugin>/skills/<skill-name>/SKILL.md | 随插件分发的 PDF 工具集(详见 12 篇) |
| 企业级 | managed settings | 公司统一下发的合规 PDF 审查 Skill |
如果同名 Skill 出现在多个层级,企业级 > 个人级 > 项目级。插件 Skill 会带插件命名空间(详见 12 篇),避免和普通 Skill 冲突。
还有一个细节:Claude Code 会监听 Skill 目录变化。你在现有 ~/.claude/skills/ 或项目 .claude/skills/ 里新增、编辑、删除 Skill,当前会话内会自动生效。只有顶层 skills 目录本身原来不存在、你刚创建时,可能需要重启 Claude Code 才能被监听。
把它和第 3 篇记忆系统对应起来:
- 个人偏好型 PDF 流程:放
~/.claude/skills/pdf-workflow/ - 团队共享 PDF 流程:放项目
.claude/skills/pdf-invoice/ - 单纯项目事实(用 pdfplumber 不用 PyMuPDF):放
CLAUDE.md - 复杂可复用 PDF 工作流:放
SKILL.md
9. Skills 和 CLAUDE.md / Hook / MCP 的边界
四者都是"配置 Claude 行为"的方式,但职责不同。一张 4 维对照表看清:
| 维度 | CLAUDE.md(03 篇) | SKILL.md(本篇) | Hook(10 篇) | MCP(09 篇) |
|---|---|---|---|---|
| 加载时机 | 每次会话启动全量注入 | 触发后才注入正文 | 事件点自动运行 | 工具被调用时连接 |
| 上下文成本 | 全量常驻(建议 ≤200 行) | metadata 约 50-150 token / Skill;触发后正文进上下文 | Hook 自身不占上下文,输出可能进 | 工具 schema 常驻;输出可能进 |
| 触发条件 | 不需要触发,启动即生效 | description 模型语义匹配 | 事件类型 + matcher | 模型决定 / 用户 @ 引用 |
| 复用粒度 | 项目 / 用户 / 系统 / 本地 4 层 | 项目 / 用户 / 插件 / 企业 4 类 | settings scope(同 CLAUDE.md) | local / project / user 3 类 |
| 典型用法 | 项目规则 / 团队约定 / 个人偏好 | 任务流程 / 领域工作流 | 自动化检查 / 副作用拦截 | 接外部系统 / 数据源 |
| 何时升级 | 重复 ≥3 次的话 → 写 CLAUDE.md | 重复 ≥3 次的流程 → 写 Skill | 漏一次会出事的规则 → 写 Hook | 频繁复制粘贴的外部系统数据 → 装 MCP |
读这张表的诀窍:机制选错的代价——CLAUDE.md 写流程会膨胀;Skill 写规则会"加载晚了不起作用";Hook 写偏好会"动不动就拦你";MCP 写本地操作是"用大炮打蚊子"。每个机制有它最贴的活,错配是大多数 Claude Code 配置失控的根因。
3 个判断练习——下面三句话,分别该写进哪里?读完答案再看你判断对了几个:
场景 A:团队约定 PDF 处理一律用
pdfplumber,不要 PyMuPDF。场景 B:处理 PDF 的完整 7 步流程(判断扫描 → OCR → 抽表 → 转 CSV → 摘要)。
场景 C:每次写 PDF 处理代码后自动跑
pytest tests/pdf_test.py。
📌 看答案
- A → CLAUDE.md:是项目级"用什么库"约定,每次会话都要知道,不需要触发。
- B → SKILL.md:是反复出现的工作流,只在 PDF 任务时才需要。
- C → Hook(PostToolUse):是"动作完成后自动执行"的规则,不依赖 Claude 触发判断。
判断诀窍——问自己:
- 这条信息每次会话都要进上下文吗?是 → CLAUDE.md。
- 这条信息只在某类任务才需要?是 → SKILL.md。
- 这条规则漏一次就出事?是 → Hook。
- 这条信息来自外部系统(GitHub / 数据库 / SaaS)?是 → MCP。
10. 一个合格 Skill 长什么样
回到 PDF 例子。一个更完整的版本可以这样写:
---
name: pdf-workflow
description: 从 PDF 中抽取文本和表格。用户提到处理 PDF、抽表格、给扫描页跑 OCR、把 PDF 数据转成 CSV 时使用。
argument-hint: "[PDF 路径]"
allowed-tools: Read Bash(python *) Bash(mkdir *) Bash(ls *)
effort: medium
---
## 目标
从 PDF 中抽取可用的文本和表格,保留每段内容到对应页码的追踪关系。
## 工作流程
1. 从 $ARGUMENTS 拿到 PDF 路径。
2. 判断每一页是文本型还是扫描图像。
3. 文本型页面用 pdfplumber 抽取。
4. 扫描页面用 OCR 抽取。
5. 表格按页码导出成 CSV。
6. 写一份摘要:含产出文件、跳过的页、置信度偏低的段落。
## 输出
返回:
- 抽取出的文本文件路径
- 各 CSV 文件路径
- 跳过或需人工复核的页
- 一段话摘要这个 Skill 有几个优点:
- description 含清晰触发词(PDF / OCR / CSV / 抽表格)
argument-hint让手动调用更顺allowed-tools只预批准必要命令effort: medium和任务复杂度匹配- 正文写流程,不写百科
- 输出格式明确
再对照一个坏版本:
---
description: 帮忙处理文档。
---
PDF 是 Adobe 推出的文件格式。OCR 指光学字符识别。
请使用工具处理文件,并返回有用的结果。坏在哪里?
- description 太泛:容易误触发,或该触发时不触发。
- 写百科知识:占上下文,不改变执行质量。
- 没有流程:Claude 还是要猜先后顺序。
- 没有输出契约:每次结果格式不稳定。
- 没有边界:可能动不该动的文件或命令。
这就是 Skill 写作的核心:描述要窄,流程要清,输出要定,参考资料要外置。
理解 Skill 的所有设计选择,可以压成一张决策矩阵——这一篇所有"为什么"汇总:
flowchart TB
Core["💡 <b>Skills 的核心设计选择</b>"]
subgraph 触发["🎯 触发方式"]
T1["默认<b>自动</b><br/>+ 高危 opt-out<br/>责任在作者"]
end
subgraph 加载["📦 加载策略"]
L1["<b>渐进式</b>三层<br/>metadata 常驻<br/>正文触发加载<br/>files 按需读"]
end
subgraph 表达["📝 表达层级"]
E1["<b>prompt 层 markdown</b><br/>不是 typed function<br/>不是 flat command"]
end
subgraph 安全["🔒 安全模型"]
S1["作者诚信 + 用户审查<br/>平台不兜底<br/>详见 12 篇 Plugin"]
end
Core --> 触发
Core --> 加载
Core --> 表达
Core --> 安全
style Core fill:#fef3c7,stroke:#f59e0b,stroke-width:3px
style 触发 fill:#dbeafe,stroke:#3b82f6
style 加载 fill:#dcfce7,stroke:#22c55e
style 表达 fill:#f3e8ff,stroke:#a855f7
style 安全 fill:#fee2e2,stroke:#ef4444
4 个选择各自的"为什么不是另一种":
- 触发为什么不是默认全手动:失去自动加载价值,新手记不住命令名 = 装了等于没装。
- 加载为什么不是全量注入:50 个 Skill 全量 ≈ 50 万 token,主对话直接爆。
- 表达为什么不是 typed function:function 强参数校验,但工作流里"这页是不是扫描件"是自然语言判断,schema 表达不了"if-else 内嵌经验"。
- 安全为什么不靠平台:平台不知道每个 Skill 实际做什么,作者声明意图比让平台静态扫描更准确。
底层取舍:Skill 是"用 system prompt token 换语义触发的灵活性"。装得多 = 启动 token 成本上升 + 误触发概率上升;装得少 = 自动复用价值丢失。所以这一篇所有"description 写窄一点"、"500 行外置"、"高危 opt-out" 的劝告,本质都是在帮你对齐这个取舍。
11. 和 SubAgents 的连接
下一篇会讲 SubAgents(子代理 = 派分身做子任务,详见 07 篇)。这里先提前接一条线:Skill 可以在主会话里运行,也可以在 forked subagent context(分叉子代理上下文)里运行。
为什么需要这个?
还是 PDF 例子。普通 PDF 提取,主会话直接做就行。
但如果你让 Claude 批量处理 200 个 PDF,中间会读大量文件、跑很多命令、产出许多日志。这些过程会污染主会话上下文。
这时可以让 Skill 在子代理里跑:
---
name: pdf-batch-workflow
description: 批量处理一整个目录的 PDF,返回抽取出的文本、CSV 文件和异常报告。
context: fork
agent: general-purpose
effort: medium
---主会话只拿回结果摘要,具体处理过程留在子代理上下文里。
实际何时升级到 SubAgent,按这张决策树判断:
flowchart TD
Start["拿到一个反复出现的 PDF 任务"]
Q1{"流程能写清楚吗?<br/>步骤、判断、输出格式"}
Q2{"主对话装得下吗?<br/>过程产出大量日志/文件"}
Q3{"任务之间需要互相对齐吗?<br/>多角色多模块"}
OneShot["单步 prompt 解决<br/>不要做 Skill"]
Skill["写 Skill<br/>放在主对话执行"]
Sub["Skill 用 context: fork<br/>跑在子代理里"]
Teams["不用 Skill<br/>开 Agent Teams"]
Start --> Q1
Q1 -->|否,每次任务都不一样| OneShot
Q1 -->|是,能模板化| Q2
Q2 -->|装得下| Skill
Q2 -->|装不下,过程会污染主对话| Q3
Q3 -->|否,单点任务| Sub
Q3 -->|是,多角色协作| Teams
style Skill fill:#dcfce7,stroke:#22c55e,stroke-width:2px
style Sub fill:#dbeafe,stroke:#3b82f6,stroke-width:2px
style Teams fill:#f3e8ff,stroke:#a855f7,stroke-width:2px
style OneShot fill:#fef3c7,stroke:#f59e0b
读这张图的诀窍:Skill 是默认选项,SubAgent 是"上下文太脏"时的升级,Agent Teams 是"需要协作"时的再升级。从下往上反着选——先问"能不能用 Skill 就解决",再考虑分身和团队。这跟 07 篇 讲的"SubAgent 不是任务拆分仪式" 是同一逻辑。
回到 PDF:
- 单个 PDF 处理 → Skill(主对话)
- 200 个 PDF 批量处理 → Skill +
context: fork(SubAgent) - 200 个 PDF + 财务 / 法务 / 技术多角色审查 + 互相确认 → 不用 Skill,开 Agent Teams(08 篇)
提前剧透:当一个 Skill 的执行过程很长、会读很多文件、会跑很多命令时,就开始接近 SubAgent 的使用场景。
12. 检验你真懂了吗
试着用自己的话回答:
- 有人说"Skill 就是给 Claude 装插件"。你能解释为什么这个说法不准确吗?对应 §1 + §2。
- 为什么
description比正文第一段更像 Skill 的入口?一个太泛的 description 会造成什么问题?对应 §3。 - 动手题 ⭐:现在打开一个真实项目,写一个 Skill 的
description字段(≤80 字),让 Claude 在用户说"扫描发票转表格"时触发,但在用户说"打开这个 PDF 让我看看" 时不触发。写完检查:触发词覆盖了"扫描"、"发票"、"表格" 三类输入吗?有没有把"PDF" 单字作为唯一锚点(这会让"看这个 PDF" 也命中)?
过关标准:能用一句话说清——Skill 是按需加载的工作流文件:description 负责触发,正文负责流程,supporting files 负责深层资料;长期事实进 CLAUDE.md,任务流程进 SKILL.md。 加上动手题真写过一遍 description,你才算真会。
13. 还没回答的问题
为了不把本篇撑爆,几个进阶问题被推到后面或官方文档:
- 多个 Skill 命名冲突时怎么办? → §6 命名空间已点;完整规则在 12 篇 Plugin 的 namespace 章节
- Skill 内部能不能
@import引用其它 markdown? → 行为类似 03 篇 CLAUDE.md 的 import;具体限制看 Anthropic skills 文档 当前版本 - Skill 能不能跨会话保留状态? → 不能直接保留;要持久化用 03 篇 Auto Memory 或自己写文件
- Skill 跟 Permissions / Hooks 怎么联动? → Skill frontmatter
allowed-tools是 Permission 的预批准入口;Skill 触发后仍受 10 篇 Hooks 拦截 - Skill 怎么版本化分发? → 单仓 Skill 跟 git 走;要打包给团队 / 社区,看 12 篇 Plugin 的 marketplace 章节
- Skill description 实际匹配的算法是什么? → 当前实现是模型基于 system prompt 决策(详见 §3),具体调权细节官方未公开
把这些标记出来不是写不全,是想让你知道:Skill 是个完整子系统,但它依赖 CLAUDE.md / Permission / Hook / Plugin 这些相邻系统。理解 Skill 的最大杠杆,是搞清楚它跟谁联动、谁负责什么——本篇 §9 对照表和 §11 决策树就是这层联动的入口。
📖 本篇术语速查表
- Skill:技能,按需加载的任务流程文件。
SKILL.md:Skill 入口文件,frontmatter + 正文流程。- frontmatter:文件头元数据,
---包裹的配置字段。 - description:触发描述,Claude 判断是否加载 Skill 的主要依据。
- supporting files:支持文件,Skill 目录下按需读取的参考、模板、脚本。
- slash command:斜杠命令,用
/skill-name手动调用 Skill 的入口。 disable-model-invocation:禁止模型自动调用,让 Skill 只能由用户手动触发。user-invocable:用户是否可调用,设为 false 时可作为后台知识 Skill。- system prompt:系统提示,启动时 Claude 看到的指令背景,Skill metadata 注册在这里。
- forked subagent context:分叉子代理上下文,Skill
context: fork时启动的隔离上下文(详见 07 篇)。
官方资料
接下来去哪
07 · 派助手去干活
Skill 解决"流程复用"。下一篇讲 SubAgents:怎么把长任务放到另一张桌子上,不污染主会话。
05 · AI 怎么决定想多深(上一篇)
复习 effort:很多 Skill 应该把思考深度写进 frontmatter,而不是每次临场手调。
03 · 怎么记住你的习惯
分不清 CLAUDE.md 和 SKILL.md 时,回到记忆篇:长期身份和规则进记忆,任务流程进 Skill。
如果你只记一个判断:凡是你第三次复制粘贴给 Claude 的流程,就该考虑写成 Skill。