首页 › tutorials › 从零造假数据工厂Skill

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

概述

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

前置条件


一、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 秒跑通第一个

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

操作说明

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"之类的回复。

常见问题


三、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"。

常见问题


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

!command`` 语法会在正文发给 Claude 之前执行命令,用输出替换占位符。关键规则(官方明确):

## 当前环境
```!
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

---
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(第三层)

# 字段语义约定
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(完整、可运行、零依赖)

#!/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 位、邮箱形如 [email protected]、created_at 是 ISO 日期)。换一句"再要 200 条 SQL,表名 users"即可拿到INSERT INTO users ...` 语句。

进阶:用 context: fork 隔离运行

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

---
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 的触发命中率。

常见问题


工作流总览(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

延伸阅读


训练营适用性评估

维度 评估
适合的训练营类型 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 — Anthropic 官方 Skills 文档,frontmatter 字段表、渐进披露、动态注入、子代理运行、评测与分发的权威来源。
  2. anthropics/skills — GitHub — Anthropic 官方 Skills 公共仓库,含可直接参考的真实 SKILL.md 与目录组织范例。
  3. How to Build Claude Code Skills: 5 Examples (2026) — 五个完整 Skill 示例的开发者指南,覆盖 SKILL.md 规范、安全控制与团队分发。
  4. Claude Code Skill Frontmatter: Every YAML Option Explained — 对全部 frontmatter 字段(含 disable-model-invocation、paths、hooks 等)的逐项图解与取舍建议。
  5. Evaluating skill output quality — agentskills.io — 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学通信知识星球海报-扫码加入