本教程面向已经用 Claude Code 跑通过项目、想把自己的重复劳动固化成"一次编写、自动触发"能力的开发者。学完你将掌握一个自定义 Skill(技能)的完整解剖结构、description 触发工程的写法、高/中/低三档自由度的取舍、渐进式披露的多文件组织方式,以及最关键的——评测驱动的迭代闭环。全程用一个可落地的 documenting-functions(函数文档字符串生成)技能做载体,从空目录一路带到发布。
~/.claude/skills/(个人级)与项目 .claude/skills/(项目级)两个技能目录的区别很多教程一上来就让你"建个 SKILL.md",但没说清技能的本质,导致写出来的东西不触发、或触发了也用不对。官方把 Skill 定义得很明确:
一个 Skill 就是一个文件夹,里面有指令、脚本和资源。文件夹的入口是
SKILL.md,它带一段 YAML 头部(frontmatter),告诉 Claude 这个技能做什么、什么时候用。
技能和普通提示词最大的区别在于渐进式披露(progressive disclosure)这条运行时规则:
name 和 description 预加载进系统提示词;SKILL.md 正文;这意味着两件事:第一,description 是技能能不能被选中的唯一门票;第二,技能里塞再多文件都不怕,因为不读就不花钱。下面这张图把这套机制画清楚了:
flowchart TD
A[启动: 加载所有技能的 name+description] --> B{用户发请求}
B -->|描述命中| C[触发目标技能]
C --> D[读取 SKILL.md 正文]
D --> E{需要更多细节?}
E -->|是| F[按需读 reference / 执行 scripts]
E -->|否| G[执行任务]
F --> G
B -->|描述不命中| H[该技能文件零 token 占用]
理解了机制,我们再看结构。一个成熟技能的目录长这样:
documenting-functions/
├── SKILL.md # 主指令(触发后才加载)
├── reference/ # 参考资料(按需加载)
│ ├── python.md
│ ├── javascript.md
│ └── go.md
└── scripts/ # 工具脚本(执行,不进上下文)
└── extract_signatures.py
SKILL.md 顶部的 frontmatter 只有两个必填字段,但每条都有硬约束,违反就直接校验失败或无法触发。
name 字段anthropic、claude命名建议用动名词形式(gerund,动词+-ing),因为它最能表达"这个技能提供的能力是一种活动"。官方给的正面例子是 processing-pdfs、analyzing-spreadsheets、testing-code;可接受的备选是名词短语 pdf-processing 或动作型 process-pdfs。要避开的是 helper、utils、tools 这种含糊名,以及一个技能库里命名风格前后不一致。
description 字段这是整篇教程最重要的一节,下文单独展开。
补充:
license是可选字段;旧版规范里的allowed-tools在新版里也已变为可选。先只关心name和description就够了。
官方文档有一句话点透了它的分量:
description字段让技能可被发现。Claude 用它从潜在 100 多个技能里挑出正确的那一个。
换句话说,这 1024 个字符是技能在"海选"阶段的唯一发言机会。写不好,正文写得再漂亮也没人加载。
description 会被注入系统提示词,人称混乱会导致识别问题。
✅ 好:Generates docstrings for Python functions and adds JSDoc to JS methods.
❌ 坏:I can help you write docstrings.
❌ 坏:You can use this skill to document functions.
只写"做什么"不够,必须补上"什么场景下该选我"。下面三组对照来自官方:
# PDF 处理
✅ Extract text and tables from PDF files, fill forms, merge documents.
Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.
# Excel 分析
✅ Analyze Excel spreadsheets, create pivot tables, generate charts.
Use when analyzing Excel files, spreadsheets, tabular data, or .xlsx files.
# Git 提交
✅ Generate descriptive commit messages by analyzing git diffs.
Use when the user asks for help writing commit messages or reviewing staged changes.
反面教材是这类空话,它们等于没写:Helps with documents. / Processes data. / Does stuff with files.
when 子句里要尽量包含用户嘴边会冒出来的关键词。写文档技能时,人们可能说"给函数加注释"、"补上 docstring"、"这个模块缺 API 文档"、"JSDoc 写一下"——把这些词都写进 description,命中率会显著提升。
先写一个差的:
description: Helps document code.
再迭代成符合三条规则的版本(这就是我们最终采用的):
description: Generates standards-compliant docstrings and API comments for
functions and methods — Google/NumPy/Sphinx style for Python, JSDoc for
JavaScript/TypeScript, godoc for Go. Use when the user asks to document,
annotate, or add docstrings/comments to a function, class, method, or
module, or when improving API documentation coverage.
注意它一次满足了:第三人称、what(生成符合规范的 docstring/JSDoc/godoc)、when("document / annotate / add docstrings / 提升文档覆盖率"等触发词)、关键术语(Google/NumPy/Sphinx/JSDoc/godoc)。
写正文前,先判断这个技能该给 Claude 多大的发挥空间。官方用一个比喻很到位:把 Claude 想象成一个探路的机器人——
三档对应到写法:
| 自由度 | 写法 | 适用场景 |
|---|---|---|
| 高(文本指令) | 列要点、给启发式 | 多种方法都对、依赖上下文判断 |
| 中(带参数的伪代码/脚本) | 给首选模式 + 可配置 | 有偏好模式、允许变体 |
| 低(固定脚本、几乎无参数) | "只许这么做" | 脆弱易错、一致性至上 |
我们的文档技能主要是中到高自由度:模板是首选(中),但具体措辞要据函数语义调整(高)。下面正文就按这个定位来写。
正文的核心原则是精简。官方的原话是:"上下文窗口是公共资源。"你要反复拷问每一段:"Claude 真的需要这段解释吗?它本来不知道这个吗?"一个经典反面:
# ❌ 太啰嗦(约 150 token)
PDF(便携式文档格式)是一种常见的文件格式,包含文本、图像等内容。
要提取文本,你需要用一个库。有很多库可以选,pdfplumber 比较好用……
# ✅ 精简(约 50 token)
## 提取 PDF 文本
用 pdfplumber:
import pdfplumber
with pdfplumber.open("file.pdf") as pdf:
text = pdf.pages[0].extract_text()
精简版默认 Claude 已经知道 PDF 是什么、库怎么用。两条硬指标记住:正文不超过 500 行,超了就拆文件;引用只允许一层深(SKILL.md 直接指向 reference 文件,不要 reference 互相套娃,否则 Claude 会用 head -100 之类部分读取,漏掉关键信息)。
此外几个反模式要绕开:
reference/guide.md,绝不写 reference\guide.md,Windows 上也用正斜杠ServerName:tool_name,否则多服务器时会"工具找不到"documenting-functions现在把前面所有概念落到一个真实可跑的技能上。目标:用户说"给 utils.py 的函数补文档",技能自动生成符合各语言规范的注释。
mkdir -p ~/.claude/skills/documenting-functions/{reference,scripts}
SKILL.md---
name: documenting-functions
description: Generates standards-compliant docstrings and API comments for
functions and methods — Google/NumPy/Sphinx style for Python, JSDoc for
JavaScript/TypeScript, godoc for Go. Use when the user asks to document,
annotate, or add docstrings/comments to a function, class, method, or
module, or when improving API documentation coverage.
---
# Documenting Functions
## Quick start
1. Determine the target language from the file extension.
2. Run the signature extractor to inventory every function.
3. Write a docstring/comment following that language's convention.
## Conventions
- **Python** (.py): Google-style docstrings → see [reference/python.md](reference/python.md)
- **JavaScript/TypeScript** (.js/.ts/.tsx): JSDoc → see [reference/javascript.md](reference/javascript.md)
- **Go** (.go): godoc conventions → see [reference/go.md](reference/go.md)
## Signature extraction
List every function/method and its parameters before writing docs:
python scripts/extract_signatures.py path/to/file.py
## Rules
- Document every **public** function or method. Skip names starting with `_`
unless the user explicitly asks.
- Infer parameter and return types from the code; mark unknowns as `(unknown)`
rather than guessing.
- Keep summaries to one imperative sentence.
## Workflow checklist
Copy and track your progress:
[ ] Identified the language
[ ] Extracted signatures
[ ] Wrote a docstring for each public function
[ ] Listed Args / Returns / Raises (or the language equivalent)
[ ] Did not document trivial one-line getters
reference/python.md:
# Python docstrings (Google style)
## Template
"""One-line summary in imperative mood.
Optional extended description.
Args:
param_name (type): Description.
Returns:
type: Description.
Raises:
ExceptionType: When this happens.
"""
## Rules
- First line ends with a period and fits on one line.
- Document every argument, return value, and raised exception.
reference/javascript.md:
# JSDoc
## Template
/**
* One-line summary.
*
* @param {Type} paramName - Description.
* @returns {Type} Description.
*/
## Rules
- Always include @param and @returns for non-void functions.
- Use {Type} braces for type annotations.
reference/go.md 留作练习,写 godoc 约定即可。这样设计的好处是:用户只写 Python 时,JS 和 Go 两个文件完全不被读取,零 token 浪费。
scripts/extract_signatures.py按官方"提供工具脚本"原则——能预制的脚本比让 Claude 现写更可靠、更省 token、更一致。同时遵守"自己处理错误,别甩给 Claude":
#!/usr/bin/env python3
"""List every function/method signature in a Python file."""
import ast
import sys
def main(path: str) -> None:
try:
with open(path, encoding="utf-8") as f:
tree = ast.parse(f.read(), filename=path)
except FileNotFoundError:
print(f"file not found: {path}", file=sys.stderr)
sys.exit(1)
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
args = [a.arg for a in node.args.args]
kind = "method" if isinstance(node, ast.FunctionDef) else "function"
print(f"{kind} {node.name}({', '.join(args)}) # line {node.lineno}")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("usage: extract_signatures.py <file.py>", file=sys.stderr)
sys.exit(1)
main(sys.argv[1])
注意它示范了官方强调的两个好习惯:try/except FileNotFoundError 显式兜底而不是抛给模型;参数个数校验给出明确的 usage 信息。
新开一个 Claude Code 会话(让技能被重新加载),扔一句不含技能名的请求:
帮我把 utils.py 里所有函数都补上规范文档
预期结果:Claude 选中 documenting-functions,先跑 extract_signatures.py 列出函数,再按 Google 风格逐个补 docstring,跳过 _ 开头的私有函数。
如果没触发,第一反应永远是回去改 description 的 when/触发词,而不是改正文。
这是新手最常踩的坑——凭想象写一大堆指令,结果真实任务里根本用不上。官方的评测驱动开发(evaluation-driven development)顺序是反过来的:
评测本身是一个结构化文件,官方给出的格式如下(我们为文档技能改写):
{
"skills": ["documenting-functions"],
"query": "Add Google-style docstrings to every public function in utils.py",
"files": ["utils.py"],
"expected_behavior": [
"Reads utils.py and lists all public functions (skips names starting with _)",
"Generates Google-style docstrings with Args/Returns/Raises sections",
"Infers parameter and return types from the code",
"Does not document trivial one-line getters unless asked"
]
}
文档里特别说明:目前没有官方内置的评测运行器,需要你自建一套(社区里 skill-creator 插件就提供了评测 + 方差分析的能力,可作为脚手架)。评测就是衡量技能效果的"真相之源"。
官方推荐的高效迭代模式是用两个 Claude 实例:
flowchart TD
A["Claude A(作者): 撰写/改进 SKILL.md"] --> B["Claude B(执行者): 带上技能跑真实任务"]
B --> C{观察 Claude B 行为}
C -->|发现问题| D["记录: 哪里没触发 / 哪里出错 / 漏读哪个文件"]
D --> A
C -->|表现良好| E[发布或分享给团队]
关键在于基于观察改,而不是基于假设改。比如你发现 Claude B 给私有函数 _helper 也补了文档,那回到 Claude A,把 SKILL.md 里"跳过 _ 开头"这条规则上移并加粗(官方建议用 MUST skip 而不是 always skip,措辞强度也影响执行率)。再比如你发现 Claude B 从不点开 reference/go.md,那要么是没人写 Go,要么是正文里的链接不够显眼——按真实观察决定,而不是猜。
还有个常被忽略的点:观察 Claude 是怎么用技能的。它读文件的顺序和你预期一样吗?有没有反复读同一个文件(说明那部分该挪进 SKILL.md 正文)?有没有某个 reference 从没被碰过(说明可能多余或信号太弱)?这些都比凭空优化有效得多。
把从零到发布的所有环节串起来:
flowchart LR
A["1.命名与定位<br/>(gerund, ≤64, 禁保留词)"] --> B["2.写 description<br/>(第三人称+what+when)"]
B --> C["3.选自由度<br/>(高/中/低)"]
C --> D["4.写正文<br/>(精简, ≤500行)"]
D --> E["5.组织多文件<br/>(渐进式披露, 一层深)"]
E --> F["6.加脚本<br/>(自己处理错误)"]
F --> G["7.评测迭代<br/>(Claude A↔B)"]
G -->|未达标| B
G -->|达标| H[发布/分享]
技能死活不触发。 99% 是 description 问题。检查:是不是用了第一/第二人称?是不是只写了 what 没写 when?触发词和用户真实说法对得上吗?把当着用户面会说的词原样写进去。
触发了但执行不对。 先确认自由度档位。该用低自由度(固定脚本)的别给高自由度文本;反之亦然。再看正文是否被 Claude 部分读取——多半是引用嵌套超过一层。
正文太长总被截断。 超过 500 行就拆。长 reference 文件在顶部加一个目录(Contents),让 Claude 即便用 head 预览也能看到全貌。
脚本找不到 / 工具报错。 路径用正斜杠;MCP 工具用 ServerName:tool_name 全限定名;依赖包在 SKILL.md 里显式声明并验证可用(注意 claude.ai 能联网装包,API 环境不能)。
技能在某些模型上表现差。 官方明确要求"在计划使用的所有模型上测试":Haiku 要更详细的指引,Sonnet 看是否清晰高效,Opus 看是否过度解释。同一个技能跨模型时,目标写成"三种都过得去"。
https://platform.claude.com/docs/en/agents-and-tools/agent-skills —— 先读"技能如何工作"理解渐进式披露的底层架构https://code.claude.com/docs/en/skills —— 个人级/项目级技能的创建与分享skill-creator 插件:https://claude.com/plugins/skill-creator —— 官方维护的技能开发、评测、方差分析脚手架documenting-functions 并跑通评测)+ 分组互测 30 分钟,合计约 2.5 小时。[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
