首页 › tutorials › 从零打造首个自定义Skill

概述

本教程面向已经用 Claude Code 跑通过项目、想把自己的重复劳动固化成"一次编写、自动触发"能力的开发者。学完你将掌握一个自定义 Skill(技能)的完整解剖结构、description 触发工程的写法、高/中/低三档自由度的取舍、渐进式披露的多文件组织方式,以及最关键的——评测驱动的迭代闭环。全程用一个可落地的 documenting-functions(函数文档字符串生成)技能做载体,从空目录一路带到发布。

前置条件


一、先搞清楚:一个 Skill 到底是什么

很多教程一上来就让你"建个 SKILL.md",但没说清技能的本质,导致写出来的东西不触发、或触发了也用不对。官方把 Skill 定义得很明确:

一个 Skill 就是一个文件夹,里面有指令、脚本和资源。文件夹的入口是 SKILL.md,它带一段 YAML 头部(frontmatter),告诉 Claude 这个技能做什么、什么时候用。

技能和普通提示词最大的区别在于渐进式披露(progressive disclosure)这条运行时规则:

这意味着两件事:第一,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

二、YAML 头部:两条铁律

SKILL.md 顶部的 frontmatter 只有两个必填字段,但每条都有硬约束,违反就直接校验失败或无法触发。

name 字段

命名建议用动名词形式(gerund,动词+-ing),因为它最能表达"这个技能提供的能力是一种活动"。官方给的正面例子是 processing-pdfs、analyzing-spreadsheets、testing-code;可接受的备选是名词短语 pdf-processing 或动作型 process-pdfs。要避开的是 helper、utils、tools 这种含糊名,以及一个技能库里命名风格前后不一致。

description 字段

这是整篇教程最重要的一节,下文单独展开。

补充:license 是可选字段;旧版规范里的 allowed-tools 在新版里也已变为可选。先只关心 name 和 description 就够了。

三、描述工程:决定技能生死的 1024 个字符

官方文档有一句话点透了它的分量:

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.

规则二:what + when,缺一不可

只写"做什么"不够,必须补上"什么场景下该选我"。下面三组对照来自官方:

# 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

先写一个差的:

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 当成已经很聪明的人

正文的核心原则是精简。官方的原话是:"上下文窗口是公共资源。"你要反复拷问每一段:"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 之类部分读取,漏掉关键信息)。

此外几个反模式要绕开:

六、实战:从零构建 documenting-functions

现在把前面所有概念落到一个真实可跑的技能上。目标:用户说"给 utils.py 的函数补文档",技能自动生成符合各语言规范的注释。

步骤 1:建目录

mkdir -p ~/.claude/skills/documenting-functions/{reference,scripts}

步骤 2:写 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

步骤 3:写各语言的 reference(渐进式披露的"第二层")

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 浪费。

步骤 4:写工具脚本 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 信息。

步骤 5:验证触发与执行

新开一个 Claude Code 会话(让技能被重新加载),扔一句不含技能名的请求:

帮我把 utils.py 里所有函数都补上规范文档

预期结果:Claude 选中 documenting-functions,先跑 extract_signatures.py 列出函数,再按 Google 风格逐个补 docstring,跳过 _ 开头的私有函数。

如果没触发,第一反应永远是回去改 description 的 when/触发词,而不是改正文。

七、评测驱动迭代:别先写文档,先写评测

这是新手最常踩的坑——凭想象写一大堆指令,结果真实任务里根本用不上。官方的评测驱动开发(evaluation-driven development)顺序是反过来的:

  1. 找缺口:不带任何技能,让 Claude 跑代表性任务,记录它哪里翻车
  2. 写评测:针对这些缺口造至少三个场景
  3. 定基线:测量"无技能"时的表现
  4. 写最小指令:只写够通过评测的内容
  5. 迭代:跑评测、对比基线、精修

评测本身是一个结构化文件,官方给出的格式如下(我们为文档技能改写):

{
  "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 A / Claude B 双实例迭代环

官方推荐的高效迭代模式是用两个 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 看是否过度解释。同一个技能跨模型时,目标写成"三种都过得去"。

十、延伸阅读


训练营适用性评估

参考来源

  1. Skill authoring best practices — Claude API Docs — 官方权威指南,frontmatter 规则、description 写法、自由度、评测驱动开发均出自此处
  2. Extend Claude with skills — Claude Code Docs — Claude Code 中技能的创建、管理与分享(官方文档)
  3. Skill Creator — Claude Plugin | Anthropic — 官方技能开发与评测/方差分析脚手架插件
  4. Claude Code Skills: The Complete Guide (2026) — 社区技术博客,技能目录结构与实战场景补充
  5. Build a Claude Skill: YAML Frontmatter, SKILL.md Instructions — 教程类资料,手把手 SKILL.md 与 frontmatter 实操

[!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 等场景全覆盖。

51学通信知识星球海报-扫码加入