我检查了自己 11 个 Agent Skill 的使用统计。有一个数据让我愣了几秒。

创建几天了,使用次数:0

不是模型不够聪明。不是 Prompt 写得差。是那个 Skill 的文档写成了"自嗨型说明书"——Agent 根本不知道什么时候该用它。

它负责的是 Gateway 配置——启动、排查、调试消息平台连接,是我日常最高频的操作之一。但 Agent 一次都没调用过它。

我翻出它的 SKILL.md,看了 30 秒就找到了原因。

它的 trigger 写了三个词:"gateway"、"启动 gateway"、"gateway 配置"。而我有另一个 Skill,trigger 里也写了 "gateway setup"、"gateway 配置"。两个 Skill 的触发条件几乎一样。

Agent 面对两个"声称能做同一件事"的 Skill,不知道该选谁。于是它做了最省事的选择——两个都不选

改了文档之后,同一个 Skill 开始被正常调用。

这篇文章不讲 prompt 怎么写。讲一个更根本的问题:你的 SKILL.md 不是一篇"给 AI 的指令",是一份"给 Agent 的接口文档"。文档结构不对,prompt 写得再好也没用。

——————————————————————————————

SKILL.md 就是一份 API 文档

Agent 读取 SKILL.md 的方式,跟开发者阅读 API 文档的方式完全一致。它不是在"理解你写了什么",它是在做一件事:在当前对话里扫描所有可用的接口,匹配最合适的那个。

把 Skill 想象成一个 API 端点,四要素的对应关系一目了然:

API 文档里有什么

SKILL.md 里对应什么

它回答的问题

端点 URL,比如 GET /users/{id}

triggers 列表

"什么时候调用我?"

请求/响应说明

description

"我能做什么,返回什么?"

错误码

边界声明

"什么时候不要调用我?"

curl 命令示例

对话示例

"用起来到底长什么样?"

大部分 Skill 被 Agent 忽略,不是因为 prompt 写得不够好,而是因为四个要素里面至少缺了两个。就像一个 API 文档没有端点 URL 也没有错误码——开发者找不到入口,调错了也不知道为什么。

不同 Agent 平台的 SKILL.md 格式略有差异(有些只有 name + description,有些有独立的 trigger 列表),本文以 Hermes Agent 为例,但四要素原则通用。

下面逐个拆解。

——————————————————————————————

规范一:Trigger 要精确到"场景",不是"关键词"

这是最致命的死因。所以我放第一个讲,也给最多篇幅。

用 API 来类比:端点写成 GET /users 和写成 GET /users?role=admin&status=active。后者更精确,但调用率反而更高——因为调用者看完就知道"这就是我要的接口"。

Skill 的 trigger 同理。你写得越模糊,Agent 越不敢用。

反面案例:两个 trigger 重叠的 Skill。

Skill A 的 trigger:

- "gateway"

- "启动 gateway"

- "gateway 配置"

Skill B 的 trigger:

- "gateway setup"

- "gateway 配置"

- "gateway 连接"

用户说了一句"gateway 启动报错"。两个 Skill 的 trigger 都匹配了。Agent 面临一个二选一:它不知道这两个 Skill 有什么区别,也不知道该用哪个来解决"启动报错"这个问题。

Agent 的选择逻辑很简单——如果两个候选看起来都能用,且无法判断优劣,就降低调用概率。两个都不选,比选错更安全。

结果:Skill A 使用次数 0。

正面案例:精确到"动作 + 内容类型"的 trigger。

Skill C 的 trigger:

- "用户分享了一份职业转型计划"

- "用户问'这个方向可行吗'并附上了具体方案"

- "用户提供了自我评估数据,想匹配市场机会"

每一条都包含了两层信息:用户做了什么(分享了计划 / 问了可行性 / 提供了数据)+ 涉及什么内容(职业计划 / 具体方案 / 评估数据)。Agent 几乎不可能误判。

可操作原则:

1. 每条 trigger 至少包含"动作 + 内容类型",不要只放关键词

2. 写完后检查:跟其他 Skill 的 trigger 有没有重叠?有重叠就必须加限定词区分

3. 删除宽泛词("配置""管理""优化""排查"),改成具体场景("配置文件报错""启动失败排查""端口冲突")

一个简单的自查方法:把你的 trigger 列表读一遍,想象自己是 Agent——看到这些 trigger,你能确定"这个 Skill 和另一个有什么区别"吗?如果不能,Agent 也不能。

——————————————————————————————

规范二:Description 要写"能做什么",不是"是什么"

继续用 API 打比方。两份 API 描述:

"Returns user data."

"Returns user profile including name, email, and role. Use this when you need to display user info in a dashboard or verify permissions."

后者没有多写任何技术细节,只是多说了两件事:输出具体包含什么、什么场景用它。开发者看完就知道什么时候该调。

反面案例:只说了"是什么"。

Skill D 的 description:

管理、配置和排查 Agent 本身。

Agent 读完知道:哦,这个 Skill 跟 Agent 管理有关。但知道这个有什么用?它不知道什么时候该用——用户说"帮我查一下配置",该用这个还是用另一个?用完了能得到什么——一条命令、一个配置文件、还是一段解释?

正面案例:写了"能做什么 + 产出什么 + 什么场景"。

Skill E 的 description:

帮用户把模糊的业务需求拆解成清晰的自动化工作流。输出可直接在 Coze、Dify、n8n 等平台落地的节点图和工作流描述。

三句话把三件事全讲清楚了:

- 输入:模糊的业务需求

- 输出:节点图 + 工作流描述(可直接落地)

- 场景:用户想在 Coze/Dify/n8n 等平台搭自动化

Agent 读完就知道"用户说我想自动化 XX 流程 → 找 Skill E"。

一个简单的检验方法:把 description 里的 Skill 名字遮住,只看剩下的文字。 如果看完还是不知道"当前这个对话该不该用这个 Skill",说明你写的是产品简介,不是触发条件。

可操作原则:每条 description 必须回答三个问题:

1. 用户给我什么?(输入)

2. 我产什么结果?(输出)

3. 什么场景用我,而不是其他 Skill?

——————————————————————————————

规范三:边界声明——"什么时候不要用我"

API 文档如果没有错误码,调用者调错了只能猜。SKILL.md 如果没有边界声明,Agent 在不该出现的场景里强行输出——用户看到的结果就是"这 AI 怎么这么蠢"。

正面案例。

Skill F 的 SKILL.md 里专门有一节叫"诚实边界",明确列了这个 Skill 做不到什么。Skill G 在注意事项里写了一行:"如果用户问的是纯技术问题,切换回正常模式。"

就一行字。但它阻止了一个经典翻车场景:用户问"这个 bug 怎么修",Agent 还在用温柔共情的语气回复"听起来不容易"。读者会直接关掉页面。

反面案例:完全没有边界声明。

大部分 Skill 根本没写这节。结果就是:

- 用户问技术问题 → Skill J 还在温柔共情

- 用户想快速解决 → Skill H 开始长篇大论

- 用户只想查一个命令 → Skill I 开始拆解整个业务流程

Agent 不是故意的。是你没告诉它"什么时候该闭嘴"。

可操作原则:加一节"何时不应触发"或"注意事项"。三种写法:

- 排他声明:"只用于 X,不用于 Y"(如"只负责运行时排错,不负责初始配置")

- 依赖声明:"需要先配置 X 才能用"(适用于有前置依赖的 Skill)

- 替代声明:"如果是 Y 场景,用其他 Skill"(解决 trigger 重叠最有效)

至少写两件事:

1. 什么场景下,即使用了 trigger 匹配,也不该激活

2. 这个 Skill 明确做不到什么

——————————————————————————————

规范四:示例——给 Agent 一个"参考答案"

Stripe 的 API 文档最有名的是什么?不是参数说明有多详细——是每个端点都有一个可以复制粘贴的 curl 命令

开发者不需要理解,照着跑就行。Agent 也一样。

正面案例。

Skill G 在正文最后给了一段完整的对话示例——用户说什么,Agent 应该回什么。Agent 读到这一段时,不需要"理解"什么叫友好回复,它直接看到了一个标准答案。

反面案例:有方法论,没示例。

很多 Skill 洋洋洒洒写了流程、设计哲学、方法论框架,但到了最关键的地方——"用户输入 X,我该输出 Y"——没了。Agent 读完整篇 SKILL.md 的感受是"这个 Skill 理念很好",但不知道具体怎么用。

一个微型对比:

Before(没有示例):

用户:"gateway 启动报错"

→ Agent:不确定这个 Skill 具体怎么处理,没调用

After(加了示例):

用户:"gateway 启动报错"

→ Skill 读取 gateway 日志 → 定位到端口冲突 → 返回:netstat -ano | findstr :8080

加了示例之后,Agent 不需要猜测"排查故障"具体意味着什么——它有了一个可直接套用的模板。

可操作原则:至少给一个完整的输入→输出示例。 不一定覆盖所有情况,但要给 Agent 一个"标准答案"当参照。Agent 对示例的跟随度,远高于对文字描述的跟随度。

——————————————————————————————

一个 Skill 的完整改造:四要素 Before / After

回到开篇那个 0 次调用的 Skill。

我把它按四个规范完整改了一遍。下面是改造前后的对照:

要素

Before(使用次数:0)

After

Trigger

"gateway"、"启动 gateway"(跟 Skill B 重叠)

"gateway 启动报错"、"gateway 端口冲突"、"gateway 连接超时"——精确到故障场景,去除了跟 Skill B 的重叠

Description

"配置、启动、停止和调试 Gateway 消息平台"——只说了是什么

"当需要启动 gateway 服务、排查连接失败、或诊断消息平台通信问题时使用。注意:平台接入凭证(App ID / Token)的配置请用 Skill B。"——说了场景 + 产出 + 边界

边界声明

"本 Skill 不负责飞书/Telegram/Discord 的 App ID 配置(用 Skill B),也不涉及代码项目开发。"

示例

用户:"gateway 启动后飞书收不到消息" → 检查运行状态 → 验证 webhook 配置 → 返回具体修复命令

改造前:Agent 面对它和 Skill B,不知道有什么不同、不知道该选谁、不知道选完能干什么。结果使用次数 0。

改造后:trigger 精确到具体故障场景,description 直接告诉 Agent"注意——如果你要配飞书 App ID,别找我,找 Skill B",示例给出了标准处理流程。

——————————————————————————————

改完文档之后

改完文档之后,Agent 开始调用那个 Skill 了。但不是每次都调。有时我写了完美的 trigger,它还是选了另一个 Skill。

写 SKILL.md 的时候,假设读它的 Agent 对这个 Skill 一无所知,只有 3 秒钟决定要不要用它。这 3 秒里,description 决定生死。它只看你写下来的东西。

文档规范解决的是"能不能被找到"的问题。但被找到之后呢?当 11 个 Skill 同时在场,Agent 怎么排序、怎么决定优先级——那涉及 Skill 的检索和加载机制。这是我接下来要去搞清楚的事。

——————————————————————————————

附:SKILL.md 四要素自检清单

复制这段,逐项对照你自己的 SKILL.md 打勾:

### Trigger(端点路由)

☐ 每个 trigger 至少包含"动作 + 内容类型",而非单个关键词

☐ 没有两个 trigger 含义重复

☐ 与其他 Skill 的 trigger 有明显区分度(用 grep 检查重叠)

### Description(接口说明)

☐ 写了"我接收什么输入"

☐ 写了"我产出什么输出"

☐ 写了"什么场景用我,而不是其他 Skill"

### 边界声明(错误码)

☐ 有"何时不应触发"或"注意事项"小节

☐ 写了至少一条"即使用了 trigger 匹配也不该激活"的场景

☐ 写了至少一条"这个 Skill 明确做不到"的事

### 示例(curl 命令)

☐ 有至少一个"用户输入 → Skill 输出"的完整示例

☐ 示例覆盖了最常见的触发场景

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐