五分钟做第一个 Skill
用最短路径做出第一个可用 Skill,快速建立对 Skill 结构和使用方式的直觉。
从需求、结构、描述和规则设计出发,独立完成一个属于自己的 Skill。
内容摘要
> 模块4:从零写一个你的 Skill | 用时:约 40 分钟 | 目标:完整走过一遍从规划到测试的流程,最后再做一个带脚本的进阶版
模块4:从零写一个你的 Skill | 用时:约 40 分钟 | 目标:完整走过一遍从规划到测试的流程,最后再做一个带脚本的进阶版
上一个模块你学会了怎么调 Skill——改 description、调正文、加触发词。但都是基于已有的 Skill 来改。
这个模块我要带你从零开始,手把手写一个完整的 Skill。
读完这篇,你将能独立走完一个 Skill 的:规划、编写、测试、迭代,全流程。不光讲概念,我带你亲手做出来。
想象一下,以后你在终端只需要说一句话,AI 就会自动分析你的代码变更,然后输出一个规范的 commit message:
你:帮我写个 commit message
AI:[自动加载 commit-message-writer Skill,读取暂存区变更]
feat: 添加用户登录页面
新增了以下内容:
- LoginForm 组件,支持邮箱/密码登录
- JWT token 存储逻辑
- 登录成功后的路由跳转
看到了吗?你不需要解释"什么是 commit message"、"要用什么格式"。AI 已经从 Skill 里学会了你的规范。
这就是我们今天要一起做出来的东西。
准备好了吗?让我们从零开始。
动笔之前,先别急。
我问你一个问题:你现在用 AI 的时候,最烦的是什么?
是不是每次都要重新描述一遍要求?是不是同样的格式要重复说十遍?
那这就是你的 Skill 要解决的问题。
我给大家举几个真实场景,你看看哪个跟你最像:
每天写文章,标题直接决定打开率。但每次让 AI 写标题,都要重新说:"开头要有悬念感"、"数据型标题也来几个"、"别写得太官方"……
你的 Skill 可以是: wechat-title-generator
它能帮你生成 5 种风格的标题(悬念型、数据型、反常识型、故事型、清单型),还能分析每个标题的预期点击率。
纯指令型就够了,不需要脚本。15 分钟就能做好。
每周写 PRD,每次都要先贴一遍公司模板。不同产品类型重点还不一样——B 端要写权限设计,C 端要写用户故事。
你的 Skill 可以是: prd-generator
它先判断产品类型,然后输出对应的 PRD 骨架。B 端自动带权限矩阵,C 端自动带增长漏斗。你往里填内容就行。
每天提交代码,commit message 经常偷懒写成 fix: 修了一下,结果被 code review 打回来。
你的 Skill 可以是: commit-message-writer
它自动读 git diff --staged,判断变更类型,输出规范的 <type>: <中文描述>。
这就是我们今天要一起做的 Skill。
写论文时,文献综述整理到崩溃。一堆论文标题和摘要,要手动分类、找研究脉络。
你的 Skill 可以是: literature-review-helper
把论文列表丢给它,自动按主题分类、标注核心贡献。
好了,不管你是什么角色,原理都一样。
下面我以开发者的 commit-message-writer 为例,带你完整走一遍 6 步流程。你跟着做一遍,换成你自己的内容,就是你的 Skill 了。
别急着写代码,先在脑子里想清楚这三个问题:
问题一:这个 Skill 到底做什么?
commit-message-writer 做的事很简单:分析代码变更,生成规范的 commit message。
但你要想清楚——"分析"到底分析什么?是所有文件的变更,还是只分析暂存区的?
我决定只分析暂存区的,因为 git diff --staged 最精准。用户 add 了什么,就分析什么。
问题二:什么时候触发?
用户什么时候会说"帮我写 commit message"?
我列了一下:
这些触发词,后面都要写进 description。
问题三:输出什么格式?
格式是 <type>: <中文描述>。
type 必须是 Conventional Commits 规范里的:feat、fix、docs、style、refactor、test、chore。
中文描述不超过 50 字。简洁精准,不啰嗦。
这三个问题的答案,就是整个 Skill 的骨架。
想清楚了?那我们开始写。
name 字段看起来简单,但有规则。不遵守,Skill 就无法被识别。
我来逐条讲,每条都给你正反例:
规则一:只用小写字母、数字、连字符
commit-message-writer、prd-generator、pdf-helper-v2CommitMessageWriter(不能大写)、PRD_Generator(不能用下划线大写混合)为啥?因为 Skill 的名字要和文件夹名完全一致。文件系统区分大小写,你写大写容易搞混。
规则二:单词之间用连字符,不用下划线
code-reviewer、data-analysiscode_reviewer(不能用下划线)、code.reviewer(不能用点号)这和域名命名规则一样。连字符是最安全的分隔符。
规则三:不能以连字符开头或结尾
architecture-diagram-diagram(开头不能是连字符)、diagram-(结尾不能是连字符)规则四:不能有连续的连字符
ecommerce-reportecommerce--report(两个连字符会被当成错误)规则五:长度限制 1 到 64 个字符
pdf-helper(9 个字符)规则六:name 必须和文件夹名一致
如果你文件夹叫 commit-message-writer/,那 name 字段就必须写 commit-message-writer。差一个字符都不行。
这是硬性规则,不是建议。
我给 Skill 起名叫 commit-message-writer。符合所有规则,一眼就知道是干什么的。
description 决定了 AI 什么时候加载你的 Skill。
我给你一个公式:
description = 做什么 + 什么时候用 + 输出什么 + 触发词
展开说:
照着这个公式,我写了 commit-message-writer 的 description:
description: >
分析代码变更并生成规范的Git commit message。
当用户需要提交代码、写commit message、git commit、
描述代码变更时使用。
遵循Conventional Commits规范,格式:<type>: <中文描述>。
逐行分析一下:
第一行 "分析代码变更并生成规范的Git commit message" → 做什么
直接告诉 AI 这个 Skill 的功能。
第二行 "当用户需要提交代码、写commit message、git commit、描述代码变更时使用" → 什么时候用 + 触发词
我把用户可能说的四种说法都写上了。不管用户怎么说,AI 都能识别到该加载这个 Skill。
第三行 "遵循Conventional Commits规范,格式:: <中文描述>" → 输出什么
告诉 AI 输出格式是 type 加冒号加中文描述。
整个 description 只有 3 行,但每个字都有用。不废话,不抒情,全是信息量。
正文是 Skill 的核心。AI 加载 Skill 之后,就是按正文里的指令来干活。
我来一步步写。
首先是 frontmatter,把 name 和 description 放进去:
---
name: commit-message-writer
description: >
分析代码变更并生成规范的Git commit message。
当用户需要提交代码、写commit message、git commit、
描述代码变更时使用。
遵循Conventional Commits规范,格式:<type>: <中文描述>。
---
然后写操作步骤,告诉 AI 具体怎么做:
## 操作步骤
1. 运行 `git diff --staged` 查看暂存区的变更内容
2. 分析变更,判断变更类型:
- feat: 新功能
- fix: 修复bug
- docs: 文档变更
- style: 格式调整(不影响逻辑)
- refactor: 重构(不是新功能也不是修bug)
- test: 测试相关
- chore: 构建/工具链变更
3. **只描述主要变更**,忽略次要细节
4. 用中文写简短描述,不超过50字
5. 输出格式:`<type>: <描述>`
注意第 3 条"只描述主要变更"。这是我迭代了三次之后加上的。
一开始没写这条,AI 就会把每个文件的每个小改动都罗列出来,50 字限制形同虚设。
然后写示例输出,让 AI 知道好的输出长什么样:
## 示例输出
feat: 添加用户登录页面
fix: 修复购物车数量计算错误
refactor: 重构订单查询逻辑提升性能
docs: 更新API接口文档
最后写反模式。告诉 AI 不要做什么,和告诉它做什么一样重要:
## 反模式
- 不要写模糊描述("update code"、"fix bug")
- 不要超过50字
- 不要试图罗列所有变更,抓主要矛盾
- 不要用英文描述(除非用户要求)
为什么反模式很重要?因为 AI 有自己的倾向。你不告诉它不要做什么,它就会按照自己的理解来。
比如 AI 默认喜欢写英文,你不写"不要用英文描述",它就会输出 feat: add user login page。你说得对,但我要中文。
把上面的部分拼在一起,就是一个完整的 SKILL.md。
现在文件写好了,要放到正确的位置。
先建文件夹。文件夹的名字必须和 name 字段一致。
我的是 commit-message-writer,所以文件夹也叫 commit-message-writer。
你的项目/
└── .claude/
└── skills/
└── commit-message-writer/
└── SKILL.md
路径是 .claude/skills/<你的Skill名字>/SKILL.md。
记住这个结构:
放好之后,文件结构应该是这样的:
.claude/skills/commit-message-writer/SKILL.md
就这么一个文件。纯指令型 Skill 不需要别的。没有脚本,没有数据文件,没有 references 目录。
简单清爽。
文件放好了,接下来就是测试。
测试方法很简单:重启 Claude Code,暂存一些代码变更,然后说"帮我写个 commit message"。
这是我刚写完的版本,非常简陋:
---
name: commit-message-writer
description: "生成Git commit message。当用户需要写commit message时使用。"
---
## 操作步骤
1. 查看 git diff 了解变更内容
2. 生成 commit message
就这么几行。我满怀信心地测试了。
结果呢?我输入"帮我写 commit",AI 确实触发了 Skill,但输出了:
update code
这种描述等于没说。
问题出在哪?
第一,我没告诉 AI 用什么格式(Conventional Commits)。 第二,我没告诉 AI 用中文。 第三,我没给示例输出。AI 不知道好的长什么样,就按自己理解来了。
我回去改了。加了类型分类和反模式:
---
name: commit-message-writer
description: "生成Git commit message。当用户需要提交代码、写commit message、git commit时使用。"
---
## 操作步骤
1. 运行 `git diff --staged` 查看变更
2. 判断变更类型:feat / fix / docs / style / refactor / test / chore
3. 用中文写描述,不超过50字
## 反模式
- 不要写模糊描述("update code"、"fix bug")
好多了。大部分情况能用了。
但有一次我改了 5 个文件,涉及登录功能、注册功能、密码重置、权限验证、用户列表。
AI 输出了:
feat: 添加用户管理模块的登录功能、注册功能、密码重置功能、权限验证、用户列表
50 个字?早超了。AI 把所有变更都列出来了,完全没有"抓主要矛盾"的意识。
我又加了两条规则:
## 操作步骤
1. 运行 `git diff --staged` 查看变更
2. 判断变更类型:feat / fix / docs / style / refactor / test / chore
3. **只描述主要变更**,忽略次要细节
4. 用中文写描述,不超过50字
5. 输出格式:`<type>: <描述>`
## 反模式
- 不要写模糊描述("update code"、"fix bug")
- 不要超过50字
- 不要试图罗列所有变更,抓主要矛盾
- 不要用英文描述(除非用户要求)
第 3 条"只描述主要变更"是关键新增。
同样的 5 个文件变更,这次输出变成了:
feat: 添加用户管理模块
简洁精准。8 个字,一击必中。
我花了三轮才打磨好。每轮测试 5 分钟,总共 15 分钟。
但之后每天至少省 5 分钟写 commit message 的时间。三天就赚回来了。
原则很简单:先跑起来,再逐步打磨。
不要一上来就追求完美。v1 能用就行,问题会在使用中自然暴露。
你不可能坐在那儿想出所有边界情况,只有真实使用才能发现漏洞。
我来模拟一下真实的使用场景。假设你的项目里有这些变更:
变更内容(git diff --staged 的输出):
diff --git a/src/components/LoginForm.vue b/src/components/LoginForm.vue
new file mode 100644
index 0000000..a1b2c3d
--- /dev/null
+++ b/src/components/LoginForm.vue
@@ -0,0 +1,45 @@
+<template>
+ <form @submit.prevent="handleLogin">
+ <input v-model="email" type="email" placeholder="邮箱" />
+ <input v-model="password" type="password" placeholder="密码" />
+ <button type="submit">登录</button>
+ </form>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+import { useRouter } from 'vue-router'
+
+const email = ref('')
+const password = ref('')
+const router = useRouter()
+
+async function handleLogin() {
+ const res = await fetch('/api/login', {
+ method: 'POST',
+ body: JSON.stringify({ email: email.value, password: password.value })
+ })
+ if (res.ok) {
+ localStorage.setItem('token', (await res.json()).token)
+ router.push('/dashboard')
+ }
+}
+</script>
diff --git a/src/utils/auth.js b/src/utils/auth.js
new file mode 100644
index 0000000..d4e5f6a
--- /dev/null
+++ b/src/utils/auth.js
@@ -0,0 +1,12 @@
+export function getToken() {
+ return localStorage.getItem('token')
+}
+
+export function isAuthenticated() {
+ return !!getToken()
+}
+
+export function logout() {
+ localStorage.removeItem('token')
+ window.location.href = '/login'
+}
v1 的输出:
update code
完全没法用。
v2 的输出:
feat: 添加LoginForm组件和auth工具函数
好一点了,但描述还是有点散。两个东西写一行,读起来不够清晰。
v3 的输出:
feat: 添加用户登录功能
精准。LoginForm 和 auth.js 都是围绕"用户登录"这个核心功能的,抓住主线就行。
写完 commit-message-writer 之后,你可能已经意识到一件事:
这个 Skill 全靠指令驱动,没有任何代码脚本。AI 读到指令,自己去执行。
这就叫纯指令型。
但有些场景,光靠指令不够用。比如你需要精确计算,需要读取外部数据,需要处理文件。
这时候就要加脚本了。
我来对比一下两条路径。
纯指令型就是只有一个 SKILL.md 文件。所有逻辑都用自然语言写在这个文件里。AI 读到就照着做。
适合什么? 适合处理文本类的任务。
这些都用纯指令型就够了。
commit-message-writer 就是一个纯指令型 Skill。它的全部逻辑就是:读 diff、判断类型、写描述。不需要算什么,不需要查什么数据库。AI 的语言能力足够处理这些。
纯指令型的优势是简单。 写一个文件,放到目录里,就完事了。不需要懂编程,不需要调试脚本,不需要管依赖。
带脚本型除了 SKILL.md,还有 scripts/ 目录(放 Python 脚本)和 data/ 目录(放数据文件)。
AI 读到 SKILL.md 里的指令,然后调用脚本去执行具体任务。
适合什么? 适合需要精确计算或需要大量数据的场景。
这些 AI 自己做容易出错,交给脚本更靠谱。
我下面给你一个完整的带脚本型 Skill 示例。这个例子是电商运营人员用的,每周出一份销售数据报告。
假设你是一个电商运营,每周要从美团或淘宝导出 CSV 数据,然后写一份销售报告。
报告包括:
这个需求如果让 AI 直接分析 CSV,它可能会:
所以我们把计算部分交给 Python 脚本。
文件结构:
ecommerce-report/
├── SKILL.md ← 告诉 AI 工作流程
├── scripts/
│ └── analyze_csv.py ← 实际干计算活的代码
└── references/
└── report-template.md ← 报告模板
SKILL.md 的内容:
---
name: ecommerce-report
description: >
分析电商销售CSV数据,生成结构化周报。
当用户提供销售数据、需要出报告、周报、
电商数据分析、销售分析时使用。
支持美团和淘宝数据格式。
---
## 操作步骤
1. 确认用户提供的数据文件路径
2. 运行分析脚本:
```bash
python3 scripts/analyze_csv.py <csv文件路径>
references/report-template.md 模板格式化输出输入CSV必须包含以下列:
**scripts/analyze_csv.py 的核心逻辑:**
```python
#!/usr/bin/env python3
"""电商数据分析脚本 - 处理CSV并输出结构化数据"""
import csv
import sys
from datetime import datetime, timedelta
from collections import defaultdict
def analyze(filepath):
# 读取CSV
with open(filepath, 'r', encoding='utf-8') as f:
rows = list(csv.DictReader(f))
# 按周分组
this_week = defaultdict(lambda: {'sales': 0, 'orders': 0})
last_week = defaultdict(lambda: {'sales': 0, 'orders': 0})
today = datetime.now()
week_ago = today - timedelta(days=7)
two_weeks_ago = today - timedelta(days=14)
for row in rows:
date = datetime.strptime(row['date'], '%Y-%m-%d')
amount = float(row['sales_amount'])
product = row['product_name']
category = row['category']
orders = int(row['order_count'])
if date >= week_ago:
this_week[(product, category)]['sales'] += amount
this_week[(product, category)]['orders'] += orders
elif date >= two_weeks_ago:
last_week[(product, category)]['sales'] += amount
last_week[(product, category)]['orders'] += orders
# 计算总销售额
total_this = sum(v['sales'] for v in this_week.values())
total_last = sum(v['sales'] for v in last_week.values())
growth_rate = (total_this - total_last) / total_last * 100 if total_last else 0
# TOP 10 商品
product_sales = defaultdict(float)
for (product, _), data in this_week.items():
product_sales[product] += data['sales']
top10 = sorted(product_sales.items(), key=lambda x: x[1], reverse=True)[:10]
# 品类占比
category_sales = defaultdict(float)
for (_, category), data in this_week.items():
category_sales[category] += data['sales']
total_cat = sum(category_sales.values())
category_ratio = {k: round(v/total_cat*100, 1) for k, v in category_sales.items()}
# 异常波动检测
anomalies = []
for (product, category) in this_week:
tw = this_week[(product, category)]['sales']
lw = last_week[(product, category)]['sales']
if lw > 0:
change = (tw - lw) / lw * 100
if abs(change) > 50:
anomalies.append({
'product': product,
'change': round(change, 1),
'direction': '暴涨' if change > 0 else '暴跌'
})
# 输出结果
print(f"本周总销售额: {total_this:.2f} 元")
print(f"环比增长: {growth_rate:.1f}%")
print(f"\nTOP 10 商品:")
for i, (name, sales) in enumerate(top10, 1):
print(f" {i}. {name}: {sales:.2f} 元")
print(f"\n品类占比:")
for cat, ratio in sorted(category_ratio.items(), key=lambda x: x[1], reverse=True):
print(f" {cat}: {ratio}%")
if anomalies:
print(f"\n异常波动预警:")
for a in anomalies:
print(f" {a['product']}: {a['direction']} {abs(a['change']}%")
if __name__ == '__main__':
analyze(sys.argv[1])
references/report-template.md:
# 电商周报 - {日期范围}
## 一、整体概览
- 本周销售额:{total_this} 元
- 本周订单数:{total_orders} 单
- 环比增长:{growth_rate}%
## 二、TOP 10 热销商品
{top10_list}
## 三、品类分布
{category_distribution}
## 四、异常波动
{anomalies}
## 五、运营建议
{ai_suggestions_based_on_data}
这是一个关键问题。
数据分析涉及精确计算。环比增长率是 (本周 - 上周) / 上周 * 100,这个公式很简单,但 AI 在处理大量数据行时容易漏算或重复计算。
TOP 10 排名如果让 AI 用自然语言做,它可能排错顺序。
异常波动检测需要逐商品对比两周数据,超过 50% 才标记。这个逻辑用 Python 写 5 行代码搞定,但用自然语言描述就容易有歧义。
分工原则:
脚本输出数字,AI 基于数字写分析和建议。这样既准确又高效。
什么时候用纯指令型,什么时候用带脚本型?
我给你一个简单的判断方法:
如果你要做的事,人用文字就能完成(写文章、做模板、审代码、提建议),就用纯指令型。
如果你要做的事,需要跑代码才能完成(数据分析、文件转换、批量处理、精确计算),就用带脚本型。
另一个判断角度是容错率。
如果输出错了只是措辞不好,纯指令型够用。
如果输出错了会导致计算错误、数据对不上、金额算错,那一定要用脚本。
不要因为"带脚本听起来更高级"就去用脚本。
大部分场景纯指令型就够了。脚本是手段,不是目的。能简单解决的问题,不要搞复杂。
先想清楚再动笔。 三个问题:做什么、什么时候用、输出什么。想清楚了再写,否则写出来也要改。
name 要规范。 小写、连字符、1-64 字符、和文件夹名一致。六条规则不能违反。
description 有公式。 做什么 + 什么时候用 + 输出什么 + 触发词。把这四样东西写进去,触发就不会出问题。
正文三要素。 操作步骤、示例输出、反模式。缺了哪个都会影响输出质量。
先跑起来再打磨。 v1 能用就行,v2 修大问题,v3 精细调整。三轮迭代足够了。
两种路径选简单的。 能纯指令解决的不要加脚本。需要精确计算和数据处理的才用脚本。
Q:一个 Skill 可以做很多事吗?
不建议。一个 Skill 专注做一件事。需要多件事就写多个 Skill,AI 会自动组合使用。
一个"万能 Skill"反而会因为 description 太宽导致触发混乱。
Q:正文写多长合适?
建议控制在 200 行以内。太长的正文会占用太多上下文空间,AI 也不一定全记住。
如果内容确实多,把详细部分拆到 references/ 目录的文件中。
Q:怎么知道自己的 Skill 写得好不好?
看两个指标:
两个指标都满意,就是好 Skill。
Q:带脚本的 Skill 需要什么前置条件?
需要 Python 环境。大部分系统自带 Python。
如果没有:
brew install python3winget install Python.Python.3.12sudo apt install python3脚本不需要任何第三方库,只用 Python 标准库就够了。
Q:references/ 目录里的模板文件,AI 怎么知道去读?
在 SKILL.md 的操作步骤里明确写出来。
比如"按 references/report-template.md 模板格式化输出"。
AI 执行到这一步就会去读这个文件。你不说它不会主动去读。
以上,这就是从零写一个 Skill 的完整流程。
你已经学会了规划、编写、测试、迭代,还了解了纯指令型和带脚本型的区别。
现在,动手写你的第一个 Skill 吧。
我们,下一个模块见。
优先展示同分类且标签更接近的内容,方便继续串联学习。
用最短路径做出第一个可用 Skill,快速建立对 Skill 结构和使用方式的直觉。
从现成 Skill 出发做个性化改造,理解一个 Skill 是怎么一步步被改成自己需要的样子。