<!-- title: 从零造假数据工厂Skill -->

# 从零造假数据工厂：Claude Code Skill 开发入门实战

## 概述

本教程面向"已经装通 Claude Code、想把自己的重复操作固化成技能"的开发者。你将学会一个 Skill 的完整生命周期：从识别一个可复用任务，到写 `SKILL.md`、配 frontmatter、做渐进披露、捆绑脚本、用动态注入喂实时数据，最后用 `skill-creator` 做量化评测。我们以一个全新的**假数据工厂（mock-data-factory）**为案例——给定一个 JSON 样例，批量生成 JSON/CSV/SQL 格式的逼真测试数据——把官方文档里散落的字段、替换变量、注入语法全部串起来，跑通一个零依赖、可直接复制使用的真实 Skill。

## 前置条件

- 已安装 Claude Code（CLI 可在终端运行 `claude`），建议 v2.1.196 以上以支持 `${CLAUDE_PROJECT_DIR}`、计划任务联动等特性。
- 一台装有 Python 3 的机器（脚本仅用标准库，无需 `pip install`）。
- 一个 git 项目目录用于测试触发（任何含若干文件的小仓库即可）。
- 基本的 Markdown 与 YAML 语法知识。

---

## 一、Skill 到底是什么：三层渐进披露

Claude Code 的 Skill（技能）本质是**一个目录**，里面至少有一个 `SKILL.md`，外加可选的脚本、模板、参考文档。官方文档原话：*"Skills extend what Claude can do. Create a `SKILL.md` file with instructions, and Claude adds it to its toolkit."*

和 `CLAUDE.md` 全程常驻不同，Skill 的核心设计叫**渐进披露（progressive disclosure）**，分三层：

| 层级 | 内容 | 何时进入上下文 | 成本 |
|:--|:--|:--|:--|
| 第一层 | frontmatter 的 `name` + `description` + `when_to_use` | 始终在上下文（受字符预算约束） | 极低 |
| 第二层 | `SKILL.md` 正文 | 你手动 `/name` 或 Claude 判断命中时 | 一次加载，会话内常驻 |
| 第三层 | `references/`、`scripts/`、`examples/` 等支撑文件 | 正文里被显式引用、且确有需要时才读取 | 按需，几乎零成本 |

这意味着：你可以在 Skill 里塞进一份 2000 行的 API 规范，只要它躺在 `references/` 里、正文只引用一次，平时就几乎不占 token。这是 Skill 区别于"把一切写进 CLAUDE.md"的关键。

存放位置决定作用范围（官方四级覆盖：企业 > 个人 > 项目 > 插件）：

| 级别 | 路径 | 作用范围 |
|:--|:--|:--|
| 个人 | `~/.claude/skills/<name>/SKILL.md` | 你的所有项目 |
| 项目 | `.claude/skills/<name>/SKILL.md` | 仅当前项目（提交进 git 即团队共享） |
| 插件 | `<plugin>/skills/<name>/SKILL.md` | 启用该插件处 |

> 小知识：旧版的 `.claude/commands/deploy.md` 已与 `.claude/skills/deploy/SKILL.md` 合并，两者都生成 `/deploy`，Skill 因支持目录、frontmatter、自动加载而成为推荐写法。

---

## 二、最小可用 Skill：30 秒跑通第一个

先做一个"零依赖"的最小例子，确认触发链路通了，再加复杂度。

**操作说明**

```bash
mkdir -p ~/.claude/skills/hello-skill
cat > ~/.claude/skills/hello-skill/SKILL.md <<'EOF'
---
description: 问候并报告当前所在 git 分支。当用户说"打个招呼"或问当前分支时使用。
---
对用户说一句简短问候，然后用下面的信息说明当前仓库状态：

当前分支：!`git branch --show-current 2>/dev/null || echo "非 git 仓库"`
EOF
```

**预期结果**

进入任意 git 项目运行 `claude`，输入 `/hello-skill`，或直接问"现在在哪个分支呀"，Claude 会先执行 `git branch --show-current`，把输出内联进指令，再给出"你好，当前分支是 main"之类的回复。

**常见问题**

- *打 `/hello-skill` 报"找不到命令"*：目录名拼错，命令名来自**目录名**而非 frontmatter 的 `name`（仅插件根目录例外）。
- *自动触发不灵*：把触发词写进 `description`，关键词要和用户自然语言一致；改完无需重启，官方支持**热加载**——会话内编辑 `SKILL.md` 立即生效。

---

## 三、Frontmatter 字段精讲

frontmatter 是两行 `---` 之间的 YAML。全部字段可选，但 `description` 强烈推荐。下表是开发中最常用的：

| 字段 | 作用 | 备注 |
|:--|:--|:--|
| `name` | 显示名 | 默认取目录名；命令名始终来自目录名 |
| `description` | 做什么、何时用 | **核心触发依据**；与 `when_to_use` 合计在列表里截断到 **1536 字符** |
| `when_to_use` | 触发短语/示例请求 | 追加到 description，共享 1536 上限 |
| `disable-model-invocation` | 设 `true` 则**只能手动触发** | 适合 `/deploy`、`/commit` 等有副作用操作 |
| `user-invocable` | 设 `false` 则**只能 Claude 自动触发**，不进 `/` 菜单 | 适合"背景知识"类技能 |
| `allowed-tools` | 该技能激活时免确认可用的工具 | 如 `Bash(python3 *)` |
| `disallowed-tools` | 激活期间**移除**的工具 | 下条消息后失效 |
| `model` / `effort` | 临时切换模型/思考强度 | 仅本回合有效，不入设置 |
| `context` | 设 `fork` 则在**隔离子代理**里跑 | 配合 `agent` 使用 |
| `agent` | `Explore`/`Plan`/`general-purpose`/自定义 | 仅 `context: fork` 时生效 |
| `paths` | glob 限定激活条件 | 如 `["**/*.sql"]` 只在改 SQL 文件时触发 |
| `shell` | 内联命令用 bash 还是 powershell | Windows 下可设 powershell |

**两组易混开关**：`disable-model-invocation: true` = 防止 Claude 自作主张（部署、发消息类必备）；`user-invocable: false` = 不暴露成命令（背景知识类）。两者默认都为关，即"你也能调、Claude 也能调"。

**预算管理**：所有技能描述挤在一个"列表预算"里，规模约为上下文窗口的 **1%**，溢出时最少使用的技能描述先被裁掉。用 `/doctor` 或 `/context` 查看被裁情况，必要时用 `skillListingBudgetFraction` 调高，或把冷门技能在 `skillOverrides` 里设为 `"name-only"`。

**常见问题**

- *YAML 写错后还能用吗*：官方行为是"正文照常加载，元数据为空"——`/name` 仍可用，但 description 没了、自动触发失效。加 `--debug` 启动可看到解析报错。
- *技能太多互相抢预算*：精简 description，把"何时用"前置（截断从头算），冷门技能降级为 name-only。

---

## 四、动态上下文注入：让指令自带实时数据

`!`command`` 语法会在正文发给 Claude **之前**执行命令，用输出替换占位符。关键规则（官方明确）：

- 内联形式 `` !`cmd` `` 仅当 `!` 出现在**行首**或**紧跟空白**时才识别；像 `KEY=!`cmd`` 这种紧跟字符的写法会被当成字面文本，不执行。
- 输出作为纯文本插入，**不会再被二次扫描**——命令不能"吐出"新占位符给下一轮。
- 多行命令改用 ``` ```! ``` 代码块。

```markdown
## 当前环境
```!
node --version
npm --version
git status --short
```
```

这是"预处理"，不是 Claude 自己执行——Claude 只看到最终结果。若要全局禁用（如受管环境），设 `"disableSkillShellExecution": true`，注入会被替换成 `[shell command execution disabled by policy]`。

---

## 五、字符串替换：$ARGUMENTS 与路径变量

调用 `/skill foo bar` 时，参数通过占位符进入正文：

| 变量 | 含义 |
|:--|:--|
| `$ARGUMENTS` | 全部参数原样字符串 |
| `$ARGUMENTS[0]` / `$0` | 第一个参数（0 起始），引号可传多词 |
| `$name` | frontmatter `arguments` 声明的具名参数 |
| `${CLAUDE_SKILL_DIR}` | 当前 `SKILL.md` 所在目录——**引用捆绑脚本用这个**，无论装在哪都正确 |
| `${CLAUDE_PROJECT_DIR}` | 项目根（v2.1.196+） |
| `${CLAUDE_SESSION_ID}` | 会话 ID，可做日志/临时文件命名 |

写脚本调用时务必用 `${CLAUDE_SKILL_DIR}/scripts/xxx.py`，不要写死 `~/.claude/skills/...`——后者在项目级或插件级安装时会失效。

---

## 六、实战案例：造假数据工厂 Skill（完整可运行）

### 需求

测试和演示经常需要一堆"像样"的数据：注册接口要 50 条用户、数据库要 200 行种子、前端原型要 CSV。手写费时、固定样例又太假。我们做一个 Skill：输入一个 JSON 样例，按字段名语义自动生成批量数据，支持 JSON/CSV/SQL 三种格式，纯标准库、零依赖。

### 目录结构

```text
~/.claude/skills/mock-data-factory/
├── SKILL.md                       # 主指令（第二层）
├── references/
│   └── data-conventions.md        # 字段语义规则（第三层，按需读）
└── scripts/
    └── generate.py                # 生成脚本（被执行，不进上下文）
```

### SKILL.md

```yaml
---
name: mock-data-factory
description: 从一个 JSON 样例或字段描述，批量生成逼真的假数据（JSON/CSV/SQL）。用于造测试数据、种子数据、mock 接口返回、数据库填充、演示样例。
when_to_use: 出现"造数据/假数据/mock 数据/测试数据/种子数据/seed data/fake data/生成 N 条用户"等请求，或需要为接口/数据库/前端填充真实感数据时。
argument-hint: [count] [format=json|csv|sql] [table=users]
allowed-tools: Bash(python3 *)
---
# 假数据工厂

你是假数据生成助手，目标是产出语义逼真、可即用的数据。

## 工作流
1. 取得目标字段：用户给了 JSON 对象样例就用它做模板；只描述字段就先帮他构造一个样例对象。
2. 解析 $ARGUMENTS：第一个整数作为 count（默认 10）；json/csv/sql 任一作为 format（默认 json）；table= 后作为表名（默认 mock_data）。
3. 把样例写到临时文件，再执行生成器：
   ```bash
   python3 ${CLAUDE_SKILL_DIR}/scripts/generate.py \
     --template /tmp/mock-tpl.json --count <count> --format <fmt> --table <table>
   ```
4. 字段语义规则（手机号/邮箱/姓名/金额等如何造）见 [data-conventions.md](references/data-conventions.md)，需要时再读取。
5. 输出格式：json 用 ```json 代码块，csv/sql 用对应代码块；超过 30 行时只展示前若干行并说明完整写入了哪个文件。

## 约束
- 始终用 ${CLAUDE_SKILL_DIR} 定位脚本，不要硬编码安装路径。
- 新增字段规则时优先扩展 data-conventions.md，而不是把规则堆进本文件。
```

> 注意 `description` 和 `when_to_use` 加起来要控制长度——1536 字符上限是从头截断，所以把"做这件事"放最前面，触发词次之。

### references/data-conventions.md（第三层）

```markdown
# 字段语义约定
generate.py 依据字段名（小写）推断语义。新增规则在此追加。

| 关键字 | 生成规则 |
|:--|:--|
| id / *_id | 自增整数（1, 2, 3…） |
| email / mail | user{n}@example.com |
| phone / mobile | 中国大陆手机号 1[3-9] + 9 位 |
| name / username | 中文姓名池随机 |
| age | 18–65 整数 |
| price/amount/salary/money/fee | 两位小数金额 |
| *date* / *_at / *_ts | ISO8601 日期（2024 起） |
| is_*/has_*/*bool* | 布尔 |
| province/city/region | 省份池 |

样例值无法推断时，按样例的 Python 类型回退：bool→随机布尔，int→0–1000，float→两位小数，str→"字段名_序号"。
```

### scripts/generate.py（完整、可运行、零依赖）

```python
#!/usr/bin/env python3
"""假数据工厂生成器，仅用标准库。
用法: generate.py --template sample.json --count 50 --format json|csv|sql --table users [--out file]
"""
import argparse, csv, io, json, random, sys, datetime

NAMES = ["张伟","王芳","李娜","刘洋","陈静","杨磊","赵敏","黄强","周杰","吴婷",
         "徐明","孙丽","马超","朱琳","胡涛","林燕","郭峰","何萍","高翔","罗静"]
DOMAINS = ["example.com","test.cn","mock.io","demo.org"]
PROVINCES = ["北京","上海","广东","浙江","江苏","四川","湖北","陕西"]

def fake_value(key, idx):
    k = key.lower()
    if k == "id" or k.endswith("_id") or k.endswith("id"):
        return idx
    if "email" in k or k.endswith("mail"):
        return f"user{idx}{random.randint(0,99)}@{random.choice(DOMAINS)}"
    if "phone" in k or "mobile" in k:
        return f"1{random.choice('35789')}{random.randint(100000000,999999999)}"
    if "name" in k:
        return random.choice(NAMES)
    if k == "age" or "age" in k:
        return random.randint(18, 65)
    if any(w in k for w in ("price","amount","salary","money","fee")):
        return round(random.uniform(10, 9999), 2)
    if "count" in k or "num" in k or k.startswith("qty"):
        return random.randint(1, 1000)
    if any(w in k for w in ("date","time","at")) or k.endswith("_ts"):
        return (datetime.date(2024,1,1) + datetime.timedelta(days=random.randint(0,500))).isoformat()
    if any(w in k for w in ("province","city","region")):
        return random.choice(PROVINCES)
    if "url" in k or "link" in k:
        return f"https://{random.choice(DOMAINS)}/{key}/{idx}"
    if k.startswith("is_") or k.startswith("has_") or "bool" in k:
        return random.choice([True, False])
    return None  # 回退到按样例类型推断

def gen_rows(sample, count):
    rows = []
    for i in range(1, count + 1):
        row = {}
        for key, val in sample.items():
            v = fake_value(key, i)
            if v is None:
                if isinstance(val, bool):   v = random.choice([True, False])
                elif isinstance(val, int):  v = random.randint(0, 1000)
                elif isinstance(val, float):v = round(random.uniform(0, 1000), 2)
                elif isinstance(val, str):  v = f"{key}_{i}"
                else:                        v = val
            row[key] = v
        rows.append(row)
    return rows

def to_sql(rows, table):
    cols = list(rows[0].keys())
    out = [f"CREATE TABLE IF NOT EXISTS {table} ({', '.join(cols)});"]
    for r in rows:
        vals = ["'%s'" % v if isinstance(v, str) else str(v) for v in r.values()]
        out.append(f"INSERT INTO {table} ({', '.join(cols)}) VALUES ({', '.join(vals)});")
    return "\n".join(out)

def to_csv(rows):
    buf = io.StringIO()
    w = csv.DictWriter(buf, fieldnames=list(rows[0].keys()))
    w.writeheader(); w.writerows(rows)
    return buf.getvalue()

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--template", required=True)
    ap.add_argument("--count", type=int, default=10)
    ap.add_argument("--format", default="json", choices=["json","csv","sql"])
    ap.add_argument("--table", default="mock_data")
    ap.add_argument("--out", default=None)
    a = ap.parse_args()

    with open(a.template, encoding="utf-8") as f:
        sample = json.load(f)
    if not isinstance(sample, dict):
        sys.exit("template 必须是 JSON 对象（字段 -> 样例值）")

    random.seed(42)  # 可复现
    rows = gen_rows(sample, a.count)
    if a.format == "json": text = json.dumps(rows, ensure_ascii=False, indent=2)
    elif a.format == "csv": text = to_csv(rows)
    else:                   text = to_sql(rows, a.table)

    if a.out:
        open(a.out, "w", encoding="utf-8").write(text)
        print(f"wrote {a.count} rows -> {a.out}", file=sys.stderr)
    else:
        print(text)

if __name__ == "__main__":
    main()
```

### 调用与预期结果

在任意项目里 `claude`，然后：

```
帮我造 20 条用户数据，JSON 格式，字段像这样：{"id":0,"name":"","email":"","phone":"","age":0,"province":"","created_at":""}
```

Claude 会：解析出 count=20、format=json → 写模板 → 执行 `python3 ${CLAUDE_SKILL_DIR}/scripts/generate.py --template /tmp/mock-tpl.json --count 20 --format json` → 用 ```json 块返回 20 条语义合理的数据（手机号是 1 开头的 11 位、邮箱形如 user1@example.com、created_at 是 ISO 日期）。换一句"再要 200 条 SQL，表名 users"即可拿到 `INSERT INTO users ...` 语句。

### 进阶：用 context: fork 隔离运行

若担心大批量生成把主会话上下文撑爆，加两行让技能在只读子代理里跑，结果汇总回主会话：

```yaml
---
name: mock-data-factory
description: ...
context: fork
agent: general-purpose
allowed-tools: Bash(python3 *)
---
```

注意：`context: fork` 的正文会成为子代理的**唯一任务**，拿不到你的对话历史，所以正文必须自包含（这正是我们把字段名、count、format 都从 `$ARGUMENTS`/样例里取的原因）。

### 进阶：用 skill-creator 做量化评测

"看到技能被触发"≠"它做对了"。官方推荐装 `skill-creator` 跑对比评测：

```
/plugin install skill-creator@claude-plugins-official
/reload-plugins
```

装好后说 `evaluate my mock-data-factory skill with skill-creator`。它会：在 `evals/evals.json` 存测试用例 → 每个用例起一个干净子代理 → 对比"有技能 vs 无技能"的通过率、token、耗时 → 产出 `benchmark.json`，还能做两版技能的盲测 A/B、自动调 description 的触发命中率。

**常见问题**

- *脚本路径找不到*：检查正文用的是 `${CLAUDE_SKILL_DIR}` 而非写死的 `~` 路径；用 `/skills` 菜单确认技能已加载。
- *生成的中文乱码*：`generate.py` 已显式 `encoding="utf-8"`；若在 Windows powershell，可加 frontmatter `shell: powershell` 并设 `CLAUDE_CODE_USE_POWERSHELL_TOOL=1`。
- *想固定随机性*：`generate.py` 里 `random.seed(42)` 已保证可复现，改种子即换一套数据。
- *新字段类型不识别*：别改 SKILL.md，去 `references/data-conventions.md` 追加规则——这正是第三层渐进披露的价值。

---

## 工作流总览（Mermaid）

```mermaid
flowchart TD
    U["用户提问：造 20 条用户 JSON<br/>或 /mock-data-factory 20 json"] --> M{"命中触发？"}
    M -->|"description/when_to_use 命中<br/>或手动 /name"| L1["第一层 frontmatter 常驻"]
    M -->|"未命中且非手动"| X["不加载，零成本"]
    L1 --> L2["第二层：SKILL.md 正文进会话"]
    L2 --> SUB["$ARGUMENTS / ${CLAUDE_SKILL_DIR} 字符串替换"]
    SUB --> INJ["动态注入 !`git/python` 执行并内联输出"]
    INJ --> L3{"正文引用支撑文件？"}
    L3 -->|"是"| REF["第三层：按需读 references/*.md"]
    L3 -->|"否"| RUN["直接进入指令"]
    REF --> RUN
    RUN --> SCRIPT["执行 scripts/generate.py"]
    SCRIPT --> OUT["输出 JSON/CSV/SQL"]
    OUT --> KEEP["正文常驻至会话结束；fork 模式则只回传摘要"]
    style L1 fill:#e8f5e9
    style L2 fill:#fff3e0
    style REF fill:#e3f2fd
```

---

## 延伸阅读

- **官方 Skill 评测与迭代流程**：`agentskills.io/skill-creation/evaluating-skills`，`evals/evals.json` 的格式与"有/无技能"对照实验方法论。
- **Skill 编写最佳实践（跨产品通用）**：`platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices`，正文简洁性、何时该拆成子代理。
- **子代理联动**：把技能当作"参考材料"预加载进自定义子代理（`subagents` 文档的 *Preload skills* 一节），与本教程的 `context: fork` 互为反向用法。
- **Hooks 与 Skill 的联动**：`frontmatter` 的 `hooks` 字段可为技能挂载生命周期钩子，实现"技能激活即自动收集上下文"。
- **分发**：项目级提交 git 共享；`skill-creator` 还能帮你做版本对比；企业级可用 managed settings 全量下发。

---

## 训练营适用性评估

| 维度 | 评估 |
|:--|:--|
| 适合的训练营类型 | **AI 工程化 / 提示词工程 / DevOps 自动化**训练营；也适合"AI 原生开发"通识课的进阶单元 |
| 学员基础 | 会用终端、写过简单脚本；不需要会 Python 进阶（脚本可直接复制） |
| 教学时长 | **建议 2 课时（约 90 分钟）**：第 1 课时讲三层披露 + frontmatter + 最小例子；第 2 课时手把手跑通 mock-data-factory 并做 skill-creator 评测 |
| 可裁剪点 | 时间紧可跳过 `context: fork` 与 skill-creator 章节；时间宽裕可加"把技能发布成插件给团队用"扩展 |
| 学员产出 | 每人带走一个自己的 Skill（不必是数据工厂，鼓励按岗位造：测试造用例、运维造巡检、前端造 mock） |
| 难度阶梯 | 本篇属 Round 2"从零造 Skill"，承接安装入门、为后续 MCP 服务器（Round 3）、Agent 自动化（Round 4）打底 |

---

## 参考来源

1. [Extend Claude with skills — Claude Code Docs](https://code.claude.com/docs/en/skills) — Anthropic 官方 Skills 文档，frontmatter 字段表、渐进披露、动态注入、子代理运行、评测与分发的权威来源。
2. [anthropics/skills — GitHub](https://github.com/anthropics/skills) — Anthropic 官方 Skills 公共仓库，含可直接参考的真实 SKILL.md 与目录组织范例。
3. [How to Build Claude Code Skills: 5 Examples (2026)](https://fp8.co/articles/Claude-Code-Skills-Complete-Developer-Guide) — 五个完整 Skill 示例的开发者指南，覆盖 SKILL.md 规范、安全控制与团队分发。
4. [Claude Code Skill Frontmatter: Every YAML Option Explained](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — 对全部 frontmatter 字段（含 `disable-model-invocation`、`paths`、`hooks` 等）的逐项图解与取舍建议。
5. [Evaluating skill output quality — agentskills.io](https://agentskills.io/skill-creation/evaluating-skills) — skill-creator 评测文件格式与"有/无技能"对照迭代方法论。

> [!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学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
