什么是 Skill?我用「发博客」这个例子跑通了一遍

📖 本文共 3,274 字,阅读需要 11 分钟

一、听说很久,今天才第一次动手

Skill(技能)这个词我听过很多次,一直没实践。今天因为一件很具体的小事,终于把它跑通了——把「发博客」这个动作固定下来,以后我只要说一句「发博客」,AI 就能自动完成整套流程。

这篇文章就用这个真实例子,讲清楚三件事:

  1. Skill 到底是什么(和提示词、说明文件有什么不同)
  2. 文件放哪里、怎么写
  3. 我踩的两个真实的坑

二、问题的起点:每次发博客都要重新交代一遍

我的博客(WordPress + 一套自己写的 Node 发布脚本)发布流程其实已经固化了:

flowchart LR
    A[写 Markdown] --> B[放 posts/年份/ 目录]
    B --> C[写 front matter<br/>title/slug/categories/status]
    C --> D[node publish.js 执行]
    D --> E[按 slug 查重<br/>有则更新 无则创建]
    E --> F[发布成功后脚本回写 id]

流程是固定的,但**「固定」只存在于我脑子里**。每次让 AI 帮我发,我都要重复说一遍:

  • 发布器在哪个目录
  • front matter 怎么写
  • 命令是什么
  • 发完要去验证

一次两次没关系,做十次就是纯浪费。这正是 Skill 要解决的问题。

三、Skill 到底是什么

一句话说清:

Skill = 把「一套固定流程 + 其中的规矩和雷区」写成一个文件,让 AI 在需要时自己加载执行。

它和常见的几种自定义机制的区别:

机制 本质 什么时候生效
全局说明(instructions) 一直挂在上下文里的”家规” 每次对话都生效
提示词(prompt) 一段一次性的任务模板 你手动触发那一次
技能(skill) 带流程和资产的完整工作流 按需自动加载(描述命中才读)
自定义 Agent 隔离上下文的子代理 需要时派发

Skill 最妙的一点是渐进式加载:

  1. 平时只读它的 name 和 description(几十个 token,几乎不占上下文)
  2. 只有当你的话和 description 对上了,才把整个 SKILL.md 正文读进来
  3. 正文里引用的脚本、模板、参考资料,再等真正用到时才加载

所以你可以装很多个 Skill,它们平时几乎不消耗任何成本。

四、落地:文件放哪儿

这一步我一开始就猜错了。有个关键区别:

位置 能放什么 生效范围
项目/.github/skills/<名字>/SKILL.md Skill 只有这个项目
个人主目录/.copilot/skills/<名字>/SKILL.md Skill(个人级) 所有项目都生效
用户配置目录的 prompts/ 只支持 .prompt.md / .instructions.md / .agent.md 所有项目

注意第三行:用户级的 prompts 目录不支持放 Skill(我本来打算放那儿,查了官方参考文档才发现)。想要”走到哪个项目都能用”,必须放在个人主目录下的 .copilot/skills/(另有 .agents/skills/、.claude/skills/ 两个等价位置)。

我实际创建的两个文件:

C:\Users\<用户名>\.copilot\skills\blog-publish\SKILL.md          ← 正主(Skill)
C:\Users\<用户名>\AppData\Roaming\Code\User\prompts\
                                      blog-publish.prompt.md      ← 兜底(斜杠命令)

第二个是保险:万一某次 Skill 没被扫描到,还能用 / 斜杠命令手动触发。

五、SKILL.md 怎么写

头部是一小段 YAML 元信息,后面是正文:

---
name: blog-publish          # 必须和文件夹名一致
description: '把 Markdown 发布到 haoyelaiga.com。当用户说"发博客"、"发到博客"、
"把这条发到博客"时使用。负责建文件、执行发布脚本、用 REST 验证并回报结果。'
argument-hint: '要发布的内容,或"上一条回答"'
---

# 发博客

## 先读这些事实,不要靠猜
- 发布器目录:<绝对路径,不在任何项目内>
- 凭据在 .env → 绝不读取、绝不打印
- 命令:node publish.js "<md 绝对路径>"

## 流程
1. 建文件 posts/<年>/<名>.md
2. 写 front matter(注意:不要写空的 id 行)
3. 执行发布
4. 用 REST 二次验证
5. 回报 ID / 状态 / 链接

## 不要做
- 不要修改 publish.js
- 不要把凭据读进上下文

有两条经验值得单独说:

第一,description 就是”发现界面”。 官方文档里那句话非常关键:如果你的触发词没写在 description 里,AI 就找不到这个技能。 所以我把「发博客」「发到博客」「发布博客」「把这条发到博客」全部写了进去——写不全就等于用不上。

第二,一定要写”不要做”这一节。 AI 很容易”过度发挥”:你让它发博客,它可能顺手去”优化”你的发布脚本。所以我明确写了两条硬约束:不要改 publish.js、不要读取 .env 凭据。事实证明这一节后来真的救了我一次(见下一节)。

六、怎么用

两种方式:

方式一:直接说人话(推荐)

发博客:把上一条回答发上去

因为触发词写在 description 里,AI 会自动加载这个 Skill 并按流程执行。

方式二:/ 斜杠命令(手动指定)

  1. 打开 Chat 面板,确认在 Agent 模式
  2. 在输入框最开头敲 /(必须是第一个字符,打在中间不触发)
  3. 继续打字过滤,比如 blog
  4. 上下键选中 → 回车
  5. 在提示后面补上内容 → 回车发送

新建的 Skill 或 prompt 文件有时需要 Developer: Reload Window 重载一次才会被扫描到。

七、我踩的两个坑

坑一:终端吞输出,误判”发布失败”

我的 PowerShell 终端有个老毛病:命令明明执行了,却什么回显都没有,甚至会提示”命令无输出”。

第一次发博客就中招了——终端一片空白,我以为失败了。结果去查线上:文章早就发出去了。

教训:不要靠终端回显判断成败,要去查真实结果。所以我把这条写进了 Skill:发布后必须调 WordPress REST 接口确认 id / status / link。

坑二:我差点”修好”一个没坏的脚本

这是今天最有价值的一次翻车。

现象:文章成功发布(ID 1346),但 Markdown 的 front matter 里 id: 还是空的,没被回写。

我的判断:脚本的写回函数有 bug。我的”证据”看起来很硬——那个正则用了 \s*,而 \s 是包含换行符的,配上多行模式的 $ 锚点,它会把换行吃掉,于是”id 这一行”实际匹配到了下一行,把 title: ... 当成了 id 的值;值非空 → 脚本判定”已有 ID,跳过写入”。

推理没错,我随即改了这个正则,还顺手把回写逻辑从”仅新建时执行”扩到”更新时也执行”。

然后作者看了以后说:

原本的逻辑是新建 MD 时第一行没有 ID 字段,发布成功后才加上 ID 并写回值。所以你刚才改的要删掉,保留我原有的发布逻辑。

原来问题根本不在脚本,在我——我按自己的模板多写了一行空的 id:,正好踩进那个分支。按作者的约定(新建文件不放 id 字段),原脚本工作得好好的。

于是我 git checkout 把脚本原样回退,只保留那条”不要写空 id 行”的约定。

三条教训:

  1. 动手改别人的代码前,先确认对方的设计约定——bug 可能只是”你用了错误的用法”
  2. 改完要能干净回退(这次靠 git diff 先确认改动范围,再整体还原,很踏实)
  3. 踩过的坑要写进 Skill——所以我专门在 front matter 那一节加了警告:不要写空的 id: 行,附上原因和症状。这才是 Skill 相对”临时交代一句”的真正价值。

八、小结

  • Skill 的本质:把重复解释的流程,从”我脑子里的约定”变成”文件里的规则”
  • 它按需加载,装很多也不占上下文
  • 放对位置(个人级是 ~/.copilot/skills/,不是 prompts/),否则怎么点都没反应
  • 描述里写全触发词,正文里写清”不要做”
  • 把踩过的坑写进去——坑只踩一次的秘诀,就是把它写成规则

顺带一个有意思的闭环:这篇文章本身,就是用刚做好的 blog-publish Skill 发布的。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部