真实案例带你理解mcp skill command- claude_0x03

目录概要

  1. 为什么拿 release notes 当例子
  2. 系统全景:四个文件,三层架构
  3. 跑起来是什么样
  4. 四个文件逐个拆解
  5. 完整执行流程时序图
  6. 两种 Skill 模式对比
  7. 实验:拆掉重来看看
  8. 踩过的坑

1. 为什么拿 release notes 当例子

上一篇画了决策树、讲了选型逻辑------但那些都是抽象的。到了这一篇,终于可以看真代码了。

选个贴真实工程的例子------Release Notes 生成器 。输入一个起始 git tag、一个目标版本号,输出一份按 feat / fix / chore / breaking 分桶好的 RELEASE_NOTES_v1.2.md。每个团队都干过这件事,手写到第 N 版会想"这活能不能自动化"------那就自动化给它看。需求本身不复杂,但拿来演示三层编排正好------既不是 hello world,又不会复杂到注意力被业务细节带偏。

更关键的是,它把我们在 02 篇讨论的每一个东西都用上了:Command 协调入口、Agent 独立上下文里扫 commit、两种 Skill 模式分工(读 git log 的知识预加载、排版写文件的流程直接调用)、模型分层省钱、memory 跨会话记住"上次处理到哪个 sha"------一个不落。

这一篇的源码是为教学清晰度构造的最小闭环。你要亲手建就照下面的 frontmatter + 正文敲四个文件;只想理解架构抽象,跟着拆就够了。


2. 系统全景:四个文件,三层架构

整个生成器由 4 个文件构成。先给你一张对照表,等会儿逐个看源码:

组件 类型 文件位置 职责
release-notes-crafter Command .claude/commands/release-notes-crafter.md 入口,问用户要 since-tag 和目标版本号
release-notes-agent Subagent .claude/agents/release-notes-agent.md 执行,独立上下文里扫 commit、分类
git-log-reader Skill(预加载) .claude/skills/git-log-reader/SKILL.md 知识,告诉 agent 怎么读 git log、怎么识别 conventional commits
release-notes-formatter Skill(直接调用) .claude/skills/release-notes-formatter/SKILL.md 输出,按模板生成 RELEASE_NOTES_<version>.md

架构总览:

graph TB User([用户]) -->|/release-notes-crafter| CMD subgraph "入口层(Command)" CMD[release-notes-crafter
model: haiku
负责编排] end subgraph "执行层(Agent,独立上下文)" AGT[release-notes-agent
model: sonnet
maxTurns: 8] AGT -.预加载.-> PS[git-log-reader Skill
commit 解析知识] end subgraph "输出层(Skill)" SK[release-notes-formatter
Markdown 排版] end CMD -->|Step 1 AskUserQuestion| Unit[since-tag? version? tone?] Unit --> CMD CMD -->|Step 2 Agent 工具| AGT AGT -->|Bash git log| GIT[(本地 git 仓库)] GIT -->|commit 列表| AGT AGT -->|返回分桶数据| CMD CMD -->|Step 3 Skill 工具| SK SK -->|写文件| FS[(RELEASE_NOTES_v1.2.md
release-notes/output.md)] style CMD fill:#afa style AGT fill:#faa style PS fill:#fcc style SK fill:#aaf

颜色编码:绿 = Command红 = Agent 链蓝 = 输出 Skill。这一套颜色后面都会用。


3. 跑起来是什么样

假设四个文件都建好了,在 Claude Code 里这样触发:

bash 复制代码
cd /你的项目
claude
arduino 复制代码
/release-notes-crafter

接下来 Claude 会:

  1. 弹出结构化单选菜单,分三步确认:
    • "起始 tag?"(默认上一个 tag)
    • "目标版本号?"(例如 v1.2.0
    • "风格:terse / detailed?"
  2. 派遣 release-notes-agent 去扫 commit(独立上下文里)
  3. Agent 跑 git log <since>..HEAD,按 conventional commits 分类
  4. Agent 返回结构化数据:"feat × 7, fix × 4, breaking × 1, chore × 12"
  5. Claude 调用 release-notes-formatter 按模板排版
  6. release-notes/ 目录下看到:
    • RELEASE_NOTES_v1.2.0.md
    • output.md(一段 summary)

跑完大约一分钟。主对话从头到尾清清爽爽------你不会在主窗口里看到 50 条 git log 刷屏,因为那部分全在 agent 的独立上下文里发生。


4. 四个文件逐个拆解

4.1 Command:release-notes-crafter

源码(关键部分):

yaml 复制代码
---
description: Craft release notes for a given version --- fetches commits between tags, categorizes them, writes a polished RELEASE_NOTES.md
argument-hint: [since-tag] [target-version]
model: haiku
---

# Release Notes Crafter Command

## Workflow

### Step 1: Collect Parameters
Use the AskUserQuestion tool to confirm:
- Starting tag (defaults to most recent tag if not given)
- Target version (e.g., v1.2.0)
- Tone: terse / detailed

### Step 2: Analyze Commits
Use the Agent tool to invoke the release-notes-agent:
- subagent_type: release-notes-agent
- prompt: Analyze commits from <since>..HEAD, return structured JSON

### Step 3: Write Release Notes
Use the Skill tool to invoke the release-notes-formatter skill with the categorized data.

这里有三个设计决策值得留意。

决策一:为什么 model: haiku

Command 只负责"协调",不做重活。让便宜的 haiku 处理入口交互性价比最高。真正的分析判断在 agent 里用 sonnet 干。这是个很有用的小技巧------不同层用不同模型,总账上省不少钱。

反问一下------既然 haiku 够用,为什么整个会话不全用 haiku?因为 haiku 分类 conventional commits 的时候经常掉链子(特别是遇到 refactor(api)!: 这种带 scope + breaking marker 的组合),分错一次你改一次,省下来的钱全吐回去。入口用便宜的、干活用贵的,这个配比得自己踩几次才有感觉,不是拍脑袋定的。

决策二:为什么用 AskUserQuestion 工具而不是直接"问用户"?

直接写"请问用户要从哪个 tag 开始"------Claude 大概率会用对话形式问你,用户回答"呃上一个 tag"还要 Claude 再推断一次。AskUserQuestion弹出结构化单选菜单(或者预填默认值),返回值规范,还能一次问三个问题不乱序。

graph LR A[普通提示词
'问用户要 since-tag'] --> B[Claude 用对话问] B --> C[用户打字回答
可能说 '上一个' 'v1.1' 'HEAD~20'...] D[AskUserQuestion 工具] --> E[弹出结构化菜单
可预填默认] E --> F[用户点选或确认
返回值规范] style A fill:#fcc style D fill:#cfc

决策三:Agent 用 Agent 工具、Skill 用 Skill 工具

官方一度把 Task 重命名为 Agent(v2.1.63),但旧名 Task 保留为 alias。你在别处看到 Task(subagent_type=...)Agent(subagent_type=...) 两种写法,都对,等价。

绝对不要用 bash 命令去调用 agent 或 skill------那不是设计路径。必须用 Agent 工具和 Skill 工具。

4.2 Agent:release-notes-agent

这是整个系统里信息密度最高的文件,几乎把 agent frontmatter 能用的字段都用上了:

yaml 复制代码
---
name: release-notes-agent
description: Use this agent PROACTIVELY when you need to draft release notes
  from a git commit range. This agent reads git log, categorizes commits using
  its preloaded git-log-reader skill, and returns structured data for a
  formatter skill to consume.
allowedTools:
  - "Bash(git log*)"
  - "Bash(git tag*)"
  - "Bash(git show*)"
  - "Read"
model: sonnet
color: blue
maxTurns: 8
permissionMode: acceptEdits
memory: project
skills:
  - git-log-reader
hooks:
  PreToolUse:
    - matcher: "Bash(git *)"
      hooks:
        - type: command
          command: python3 ${CLAUDE_PROJECT_DIR}/.claude/hooks/scripts/hooks.py --agent=voice-hook-agent
          timeout: 5000
          async: true
---

# Release Notes Agent

## Workflow

1. Run `git log <since-ref>..HEAD` per the preloaded git-log-reader skill instructions
2. Classify each commit (feat / fix / chore / refactor / docs / breaking)
3. For breaking changes, read commit body via `git show <sha>`
4. Memory: Update your agent memory with the commit range processed for historical tracking
5. Return structured JSON: { feat: [...], fix: [...], chore: [...], breaking: [...] }

逐字段看一遍:

字段 作用 这里的选择
description Claude 自动匹配的依据,必写 PROACTIVELY 明确触发条件:draft release notes from a git range
allowedTools 工具白名单 只给 Bash(git *) 子集和 Read,不给 Write(输出交给 skill 做)
model: sonnet 比入口重一级的模型 分类 conventional commits、判断 breaking change 要上下文理解
maxTurns: 8 最多执行 8 轮就停 一般 50 条 commit 3--5 轮搞定,8 轮留余量防死循环
permissionMode: acceptEdits 自动接受文件编辑 agent 不写文件,其实不强需要,但留着省事
memory: project 记忆存到项目级文件 下次跑记得上一次处理到哪个 sha,避免重复分析
skills: [git-log-reader] 预加载 skill 这是关键,看下一节
hooks.PreToolUse 每次调 git 命令前播音效 声音反馈,防止 agent 后台跑完你不知道

重点:这个 agent 的 Markdown 正文非常短------它只说"按照你预加载的 skill 执行"。真正"git log 怎么 parse、conventional commits 的前缀表是什么、breaking change 怎么识别"的细节全部在 skill 里。

这一点最容易被新手搞反------agent 正文写得越来越长、越来越全,把 git 命令、正则、错误处理全塞进去,写完自己都懒得读。正确的做法反过来:agent 只留骨架,知识全在 skill 里。

这就是职责分离

  • Agent 管"我要做什么"(工作流骨架)
  • Skill 管"具体怎么做"(领域知识)

4.3 Skill(预加载):git-log-reader

yaml 复制代码
---
name: git-log-reader
description: Instructions for reading git commits between two refs and
  classifying them as conventional commits (feat/fix/chore/refactor/docs/breaking).
user-invocable: false
---

## Instructions

1. Fetch commits:

git log --pretty=format:"%H|%s|%an|%ad" --date=short ..HEAD

markdown 复制代码
Parse each line into `{ sha, subject, author, date }`.

2. Classify by conventional commit prefix:
- `feat:` / `feat(scope):` → **feat**
- `fix:`  / `fix(scope):`  → **fix**
- `chore:` / `refactor:` / `docs:` / `test:` / `style:` → accordingly
- No recognized prefix → **uncategorized**(flag for human review)

3. Detect breaking changes:
- Subject contains `!:` (e.g., `feat!: drop Node 14`)
- OR commit body contains `BREAKING CHANGE:` (fetch via `git show <sha>`)

4. Return per-category lists, preserving commit sha so the formatter can link back.

user-invocable: false 是什么意思?

这个字段让 skill 不出现在 / 菜单里 ------用户不能主动 /git-log-reader 触发它。它只作为 agent 的"私人手册"存在。对这种"不自洽的半成品指令"(单独跑毫无意义),隐藏是对的。

为什么把 commit 解析规则放在 skill 里而不是 agent 里?

对比两种写法:

graph TB subgraph "写法 A 全塞在 agent 里" A1[release-notes-agent.md
300+ 行
git 命令 正则 前缀表 全在这] end subgraph "写法 B 当前实现" B1[release-notes-agent.md
10 行工作流骨架] B2[git-log-reader/SKILL.md
commit 解析规则] B1 -.预加载.-> B2 end style A1 fill:#fcc style B1 fill:#cfc style B2 fill:#cfc

写法 B 的好处:

  • Skill 可以被多个 agent 复用------以后做"月度活跃贡献者榜" agent,直接预加载同一个 skill
  • Agent frontmatter 保持干净------骨架和知识解耦
  • 便于演进------公司内部从 conventional commits 切到 gitmoji 约定,只改 skill 一个文件

4.4 Skill(直接调用):release-notes-formatter

yaml 复制代码
---
name: release-notes-formatter
description: Formats categorized commit data into a polished
  RELEASE_NOTES_<version>.md following the project's house style. Writes
  to release-notes/RELEASE_NOTES_<version>.md and release-notes/output.md.
---

## Instructions

1. Read the Markdown template from [reference.md](reference.md)
2. Render each category section (skip empty categories so空分桶不留空 heading)
3. Prepend breaking changes at the top with ⚠ marker --- breaking 最显眼
4. Append compare link: `https://github.com/<repo>/compare/<since-tag>...<target-version>`
5. Write to `release-notes/RELEASE_NOTES_<target-version>.md`
6. Write a one-paragraph summary to `release-notes/output.md`

## Additional resources

- For template, category headers, tone examples, see [reference.md]
- For 10 real release notes across OSS projects as style reference, see [examples.md]

两个值得注意的细节:

1. 没有 user-invocable: false

对比上一个 skill,这个可以被用户 /release-notes-formatter 直接触发 ,也会出现在 / 菜单------只要当前上下文里已经有分桶好的 commit 数据,比如你手工贴一段 JSON 进去,skill 就能直接跑。它是独立可复用的操作,不绑死在 agent 上。

2. 渐进式揭露(Progressive Disclosure)

SKILL.md 正文只有几行,但引用了 reference.mdexamples.md

graph TD A[SKILL.md
入口 简述 何时用
总是加载] -->|链接到| B[reference.md
Markdown 模板 类别头 语气
用到才加载] A -->|链接到| C[examples.md
10 份真实 OSS release notes
用到才加载] style A fill:#ff9 style B fill:#9ff style C fill:#9ff

核心理由:省 token 。把 500 行 Markdown 模板和 10 份 release notes 样本直接塞进 SKILL.md,每次 skill 被激活都会消耗这上千行;现在 Claude 只在真正要生成 release notes 时才去读 reference.md不这么做的代价你自己算------每次触发多 1000 行上下文,一天跑十次就是 1 万行白烧

这个设计模式不只是 release-notes-formatter 一个 skill 在用,它是 Skill 生态的一条基本设计原则。后面专门讲 Skills 的篇章还会再讲。


5. 完整执行流程时序图

sequenceDiagram actor User as 用户 participant Main as 主对话 Claude participant Cmd as release-notes-crafter participant Q as AskUserQuestion participant Agent as release-notes-agent
(独立上下文) participant Reader as git-log-reader
(预加载) participant Git as 本地 git 仓库 participant Fmt as release-notes-formatter participant FS as 文件系统 User->>Main: /release-notes-crafter Main->>Cmd: 加载 command 内容为提示词 Cmd->>Q: 调用 AskUserQuestion Q->>User: 弹出三步单选菜单 User->>Q: since=v1.1.0, target=v1.2.0, tone=terse Q-->>Cmd: 参数回传 Note over Main,Agent: Agent 启动
独立上下文 Cmd->>Agent: Agent 工具调用
subagent_type=release-notes-agent Note over Agent,Reader: skills: [git-log-reader]
已注入 system prompt Agent->>Reader: 按预加载指令执行 Reader-->>Agent: 提供命令 + 分类规则 Agent->>Git: git log v1.1.0..HEAD Git-->>Agent: 48 条 commit Agent->>Agent: 逐条分类 Agent->>Git: git show (检查 breaking) Git-->>Agent: commit body Agent-->>Cmd: { feat: [...], fix: [...], breaking: [...] } Note over Main,Fmt: 回到主上下文 Cmd->>Fmt: Skill 工具调用 + 传入结构化数据 Fmt->>Fmt: 读 reference.md 拿模板 Fmt->>FS: Write RELEASE_NOTES_v1.2.0.md Fmt->>FS: Write release-notes/output.md Fmt-->>Cmd: 完成 Cmd->>User: 展示摘要 + 文件路径

这张图里藏着两个精妙之处。

精妙一:Agent 上下文是真正隔离的

release-notes-agent 工作时,它看不到主对话的历史,也看不到 command 里的所有提示词。它只看到:

  • 自己的 frontmatter
  • 预加载的 git-log-reader 内容
  • Command 传给它的 prompt

执行完毕只有返回值 流回主对话。主对话不会被 48 条 git log 输出、breaking change 的 commit body、各种 git show 的中间结果污染------这一点在 commit 数多的大版本里价值爆炸。不信你试试不走 agent、在主对话里直接拉 300 条 commit 做分类,上下文窗口直接满。

精妙二:两种 skill 调用并存

  • git-log-reader预加载skills: 字段注入 agent 启动时的 system prompt)
  • release-notes-formatter运行时调用 (通过 Skill 工具显式调用)

同一个 skill 概念,两种使用模式------下一节专门对比。


6. 两种 Skill 模式对比

Agent Skill(预加载) Skill(直接调用)
例子 git-log-reader release-notes-formatter
何时加载 Agent 启动时注入 system prompt 被调用时执行
如何触发 通过 agent frontmatter 的 skills: 字段 通过 Skill 工具 或 /skill-name
作用 给 agent 注入领域知识 在当前上下文执行一个流程
用户可见 通常 user-invocable: false 隐藏 可以出现在 / 菜单
Token 成本 Agent 每轮都带着 skill 内容 只在调用时消耗

一条经验法则:

  • 如果 skill 是 agent 完成任务必需的知识 → 预加载
  • 如果 skill 是一个独立可复用的操作 → 直接调用

git-log-reader 是"没有它 agent 不知道怎么 parse commit"------必需的知识,所以预加载。 release-notes-formatter 是"把结构化数据排版成 Markdown 文件"这件事------独立操作,给什么数据都能跑,所以运行时调用。


7. 实验:拆掉重来看看

看完源码不如动手。试试下面三个变体:

实验 1:不用 Command,直接对话

arduino 复制代码
> 帮我根据 v1.1.0..HEAD 起一份 release notes 草稿

观察:Claude 会自动识别到 release-notes-agent(因为 description 里写了 PROACTIVELY),直接派遣它。你会省掉 AskUserQuestion 这一步,代价是参数需要在自然语言里说清楚。

实验 2:直接调用 skill

shell 复制代码
> /release-notes-formatter

观察:skill 会提示它需要分桶好的 commit 数据,因为当前上下文没有这些数据。它不会"自己去跑 git log"------它只负责排版,不负责分析。职责分离在这里看得最清楚。

实验 3:对话式触发排版

css 复制代码
> 已知 feat 有 X/Y/Z、fix 有 P/Q、breaking 有 W,给我排一份 v1.2.0 release notes

观察:Claude 会自动匹配 release-notes-formatter(description 匹配),把上下文里的分桶数据作为输入。这种用法适合你已经自己手工整理好 commit 列表、只想借个模板的场景。

小结

触发方式 路径 覆盖机制
/release-notes-crafter Command 主动触发 Command
"帮我起份 release notes" Claude 匹配 agent description Agent 自动匹配
/release-notes-formatter Skill 主动触发 Skill 主动调用
"给我排一份 release notes" Claude 匹配 skill description Skill 自动匹配

三种扩展机制的"触发面"被四个实验全部覆盖。上一篇的决策树里讲的"谁触发",到这里就有了具体感觉。


8. 踩过的坑

一些在真实项目里踩过的坑,列在这里防你踩。

坑 1:Task 还是 Agent

Command 源码里写 Task tool 还是 Agent tool?v2.1.63 之后官方重命名为 Agent tool,旧名 Task 保留为 alias。看到新老文档混用别困惑------两个都跑得通。

坑 2:Agent 里 allowedTools 忘声明 git 命令族

Agent 的 allowedTools 是白名单。如果只写 Bash(*),权限太大,review 过不去;如果只写 Bash(git log*),忘了 git show*------agent 跑到检查 breaking change 那步直接报"无工具可用"。新建 agent 最容易踩这个。正确做法:按需最小化,但覆盖全工作流,写清楚每条子命令。

坑 3:user-invocable: false 搞反

想让 skill 给 agent 预加载,要写 user-invocable: false(不让用户看见)。很多人会写成 true------结果 skill 同时出现在 / 菜单,用户点了一脸懵(因为它单独跑毫无意义)。

坑 4:memory: project 不会自动总结

Agent 的 memory 是有的,但需要你在 agent 正文里明确指示 "更新你的 memory 记录这次处理到哪个 sha"。不写的话 agent 不会主动维护 memory------这个默认行为谁拍的板我不知道,反正第一次用的人基本都会栽一次。release-notes-agent 正文里就有这一步:

sql 复制代码
4. Memory: Update your agent memory with the commit range processed for historical tracking

memory 字段本身的 user / project / local 三档差异、以及和 CLAUDE.md 的关系,后面 08 篇"Memory 与配置层级"会专门展开,这里先知道"需要手动触发"就够了。

坑 5:Skill 引用 reference.md 的路径

release-notes-formatter/SKILL.md 里写的是 [reference.md](reference.md)------相对路径。Skill 的相对路径是相对于 skill 目录本身的,不是工作目录。容易搞错,尤其从别的 skill 例子里 copy 过来不改的时候。


拆完看全景

把整个 release-notes 生成器剖开之后,前两篇的抽象概念应该都落到了具体的字段和执行路径上:

  • Command 用 haiku 干协调、Agent 用 sonnet 干活 ------ 分层用模型,省钱又合理
  • Agent 正文短、Skill 存细节 ------ 职责分离,骨架与知识解耦
  • Skill 分预加载和直接调用两种用法 ------ 同一个概念、两种使用模式
  • SKILL.md 写简述 + 引用其他文件 ------ 渐进式揭露,省 token
  • Agent 独立上下文隔离 ------ 主对话不被 50 条 git log 和中间推理过程污染

下一篇会把 Commands 单独拎出来细讲------官方内置命令有哪些、哪些值得背下来、以及"什么时候该自己封装 command"的判断标准。


Footnotes

外部链接

相关推荐
Flying pigs~~2 小时前
从零开始掌握A2A协议:构建多智能体协作系统的完整指南
人工智能·agent·智能体·mcp·多智能体协作·a2a
赞奇科技Xsuperzone2 小时前
零售行业桌面端算力升级方案(含最新GPU选型指南)
大数据·人工智能·零售
IDZSY04302 小时前
机乎 vs Moltbook:2026年AI社交平台全面对比
人工智能
bughunter2 小时前
别再无脑堆 Function Calling 了,这 5 个坑我替你踩完了
人工智能
AniShort2 小时前
从单兵作战到工业化量产!AniShort重构AI短剧生产革命
大数据·人工智能·重构
2501_948114242 小时前
大模型API调用成本优化的工程路径:星链4SAPI聚合网关的技术实践
大数据·开发语言·人工智能·架构·php
JAVA学习通2 小时前
AI Agent 工具调用机制深度解析与 Spring Boot 工程集成实战(2026版)
java·人工智能·spring boot·python·spring
南宫萧幕2 小时前
从YALMIP工具箱到车辆工况仿真:MATLAB控制策略开发的完整实践指南
开发语言·人工智能·matlab·simulink
做个文艺程序员2 小时前
Function Calling 与工具调用:让 AI 真正干活【OpenClAW + Spring Boot 系列 第5篇】
人工智能·spring boot·后端