本教程面向希望从零掌握 Claude Code 自定义 Skill 开发的工程师。你将从"能力建模"出发,亲手构建一个名为 regex-workbench(正则表达式工作台)的完整、可运行的生产级 Skill,覆盖 SKILL.md 元数据设计、渐进披露、动态上下文注入、捆绑脚本、触发控制、隔离执行与评测分发全链路。学完后你能独立设计、实现、测试并分发任何领域的自定义 Skill。
/run-skill-generator、skillOverrides 等新特性)~/.claude/skills/ 目录(个人 Skill 默认存放点)在动手之前,必须先建立正确的心智模型。Skill 的官方定义是:一个目录 + 一个 SKILL.md 入口文件,外加若干可选的支撑文件。它的核心价值不在于"写一段提示词",而在于渐进披露(Progressive Disclosure)——只在真正需要时才把完整内容载入上下文。
这与 CLAUDE.md 形成关键对比:
| 维度 | CLAUDE.md | Skill |
|---|---|---|
| 载入时机 | 每个会话常驻上下文 | 仅在被触发时载入完整正文 |
| 适合内容 | 简短的事实、约定、不变量 | 多步流程、长参考资料、可执行脚本 |
| Token 成本 | 持续消耗 | 触发前几乎为零(仅 description 常驻) |
| 触发方式 | 被动 | 可由用户 /skill-name 或模型自动触发 |
一条经验法则:当 CLAUDE.md 中某一段从"事实"长成了"流程",就把它抽成 Skill。
Skill 存放位置决定其作用范围,这是第二个必须记住的规则:
| 层级 | 路径 | 作用范围 |
|---|---|---|
| 企业(Managed) | 由托管设置下发 | 全组织所有用户 |
| 个人(Personal) | ~/.claude/skills/<name>/SKILL.md |
你自己的所有项目 |
| 项目(Project) | <repo>/.claude/skills/<name>/SKILL.md |
仅当前项目 |
| 插件(Plugin) | <plugin>/skills/<name>/SKILL.md |
启用该插件处,命名空间隔离 |
当同名冲突时,优先级为 企业 > 个人 > 项目。本教程为方便跨项目复用,把 regex-workbench 放在个人级。
新手最常见的错误是打开编辑器就写 SKILL.md。正确的顺序是先回答三个设计问题:
问题一:这是"参考资料型"还是"任务型" Skill?
/name 手动触发。regex-workbench 是混合型:既要注入"如何解读正则"的知识(参考型),又要有"测试 → 解释 → 优化"的明确流程(任务型)。我们让它在两个方向都能触发。
问题二:谁来触发?
这是 Skill 设计中影响最大、却最容易被忽视的决策。三个旋钮组合出四种行为:
| 配置 | 用户可触发 | 模型可触发 | description 是否常驻 |
|---|---|---|---|
| 默认 | ✅ | ✅ | ✅ 常驻 |
disable-model-invocation: true |
✅ | ❌ | ❌ 不常驻 |
user-invocable: false |
❌ | ✅ | ✅ 常驻 |
| 两者都设 | 仅作为被预加载的背景知识 |
regex-workbench 让用户随时调用,也允许模型在用户问正则问题时自动加载——所以用默认配置。
问题三:description 能否撑起自动匹配?
Skill 列表中的 description + when_to_use 合计上限为 1,536 字符,且整体受列表预算约束(默认为模型上下文窗口的 1%)。预算超限时,用得最少的 Skill 的 description 会被优先截断。所以 description 必须把最关键的用例放在最前面,关键词要覆盖用户会自然说出口的词。
常见问题:Skill 该触发时不触发?→ 用
/doctor检查预算是否溢出、description 是否被截断;并把关键词前移。不该触发却乱触发?→ 收窄 description,或加disable-model-invocation: true。
先建目录与文件树:
mkdir -p ~/.claude/skills/regex-workbench/scripts
mkdir -p ~/.claude/skills/regex-workbench/examples
目标结构:
regex-workbench/
├── SKILL.md # 入口(必需):元数据 + 核心流程
├── reference.md # 渐进披露:正则速查表与陷阱(按需加载)
├── examples.md # 渐进披露:已解案例(按需加载)
└── scripts/
└── test.js # 捆绑脚本:测试引擎(被执行,不载入上下文)
接着写 SKILL.md。它由两部分组成:--- 之间的 YAML 元数据,以及其后的 Markdown 正文。
---
name: regex-workbench
description: Build, test, explain, and optimize regular expressions. Use when the user wants to write or debug a regex, test a pattern against sample strings, explain what a pattern matches, or fix a regex that doesn't match as expected. Also use for extracting, validating, or replacing text with patterns.
argument-hint: [pattern] [flags]
allowed-tools: Bash(node *) Bash(cat *)
---
正文遵循"陈述做什么,而非叙述如何/为什么"的极简原则——因为一旦 Skill 被触发,其正文会作为单条消息常驻整个会话,每一行都是反复出现的 token 成本。
# 正则表达式工作台
当用户需要编写、调试或优化正则时,按以下流程工作。
## 1. 收集输入
- 用户提供的模式(pattern)、标志(flags,如 `g`、`i`、`m`)以及待测字符串。
- 如果用户只给了自然语言需求(如"匹配中国大陆手机号"),先写出候选模式并说明推理过程。
## 2. 运行测试引擎
把候选字符串写入临时文件,调用捆绑脚本获取结构化匹配结果:
```bash
node ${CLAUDE_SKILL_DIR}/scripts/test.js "PATTERN" "FLAGS" /tmp/regex-subject.txt
```
脚本输出 JSON:每个匹配的文本、起始下标、捕获组,以及按行统计的命中数。
## 3. 解释模式
逐段拆解模式,说明每个锚点、字符类、量词、分组的作用。遇到贪婪/惰性量词、回溯陷阱时主动提示风险(详见 reference.md)。
## 4. 给出可执行的改进
如果未命中、误命中或存在灾难性回溯风险,给出修正版模式并说明理由。优先推荐更安全、可读性更强的写法。
## 资源
- 完整速查表与陷阱清单见 [reference.md](reference.md)
- 已解案例(邮箱、IPv4、日期)见 [examples.md](examples.md)
预期结果:保存后,在任意项目里启动 Claude Code,输入 /regex 应在自动补全中看到该 Skill;或直接问"帮我写个正则匹配手机号",模型应自动加载它(无需重启——Claude Code 会实时监听 Skill 目录的 SKILL.md 文本变更)。
常见问题: - 改了不生效?注意实时检测只覆盖
SKILL.md文本。如果是插件型 Skill,改了hooks/、.mcp.json需运行/reload-plugins。新建一个会话启动时不存在的顶层 skills 目录,则需要重启。 -/regex-workbench找不到?命令名取自目录名而非name字段(除非是插件根SKILL.md)。 - 路径斜杠不要写死:始终用${CLAUDE_SKILL_DIR}引用捆绑文件,这样无论装在个人级、项目级还是插件级都能正确解析。
如果正则的所有速查表都塞进 SKILL.md,正文会膨胀,每次触发都付出昂贵的常驻成本。正确做法是拆分到 reference.md,仅在 Claude 判断需要时才读取。
reference.md 节选(按需载入,不计入常驻成本):
# 正则速查表与陷阱
## 量词
- `*`(0+)、`+`(1+)、`?`(0/1)、`{n,m}`(n 到 m 次)
- 默认**贪婪**,会尽可能多匹配;加 `?` 变惰性(`.*?`)。
## 灾难性回溯
形如 `(a+)+b` 的嵌套量词在输入不匹配时可能引发指数级回溯。
对策:使用占有量词 `a++b` 或原子分组。
## 边界
- `^` / `$` 匹配行首尾;`m` 标志下按行匹配。
- `\b` 词边界,`\B` 非词边界。
## Unicode
默认 `.` 不匹配换行;需匹配所有字符用 `[\s\S]` 或 `s` 标志。
匹配中文范围:`[\u4e00-\u9fa5]`。
在 SKILL.md 末尾用相对链接显式声明这些文件的存在与用途,Claude 才知道"何时去读"。这是渐进披露的契约:入口只放导航,细节放支撑文件。官方建议 SKILL.md 控制在 500 行以内。
Skill 的强大之处在于它不只是静态文本。!command`` 语法会在发送给 Claude 之前执行命令,并把输出原地替换进正文——Claude 看到的是数据本身,而非命令。
这对 regex-workbench 意味着:当用户在工作目录里有一份需要被正则处理的样本文件时,可以直接把它的内容注入:
## 当前工作区候选样本
!`cat data/sample.log 2>/dev/null | head -50`
执行规则:
!cmd仅在 `!` 位于行首或紧跟空白时才被识别。`KEY=!`cmd 这种紧贴形式会被当作字面量。```! 围栏代码块。! 不会被二次扫描。shell: powershell 切换。变量替换同样重要。完整清单:
| 变量 | 含义 |
|---|---|
$ARGUMENTS |
调用时传入的全部参数 |
$ARGUMENTS[N] / $N |
第 N 个参数(0 基) |
$name |
在 arguments 声明的命名参数 |
${CLAUDE_SKILL_DIR} |
Skill 所在目录,引用捆绑脚本必用 |
${CLAUDE_SESSION_ID} |
当前会话 ID,可用于日志/临时文件 |
${CLAUDE_EFFORT} |
当前 effort 级别(low/medium/high/xhigh/max) |
常见问题:在受控企业环境里,管理员可通过托管设置
"disableSkillShellExecution": true一键禁用所有用户/项目/插件级 Skill 的 shell 执行,占位符会被替换为提示文本而不运行命令。
Skill 可以捆绑任意语言的脚本,赋予 Claude 超越单条提示词的能力。这是 regex-workbench 的核心——一个结构化、可复现的测试引擎。
scripts/test.js 完整实现:
#!/usr/bin/env node
// 用法: node test.js "<pattern>" "<flags>" <subject-file|->"
// 输出 JSON:匹配文本、下标、捕获组、按行命中统计。
const fs = require("fs");
const [, , pattern, flags = "", file] = process.argv;
const result = { ok: true, pattern, flags, matches: [], summary: {} };
// 读取待测文本:支持文件路径或 "-" 从 stdin
let subject = "";
if (!file) {
result.ok = false;
result.error = "缺少待测文本参数(文件路径或 - 表示 stdin)";
console.log(JSON.stringify(result, null, 2));
process.exit(0);
}
subject = file === "-" ? fs.readFileSync(0, "utf8") : fs.readFileSync(file, "utf8");
try {
const re = new RegExp(pattern, flags);
const lines = subject.split(/\r?\n/);
if (flags.includes("g")) {
let m, count = 0;
while ((m = re.exec(subject)) !== null) {
result.matches.push({ match: m[0], index: m.index, groups: m.slice(1) });
count++;
if (m.index === re.lastIndex) re.lastIndex++; // 避免零宽匹配死循环
if (count >= 1000) { result.truncated = true; break; }
}
result.summary.globalMatches = count;
} else {
const m = re.exec(subject);
if (m) result.matches.push({ match: m[0], index: m.index, groups: m.slice(1) });
}
const lineRe = new RegExp(pattern, flags.includes("g") ? flags.replace("g", "") : flags);
result.summary.linesMatched = lines.filter(l => lineRe.test(l)).length;
result.summary.totalLines = lines.length;
} catch (e) {
result.ok = false;
result.error = e.message;
}
console.log(JSON.stringify(result, null, 2));
要点说明:
lastIndex++,防止 a* 这类模式卡死。${CLAUDE_SKILL_DIR}/scripts/test.js,配合 allowed-tools: Bash(node *) Bash(cat *) 免确认执行。预期结果:在 SKILL.md 指引下,Claude 会先把候选字符串写入 /tmp/regex-subject.txt,再调用脚本。例如:
printf '13800138000\n010-12345678\ninvalid\n' > /tmp/regex-subject.txt
node ${CLAUDE_SKILL_DIR}/scripts/test.js "^1[3-9]\d{9}$" "" /tmp/regex-subject.txt
输出:
{
"ok": true,
"pattern": "^1[3-9]\\d{9}$",
"matches": [{ "match": "13800138000", "index": 0, "groups": [] }],
"summary": { "linesMatched": 1, "totalLines": 3 }
}
Claude 据此即可精确地"逐段解释 + 给出改进"。
随着 Skill 数量增长,必须精确控制"谁能触发"。除 disable-model-invocation 与 user-invocable 外,还有三个利器:
allowed-tools(预批准工具):列出该 Skill 激活时可免确认使用的工具。注意它不限制可用工具范围——所有工具仍可调用,只是被列出的免去逐次确认。
disallowed-tools(临时移除):从可用池中移除某些工具,适合自治型 Skill 强制约束(如后台循环禁用 AskUserQuestion)。该限制在用户发送下一条消息时清除。
paths(路径触发):Glob 模式限定——仅当编辑匹配文件时才自动加载。例如一个只在碰 SQL 文件时触发的审查 Skill:
paths: ["**/*.sql", "**/migrations/**"]
还可在 /permissions 用权限规则精细管控:
Skill(commit) # 仅允许 commit 这一个
Skill(deploy *) # 前缀匹配,禁用 deploy 及其参数
或用 skillOverrides(写入 .claude/settings.local.json,也可在 /skills 菜单按空格切换)把某个 Skill 设为 name-only(只留名不留描述,省预算)或 off(完全隐藏)。
当某个 Skill 需要做大量独立探索(如全仓库扫描)却不希望污染主对话上下文时,加 context: fork 让它在一个隔离的子代理中运行。其正文成为驱动子代理的任务,不访问主对话历史。
---
name: deep-scan
description: Thoroughly scan the codebase for a specific concern.
context: fork
agent: Explore
---
使用 Glob 与 Grep 定位 $ARGUMENTS 相关代码……
agent 可选内置的 Explore(只读、跳过 CLAUDE.md 与 git status,上下文极小)、Plan、general-purpose,或 .claude/agents/ 下的自定义子代理。注意:context: fork 只适合有明确任务的 Skill;若 Skill 只有"约定"而无任务,子代理会收到没有行动指令的指导而空手而归。
Skill 是代码,就要有测试。利用内置的 skill-creator(/skill-creator 或其命令)可生成评测用例并做方差分析。为 regex-workbench 设计三类用例:
| 用例 | 输入 | 期望 |
|---|---|---|
| 基础匹配 | "匹配邮箱,测试 [email protected] 与 xx" | 模式正确,准确区分匹配/不匹配 |
| 陷阱规避 | "匹配 HTML 标签 <b>hi</b>" |
提示避免贪婪 .* 误吞,给出非贪婪版 |
| 优化建议 | "优化 (a|ab)* 类模式" |
指出回溯风险,给出原子/占有写法 |
每个用例以"输入 → 期望行为"形式录入,skill-creator 会对多次运行做方差分析,帮助发现 description 触发不稳定或正文含糊的问题。迭代时遵循:先修 description(提升触发准确度)→ 再修正文(提升执行质量)→ 最后跑评测验证。
Skill 停止影响行为? 通常内容仍在,是模型选择了别的工具。优先强化 description;若需确定性,用 hooks 强制行为;若在压缩后失效,重新触发即可(压缩会按 5K/个、合计 25K token 预算重新挂载最近调用的 Skill)。
~/.claude/skills/,所有项目可用。<repo>/.claude/skills/ 到版本控制,团队 clone 即得(首次需接受工作区信任对话框,因为 Skill 可在 allowed-tools 中授予自身较宽权限——务必先审阅再信任)。.claude-plugin/plugin.json,即可打包为插件,附带 hooks、agents、MCP 服务器,并通过 /plugin 管理。disableSkillShellExecution 等安全策略。把上面所有片段组装起来,完整文件树与三份文件(SKILL.md、reference.md、scripts/test.js)即构成可发布的 Skill。下面是一次真实交互:
用户:帮我写一个正则,从日志里提取形如 2026-06-13T14:30:00 的时间戳,并解释。
模型自动加载 regex-workbench,执行流程:
(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}),写入样本到临时文件。node ${CLAUDE_SKILL_DIR}/scripts/test.js "(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})" "g" /tmp/sample.log。\d{4} 年份、- 字面量、T 分隔、捕获组 () 的作用。(20\d{2}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}),并提示按 reference.md 的边界规则用 \b 避免误匹配。整个过程中,reference.md 与 examples.md 仅在需要解释陷阱或展示范例时才被读取——这正是渐进披露把成本压到最低的体现。
flowchart TD
A[用户需求/自然语言] --> B{匹配 description?}
B -- 是 --> C[自动加载 SKILL.md 正文]
B -- 否 --> U[用户手动 /regex-workbench]
U --> C
C --> D[收集 pattern/flags/subject]
D --> E["动态注入 !cat 或读临时文件"]
E --> F["运行捆绑脚本\n${CLAUDE_SKILL_DIR}/scripts/test.js"]
F --> G[结构化 JSON 结果]
G --> H{需解释陷阱?}
H -- 是 --> I[按需读取 reference.md]
H -- 否 --> J[逐段解释 + 给改进版]
I --> J
J --> K[返回用户]
style F fill:#e1f5ff,stroke:#0288d1
style I fill:#fff4e1,stroke:#f57c00
flowchart LR
subgraph 常驻上下文[常驻 - 低成本]
D1[description\n≤1536 字符]
end
subgraph 按需载入[按需 - 渐进披露]
S1[SKILL.md 正文]
S2[reference.md]
S3[examples.md]
end
subgraph 被执行[执行 - 不进上下文]
SC1[scripts/test.js]
end
D1 -->|触发| S1
S1 -.引用.-> S2
S1 -.引用.-> S3
S1 ==>|Bash调用| SC1
| 现象 | 根因 | 解决 |
|---|---|---|
| Skill 不触发 | description 关键词不匹配或被预算截断 | /doctor 查预算;关键词前移;调高 skillListingBudgetFraction |
| Skill 乱触发 | description 过宽 | 收窄描述或加 disable-model-invocation: true |
| 命令名不对 | 取自目录名而非 name |
重命名目录,或改用插件根 SKILL.md |
| 找不到脚本 | 硬编码路径 | 统一用 ${CLAUDE_SKILL_DIR}/scripts/... |
| 改了 hooks 不生效 | 实时检测仅覆盖 SKILL.md 文本 |
/reload-plugins |
| fork 后空手而归 | 只有约定无任务 | 补充明确任务指令再 fork |
| 企业环境命令被禁 | disableSkillShellExecution: true |
改为不依赖 shell 注入的实现 |
code.claude.com/docs/en/skills——frontmatter 字段与生命周期权威说明。agentskills.io——Skill 跨工具复用的规范基础。context: fork:code.claude.com/docs/en/sub-agents——隔离执行的完整语义。code.claude.com/docs/en/hooks——用确定性钩子强制 Skill 行为。code.claude.com/docs/en/plugins——把 Skill 升级为可分发插件。disable-model-invocation、user-invocable、allowed-tools、paths 等)逐一示例与取舍。[!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 等场景全覆盖。
