你有没有过这种经历:给 AI Agent 写了一个 Skill,它能跑,但总是触发不准、执行不稳、隔两天就出 bug?就像做菜——照着网上的菜谱做,味道对,但跟大厨做的差了点什么。
差的那点东西,就是**专业性**。
我维护过一个 10 万行级别的 Skill(WeWrite 公众号全流程助手),从最初的一个简单脚本迭代到现在。过程中踩过的坑、总结的方法论,今天系统性地分享出来。
## 先搞清楚:Skill 是什么
30 秒版本:**Skill = 给 AI Agent 的工作手册**。
不是提示词(prompt),不是 API 文档,而是一份**结构化的操作指南**,告诉 Agent:
* **什么时候**该被触发(trigger)
* **怎么**一步步完成任务(workflow)
* **什么情况**下会出错以及怎么处理(pitfall)
* 做完之后**怎么验证**结果(verification)
一个 Skill 的物理结构长这样:
my-skill/ ├── SKILL.md # 核心指令(必需) ├── scripts/ # 可执行脚本 │ └── helper.py ├── references/ # 参考文档 │ ├── api.md │ └── best-practices.md └── templates/ # 模板文件 └── output.md
SKILL.md 是唯一必需的文件,其他都是辅助。但**专业的 Skill 和入门级 Skill 的差距,恰恰就在这些辅助文件里**。
## 能跑 vs 专业:一个对比
我用两个真实案例来说明差距。
### 案例 A:入门级 Skill(aihot)
这是一个查询 AI 资讯的 Skill,核心功能正确,结构清晰。它的 frontmatter 长这样:
— name: aihot description: AI HOT 资讯查询 Skill。当用户想知道”今天 AI 圈有什么”时使用。 —
工作流部分只有一段核心命令:
since=$(date -u -v-24H +%Y-%m-%dT%H:%M:%SZ) curl -s “https://aihot.virxact.com/api/public/items?mode=selected&since=$since”
**能用吗?完全能用。** 239 行代码,11KB 大小,功能完整。
### 案例 B:专业级 Skill(WeWrite)
同样是完成一个任务,WeWrite 这个公众号写作 Skill 有这些额外的东西:
| 维度 | 入门级 (aihot) | 专业级 (WeWrite) |
| — | — | — |
| Frontmatter | name + description | 完整元数据 + tags + related_skills + version |
| 触发条件 | 一句话描述 | 20+ 触发关键词 + 反向触发词(何时**不**用) |
| 工作流 | 1 个主路径 | 8 步流水线,每步有输入输出定义 |
| 错误处理 | 无 | 每个步骤有 fallback + 已知坑位手册 |
| 辅助文件 | 无 | references/ (7个文档) + scripts/ (5个脚本) + personas/ (人格库) |
| 验证方法 | 手动测试 | 自动化质量检查(禁用词扫描、human-ness 评分) |
| 迭代机制 | 无 | history.yaml 记录每篇文章的元数据,避免重复 |
**文件大小:11KB vs 103KB+。** 不是故意写长,而是每个字节都在解决一个实际问题。
## 专业 Skill 的五个关键维度
### 1. Frontmatter:Agent 决定是否加载你的 Skill 的唯一依据
这是最重要也最容易被忽视的部分。
**Agent 在加载 Skill 之前,只能看到 frontmatter 里的 `name` 和 `description`。** 如果这两个字段写得不好,你的 Skill 就像一本没有封面的书——再好也没人打开。
入门级写法:
— name: my-skill description: 帮我做 X 事 —
问题:太泛。”帮我做 X 事”可以匹配任何请求,导致**过度触发**(在不该用的时候被加载)或者**触发不准**(在该用的时候没被选中)。
专业级写法:
— name: commit-message-generator description: | Use when the user wants to generate conventional commit messages based on staged git changes. Covers feat/fix/docs/refactor/test/chore types. Triggers on: “commit message”, “生成提交信息”, “git commit”, “提交说明”. Do NOT use for: general text writing, PR descriptions, or changelogs. version: 1.2.0 author: your-name license: MIT metadata: hermes: tags: [git, commit, conventional-commits, automation] related_skills: [github-pr-workflow, github-code-review] —
关键改进:
* **description ≤ 1024 字符**(Hermes 硬性限制),但要把触发条件写清楚
* **明确反向触发词**(Do NOT use for):减少误触发
* **添加 metadata**:tags 用于分类检索,related_skills 用于关联推荐
* **版本号**:方便后续迭代追踪
### 2. 结构化指令:让 Agent 能”按图施工”
入门级的指令通常是段落式的:
> 先拉数据,然后处理一下,最后输出结果是 Agent 对”处理一下”的理解可能跟你完全不同。
专业级用**分步骤 + 表格 + 代码块**的组合。下面是一个工作流片段的写法示例——先定义参数表,再给命令:
| 参数 | 值 | 说明 |
| — | — | — |
| 端点 | `/api/public/items` | 主数据接口 |
| mode | `selected` | 精选模式(默认) |
| since | 动态计算 | 最近 24 小时 |
# 计算时间窗口(兼容 macOS 和 Linux) if [[ “$OSTYPE” == “darwin”* ]]; then since=$(date -u -v-24H +%Y-%m-%dT%H:%M:%SZ) else since=$(date -u -d ’24 hours ago’ +%Y-%m-%dT%H:%M:%SZ) fi curl -sH “User-Agent: $UA” “https://example.com/api/public/items?mode=selected&since=$since&take=50”
**输出格式**:JSON 数组,每项包含 `{id, title, category, url, published_at}`。下一步将输出传入 Step 2 进行过滤。
注意这几个细节:
* **表格定义参数**:Agent 不需要猜
* **代码块可直接复制运行**:不省略任何参数
* **明确输出格式**:下一步知道该怎么接
* **兼容性处理**:macOS 和 Linux 的 date 命令不同
### 3. 错误处理:专业和业余的分水岭
入门级 Skill 假设一切顺利。专业级 Skill 假设**一定会出错**。
下面是一段 pitfalls 章节的写法示例——每个坑都按”现象→原因→处理”三段式展开:
**Pitfall 1:API 返回空结果**
`items` 数组为空,但 HTTP 状态码是 200。原因:时间窗口内无数据,或 `since` 参数格式错误。
# 检查返回是否为空 response=$(curl -s “…”) count=$(echo “$response” | python3 -c “import sys,json; print(len(json.load(sys.stdin)))”) if [ “$count” -eq 0 ]; then echo “⚠️ 无数据,尝试扩大时间窗至 7 天” since=$(date -u -v-7d +%Y-%m-%dT%H:%M:%SZ) # 重试… fi
**Pitfall 2:User-Agent 被 403**
直接 curl 返回 403 Forbidden。原因:API 端点有 UA 黑名单。处理:所有 curl 必须带浏览器 UA。
**Pitfall 3:编码问题**
中文显示乱码。原因:远程终端 locale 不是 UTF-8。处理:传递数据用纯 ASCII JSON 文件,不在命令行中硬编码中文。
**专业 Skill 的 pitfalls 不是事后补的,而是边写边记录的。** 每次踩坑就记一条,积累下来就是竞争力。
### 4. 辅助文件体系:把 SKILL.md 控制在合理大小
SKILL.md 有 **100,000 字符的硬性限制**(约 36K tokens)。超过这个值,Agent 加载时会截断。
当 Skill 复杂度上升时,解决方案不是把 SKILL.md 写得更长,而是**拆分到辅助文件**:
professional-skill/ ├── SKILL.md # 核心(控制在 8-15K 字符) ├── references/ │ ├── api-reference.md # API 完整文档 │ ├── error-codes.md # 错误码对照表 │ └── best-practices.md # 最佳实践 ├── scripts/ │ ├── validate.sh # 自动化验证脚本 │ └── deploy.sh # 部署脚本 └── templates/ └── output-template.md # 输出模板
SKILL.md 里只保留简短引用指向细节,比如 API 文档部分可以这样写:
> 详见 `references/api-reference.md`,覆盖以下端点:
> – 数据采集(3 个端点)
> – 内容发布(2 个端点)
> – 媒体上传(1 个端点)
| 端点 | 用途 | 认证 |
| — | — | — |
| `/api/data` | 数据采集 | 无 |
| `/api/publish` | 发布内容 | OAuth |
| `/api/media` | 上传图片 | OAuth |
**原则:SKILL.md 放决策逻辑和关键路径,references 放参考信息,scripts 放可执行逻辑。**
### 5. 验证机制:能自动检查就不要靠人眼
专业 Skill 都有自检能力。最基础的是**验证清单**,发布前逐项确认:
* [ ] Frontmatter 以 `—` 开头,无前导空白行
* [ ] `name` ≤ 64 字符,仅小写字母 + 连字符
* [ ] `description` ≤ 1024 字符,包含触发词 + 反向触发词
* [ ] 所有代码块可直接复制运行(已实测)
* [ ] `curl` 命令都带了正确的 User-Agent
* [ ] 时间窗口计算兼容 macOS 和 Linux
* [ ] pitfalls 至少覆盖 3 个已知错误场景
* [ ] 总文件大小 ≤ 100,000 字符
* [ ] `related_skills` 引用的 Skill 确实存在
进阶做法是写一个**自动化验证脚本**:
#!/usr/bin/env python3 “””Skill 质量检查工具””” import yaml, re, pathlib def validate_skill(skill_path: str) -> dict: path = pathlib.Path(skill_path) content = path.read_text() issues = [] # 检查 1: frontmatter 格式 if not content.startswith(“—“): issues.append(“❌ 必须以 — 开头”) # 检查 2: description 长度 fm = yaml.safe_load(content.split(“—“)[1]) desc_len = len(fm.get(“description”, “”)) if desc_len > 1024: issues.append(f”❌ description {desc_len} 字符,超过 1024 限制”) # 检查 3: 代码块数量(教程型 Skill 要求 ≥ 5) code_blocks = re.findall(r”“`[sS]*?“`”, content) if len(code_blocks) < 5: issues.append(f"⚠️ 只有 {len(code_blocks)} 个代码块,建议 ≥ 5") # 检查 4: 是否有 pitfalls 章节 if "## Common Pitfalls" not in content and "## pitfalls" not in content.lower(): issues.append("⚠️ 缺少 pitfalls 章节") return { "skill": skill_path, "valid": len([i for i in issues if i.startswith("❌")]) == 0, "issues": issues, "stats": { "size_chars": len(content), "code_blocks": len(code_blocks), "has_pitfalls": "pitfalls" in content.lower(), } } if __name__ == "__main__": import sys, json result = validate_skill(sys.argv[1] if len(sys.argv) > 1 else “SKILL.md”) print(json.dumps(result, indent=2, ensure_ascii=False))
运行效果:
$ python3 validate_skill.py SKILL.md { “skill”: “SKILL.md”, “valid”: true, “issues”: [], “stats”: { “size_chars”: 12458, “code_blocks”: 7, “has_pitfalls”: true } }
## 实战:从零写一个专业 Skill
理论讲完了,动手写一个。场景:**Git 提交信息自动生成器**。
### 第一步:创建目录结构
mkdir -p commit-gen/{scripts,references,templates} touch commit-gen/SKILL.md
### 第二步:写 Frontmatter
— name: commit-message-generator description: | Use when user wants to generate conventional commit messages from staged git changes. Supports feat/fix/docs/refactor/test/chore/style/ci/perf/revert types. Triggers on: “commit message”, “提交信息”, “git commit”, “生成提交”, “commit msg”. Do NOT use for: PR descriptions, changelogs, release notes. version: 1.0.0 author: your-name license: MIT metadata: hermes: tags: [git, commit, conventional-commits, automation] related_skills: [github-pr-workflow] —
### 第三步:写核心工作流
Skill 的主体部分从获取 diff 开始。如果没有 staged 变化,先提示用户 `git add`:
git diff –cached –no-color
然后根据变更内容判断 commit type:
| 变更特征 | type | 示例 |
| — | — | — |
| 新功能/新文件 | `feat` | feat: add user login endpoint |
| Bug 修复 | `fix` | fix: null pointer in auth middleware |
| 文档变更 | `docs` | docs: update API reference |
| 重构(不改变行为) | `refactor` | refactor: extract validation logic |
| 测试相关 | `test` | test: add coverage for auth flow |
| 构建/CI 相关 | `ci` | ci: upgrade node version to 20 |
| 性能优化 | `perf` | perf: reduce DB queries by 50% |
生成的 commit message 遵循 Conventional Commits 规范:第一行 `type(scope): subject`(≤ 50 字符),第二行空行,正文可选(说明 why 不是 what),页脚可选(Breaking Change / Closes #123)。示例输出:
feat(auth): add OAuth2 Google login – Integrate with Google Identity Platform – Support token refresh flow – Add unit tests for token validation Closes #42
最后用户确认后执行:
git commit -e -F /tmp/commit-msg.txt
### 第四步:补充 Pitfalls 和 Checklist
**Pitfall 1:空 diff** — 用户忘记 `git add` 就触发了 Skill。处理:检测 staged 变化数量,为 0 时提示而非报错。
**Pitfall 2:subject 过长** — Conventional Commits 建议 subject ≤ 50 字符。处理:生成后自动检查长度,超长则拆分。
**Pitfall 3:scope 歧义** — 同一个 diff 可能涉及多个模块。处理:取变化文件最多的目录作为 scope,或省略 scope。
Verification Checklist:
* [ ] 支持 9 种 commit type
* [ ] subject 长度检查(≤ 50 字符)
* [ ] 空 diff 优雅降级
* [ ] `-e` flag 允许用户编辑
* [ ] 兼容 Git 2.0+
运行验证:
python3 scripts/validate_skill.py commit-gen/SKILL.md
**这就是一个完整的、专业的 Skill scaffold。** 从这里出发,根据实际需求迭代即可。
## 从能跑到专业的检查清单
最后,给你一个速查表。下次写 Skill 的时候逐项打勾:
| 层次 | 检查项 | 入门 | 专业 |
| — | — | — | — |
| Frontmatter | name/description 清晰 | ✅ | ✅ |
| | 包含触发词 + 反向触发词 | ❌ | ✅ |
| | 有 version/author/metadata | ❌ | ✅ |
| 结构 | 分步骤工作流 | ✅ | ✅ |
| | 表格定义参数 | ❌ | ✅ |
| | 代码块可直接运行 | ⚠️ | ✅ |
| | 明确输入输出格式 | ❌ | ✅ |
| 鲁棒性 | 有 pitfalls 章节 | ❌ | ✅ |
| | 覆盖 ≥ 3 个错误场景 | ❌ | ✅ |
| | 兼容性处理(OS/版本) | ❌ | ✅ |
| 工程化 | 拆分 references/ | ❌ | ✅ |
| | 有验证脚本 | ❌ | ✅ |
| | 有历史记录机制 | ❌ | ✅ |
| 总工作量 | | 30 分钟 | 2-4 小时 |
**结论:入门级 Skill 解决”能不能做”,专业级 Skill 解决”能不能稳定地做 100 次”。**
如果你的 Skill 只用一次,入门级就够了。但如果它会被反复调用、给别人用、或者承载关键业务流程,投入时间做到专业级是值得的——回报是调试时间的数量级减少。
* * *
*本文基于 Hermes Agent Skill 系统(hermes.nousresearch.com)和 WeWrite 公众号流水线的实战经验写成。文中所有代码片段均可直接复制运行。*


