首页 › tutorials › 依开放规范打造首个跨平台Skill

概述

本教程聚焦 2026 年正在成型的 Agent Skills 开放规范(agentskills.io),带你从零编写一份同时满足规范、可在 Claude Code 与 Codex/OpenClaw 等多运行时复用的 SKILL.md。适合已经跑通过 Claude Code 首个项目的开发者,希望把重复的团队约定沉淀成"按需加载、可分发、可跨平台"的能力模块。学完你将掌握:frontmatter 元数据规范、三层渐进式披露结构、捆绑脚本与引用文件的工程化写法、allowed-tools 安全护栏,以及"计划—校验—执行"的可复用模式。

前置条件


一、先理解:Skill 是什么,规范为何重要

一个 Skill 本质上是一个目录,至少包含一个 SKILL.md。它把"指令 + 可选脚本 + 引用资料 + 资产"打包成一个可被 Agent 按上下文自动发现并加载的模块。和把长提示词塞进 CLAUDE.md 相比,Skill 的关键优势是按需加载——只有当当前任务匹配时才进入上下文,不污染无关会话。

为什么强调"开放规范"?因为 2026 年 SKILL.md 已不再是 Claude Code 的私有格式。Codex、OpenClaw 等运行时都在采纳同一份清单文件。只要遵守规范的必填字段、把平台特有行为放进可选元数据,同一份 Skill 就能在多个 Agent 间移植,这让你沉淀的团队知识不会和单一工具绑定。

三层渐进式披露(Progressive Disclosure)

规范的核心设计是分层,让上下文成本最小化:

层级 内容 何时加载 设计目标
L1 元数据 frontmatter 的 name / description 会话开始时全量扫描 让 Agent 判断"要不要用",≤200 字符
L2 正文 SKILL.md 的 Markdown 指令 决定调用后才读 工作流与步骤,建议 <5000 token
L3 引用/脚本 references/、scripts/、assets/ 正文明确指向时才加载 大段规范、模板、可执行代码

一个装了 20 个 Skill 的项目,任意一次会话通常只为其中 1–2 个付出完整 token 成本——这就是分层加载的价值。

二、SKILL.md frontmatter 规范

frontmatter 用 YAML 写在文件最顶部,三横线包裹。

必填字段(开放规范底线):

可选字段:

一段合格的 description 长这样(注意它同时给了能力与触发词):

description: >-
  Drafts a changelog from git history grouped by conventional-commit type.
  Use when the user asks to "generate release notes", "write a changelog",
  or "what changed since the last release". Emits Keep-a-Changelog format.

反面教材:description: 帮助写发布说明——既没有触发词,也没说输入输出,Agent 很难在合适时机选中它。

三、跨平台兼容性对照

写"跨平台"并不等于处处等价,而是让差异优雅降级:

维度 Claude Code Codex OpenClaw
清单文件 SKILL.md SKILL.md(兼容) SKILL.md
个人目录 ~/.claude/skills/ 用户级 skills 目录 用户级目录
项目目录 .claude/skills/ .claude/skills/ 项目级目录
安全字段 allowed-tools 白名单 平台自有策略 ClawHub 注册表校验
注册表分发 插件市场 / ZIP 内置安装命令 ClawHub(类 npm)

可移植性原则:必填只留 name + description;把 allowed-tools 这类 Claude 专有字段当作"增强"而非"必需"——其它运行时遇到不认识的键会忽略它,而不是报错。脚本要面向 Agent 设计(命令行入参、JSON 出参、有意义的退出码),避免依赖某个 Agent 的交互特性。

四、动手:创建 release-notes-drafter Skill

下面构建一个完整、可运行的示例:读取自上次 tag 以来的提交,按约定式提交类型分桶,草拟 Keep-a-Changelog 风格的发布说明。它同时演示捆绑脚本、引用文件与安全护栏。

Step 1 — 建目录结构

在项目根或用户级目录下创建:

.claude/skills/release-notes-drafter/
├── SKILL.md
├── scripts/
│   └── group_commits.py
└── references/
    └── keepachangelog.md

目录名 release-notes-drafter 必须与 frontmatter 的 name 完全一致,否则部分运行时无法发现。

Step 2 — 编写 SKILL.md

---
name: release-notes-drafter
description: >-
  Drafts a changelog from git history grouped by conventional-commit type.
  Use when the user asks to "generate release notes", "write a changelog",
  or "what changed since last release", or before tagging a version. Emits
  Keep-a-Changelog format from commits since the last tag.
---

# Release Notes Drafter

You draft human-readable release notes from a repository's git history,
following Keep-a-Changelog. Deterministic grouping is delegated to a bundled
script; you own the prose and the judgment calls.

## When to use
- User asks for release notes / changelog / "what changed".
- About to cut a tag or publish a release.

## When NOT to use (negative triggers)
- User just wants a raw commit log — run `git log` directly.
- User only needs a semver bump — defer to a dedicated tool.
- Repo has no conventional commits — warn first, do not silently guess types.

## Workflow (plan → validate → execute)
1. PLAN: run `scripts/group_commits.py <range> --dry-run` to confirm scope.
2. VALIDATE: if the script exits with code 2 (empty range), ask the user for
   the correct revision range instead of guessing.
3. EXECUTE: run `scripts/group_commits.py <range>`, read the JSON, then write
   the changelog. Bucket order: Breaking, Added, Fixed, Performance, Changed,
   then Others. Reference each entry's short SHA.
4. Return the draft in a fenced block; list any commits you could not classify.

See `references/keepachangelog.md` for exact section headings and tone.

预期结果:保存后,当你在 Claude Code 里说"帮我生成本次发布的 changelog",Agent 会自动加载该 Skill 并按四步流程执行。

Step 3 — 捆绑脚本(面向 Agent 设计)

scripts/group_commits.py 的设计遵循四条铁律:命令行入参、JSON 出参、有意义退出码、破坏性操作默认 dry-run。

#!/usr/bin/env python3
"""Group conventional commits into changelog buckets.
Agent-facing: CLI args in, JSON out, meaningful exit codes."""
import sys, json, subprocess, re, argparse

TYPE_MAP = {
    "feat": "Added", "fix": "Fixed", "perf": "Performance",
    "refactor": "Changed", "docs": "Documentation", "test": "Tests",
    "chore": "Maintenance", "build": "Build", "ci": "CI",
}
CONV = re.compile(
    r'^(feat|fix|perf|refactor|docs|test|chore|build|ci)(\([^)]+\))?(!)?:\s*(.+)$',
    re.I)

def resolve_range(rng):
    if rng:
        return rng
    try:
        tag = subprocess.check_output(
            ["git", "describe", "--tags", "--abbrev=0"],
            stderr=subprocess.DEVNULL, text=True).strip()
        return f"{tag}..HEAD"
    except subprocess.CalledProcessError:
        return ""

def collect(rng):
    cmd = ["git", "log", "--pretty=format:%H%x09%s"]
    if rng:
        cmd.append(rng)
    out = subprocess.check_output(cmd, text=True, stderr=subprocess.DEVNULL)
    groups, misc = {}, []
    for line in out.splitlines():
        if "\t" not in line:
            continue
        sha, subj = line.split("\t", 1)
        m = CONV.match(subj)
        if m:
            typ, scope, bang, msg = m.groups()
            label = "Breaking" if bang else TYPE_MAP.get(typ.lower(), "Changed")
            groups.setdefault(label, []).append(
                {"sha": sha[:8], "scope": scope or "", "msg": msg.strip()})
        else:
            misc.append({"sha": sha[:8], "msg": subj.strip()})
    return {"range": rng or "all", "groups": groups, "misc": misc}

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("range", nargs="?", default="")
    ap.add_argument("--dry-run", action="store_true")
    args = ap.parse_args()
    if args.dry_run:
        print(json.dumps({"action": "would-parse", "range": args.range or "auto"}))
        return 0
    result = collect(resolve_range(args.range))
    if not result["groups"] and not result["misc"]:
        print(json.dumps({"error": "no commits in range", "range": result["range"]}))
        return 2
    print(json.dumps(result, indent=2, ensure_ascii=False))
    return 0

if __name__ == "__main__":
    sys.exit(main())

预期输出(在任意约定式提交仓库执行 python3 scripts/group_commits.py):

{
  "range": "v1.2.0..HEAD",
  "groups": {
    "Added": [{"sha": "a1b2c3d4", "scope": "(auth)", "msg": "support OIDC login"}],
    "Fixed":  [{"sha": "e5f6g7h8", "scope": "",       "msg": "null crash on empty payload"}]
  },
  "misc": []
}

退出码约定:0 正常产出;2 范围内无提交(让 Agent 转而询问用户)。这种"机器可读 + 可分支"的输出,是 Agent 能稳定编排的前提。

Step 4 — 引用文件(L3 加载)

references/keepachangelog.md 只在正文指向时才进上下文,放完整的章节标题与语气规范:

# Keep-a-Changelog 参考
- 段落顺序:Added → Changed → Deprecated → Removed → Fixed → Security
- 语气:过去时、面向使用者,不要写实现细节
- 每条以短 SHA 收尾,便于回溯

Step 5 — 加安全护栏

若希望该 Skill 只读不写,可在 frontmatter 追加:

allowed-tools:
  - Read
  - Bash(python3 scripts/group_commits.py:*)
  - Bash(git log:*)
  - Bash(git describe:*)

这样即使被提示注入诱导,也无法执行 rm 或外发请求。注意这是 Claude Code 专有字段,Codex/OpenClaw 会忽略它而不报错——这正是"平台特有字段放可选元数据"的可移植性体现。

五、测试与触发验证

  1. 静态自检:目录名 == name;description < 200 字符且含触发词;所有 references/、scripts/ 路径真实存在。
  2. 触发测试:开新会话,用 3–5 种不同措辞("写发布说明"/"这次改了啥"/"准备发版")确认能被自动选中;再用一条不该触发的指令("看一下最近提交")确认不会误触。
  3. 观察 thinking:若未触发,检查 description 是否缺少关键短语,迭代措辞后再测。

六、最佳实践与安全清单


实战案例小结

最终产物是一个三文件 Skill:SKILL.md(L1+L2)、scripts/group_commits.py(确定性分桶)、references/keepachangelog.md(L3 规范)。它演示了"计划—校验—执行"模式:先 dry-run 确认范围,校验空范围则反问用户,再产出 JSON 并由 Agent 撰写散文。同一份目录放进 .claude/skills/ 即被 Claude Code 发现,放进 Codex/OpenClaw 的对应目录同样可用——因为核心只依赖规范的 name + description。

工作流与架构图

flowchart TD
    U[用户: 生成本次发布说明] --> CC{Claude Code 会话}
    CC -->|L1 扫描全部 frontmatter| D[匹配 description?]
    D -->|是| L2[加载 SKILL.md 正文]
    D -->|否| SKIP[跳过, 不消耗 token]
    L2 --> PLAN[1.PLAN: dry-run 确认范围]
    PLAN --> VAL[2.VALIDATE: 退出码=2?]
    VAL -->|是| ASK[反问用户范围]
    VAL -->|否| EXE[3.EXECUTE: 运行脚本取 JSON]
    EXE --> L3[按需加载 references/]
    L3 --> WRITE[4.撰写 Keep-a-Changelog 草稿]
    WRITE --> OUT[返回草稿 + 未分类提交]
    ASK --> PLAN

    subgraph 跨平台复用
      P1[Claude Code<br/>.claude/skills/]
      P2[Codex<br/>兼容 SKILL.md]
      P3[OpenClaw<br/>ClawHub]
    end
    SKILL[同一份 SKILL.md 目录] --> P1
    SKILL --> P2
    SKILL --> P3

常见问题

延伸阅读

训练营适用性评估

参考来源

  1. How to create custom skills — Claude Help Center — 官方对 SKILL.md 结构、三层渐进式披露与 ZIP 分发的权威说明。
  2. AI Agent Skills Guide 2026: SKILL.md, Claude Code, Codex & Security — The Prompt Index — 开放规范、跨平台对照表与 Skill 安全/最佳实践。
  3. How to Build Claude Code Skills: 5 Examples (2026) — fp8.co — frontmatter 规范、allowed-tools 安全控制与多个可复用示例。
  4. Claude Code Skills: How to Set Them Up and Use Them — DEV Community — 目录约定与个人/项目级 Skill 的设置流程。

[!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学通信知识星球海报-扫码加入