SSD Nodes Learn 🎉 VPS $5.50/月起
指南 Matt Connor作者: Matt Connor · 更新于 2026-08-13

如何编写自定义 Agent 技能:从真实故障中提炼

编写 Agent 技能的最佳实践是从真实故障中提炼。本文详细解析 SKILL.md 文件结构、触发条件编写规范以及如何通过复现错误进行测试,帮助你将重复出现的 AI 错误转化为可复用的自动化技能。

从一次真实故障中编写您自己的 Agent 技能

编写 Agent 技能的最佳方式是从一次真实故障中提炼。找到一个您的编码 Agent 连续两次出错的任务,记录下您两次输入的修正内容,并将该修正保存为 Agent 可以自行加载的 SKILL.md 文件。此后的步骤均为机械操作:文件布局,以及决定该技能是否触发的那一行代码。

顺序至关重要。凭空编写的技能记录的是您从未遇到过的问题,且在每次会话中都会占用上下文空间。从您亲眼目睹的故障中提炼出的技能自带测试:再次询问相同的问题,观察 Agent 这次是否能正确处理。如果您对该格式尚不熟悉,请先阅读 什么是 Agent 技能以及 Agent 如何加载它们,然后再回来编写。

从代理两次出错的任务开始

一次是偶然,两次就是模式,而模式值得记录在案。

以下是在真实服务器上反复出现的故障。你要求代理在 Nginx 中添加一个反向代理块。它修改了 /etc/nginx/conf.d/app.conf,然后运行 sudo systemctl restart nginx。由于编辑内容存在拼写错误,Nginx 拒绝启动,导致站点在修复前一直处于宕机状态:

nginx: [emerg] unknown directive "proxy_pas" in /etc/nginx/conf.d/app.conf:12
Job for nginx.service failed because the control process exited with error code.

你在对话中纠正了它。在触碰服务之前,先用 sudo nginx -t 测试配置,然后使用 reload 而不是 restart 来应用更改。一周后,在执行另一项任务时,它又犯了同样的错误。第二次出错就是信号。

在故障发生时,记录下两件事:你输入的请求,以及你给出的纠正,使用你当时的原话。这两行内容就构成了技能。请求说明了触发条件必须匹配的内容,而纠正则是全部核心内容。

Anthropic 官方的创作指南将此放在首位。在没有技能的情况下,让代理执行代表性任务,记录它失败的地方,然后编写修复这些故障的最简指令。故障本身就是需求规格说明,因此,如果你无法追溯到具体故障的技能,通常就是没人需要的技能。

关于这种提炼过程的完整示例,你可以阅读 Ponytail 如何将一个重复出现的故障(代理重写的内容远超你的要求)转化为一项技能,在编写自己的技能前,你可以通读全文。

技能的构成

技能是一个包含一个必需文件的目录。

.claude/skills/nginx-config-changes/
├── SKILL.md
├── reference/
│   └── proxy-headers.md
└── scripts/
    └── check-and-reload.sh

SKILL.md 以一个元数据块开头,即在 --- 标记之间编写的少量 YAML 设置(与 Docker Compose 文件使用的配置格式相同),随后是 Markdown 格式的指令。以下是上述失败案例的完整技能内容。

---
name: nginx-config-changes
description: Tests and reloads nginx safely after a config edit. Use when editing files under /etc/nginx, adding a server block or a reverse proxy, or changing a TLS certificate path.
---

## Rules

Run `sudo nginx -t` after every edit under `/etc/nginx`. Do not touch the service until it prints `test is successful`.

Apply the change with `sudo systemctl reload nginx`. Never use `restart`. A reload keeps the running workers serving traffic until the new config parses, so a broken config leaves the site up. A restart stops nginx first, so a broken config takes the site down.

If `nginx -t` fails, fix the file and test again. Never reload a config that failed the test.

For the proxy header defaults this project expects, see [reference/proxy-headers.md](reference/proxy-headers.md).

该文件不到 20 行,即构成了一个完整的技能。各部分说明如下:

  • name:最多 64 个字符,仅限小写字母、数字和连字符,且不能包含 claudeanthropic。在个人或项目技能中,这仅作为显示标签。你输入的命令源自目录名称,因此该技能响应 /nginx-config-changes
  • description:技能的功能及使用时机,最多 1,024 个字符。此行执行实际工作,下一节将专门讨论此内容。
  • 正文:指令内容,仅在技能实际触发时加载。
  • reference/:代理按需读取的额外文件。请从 SKILL.md 链接它们,并将链接保持在单层深度,因为从另一个被引用的文件再引用的文件,往往只能被部分读取。
  • scripts/:代理执行而非读取的文件。仅其输出会占用上下文,因此 300 行的脚本成本很低。

目录的存放位置决定了谁可以使用该技能。

  • .claude/skills/<name>/SKILL.md 位于仓库中:仅限当前项目,且会随仓库克隆同步给所有人。
  • ~/.claude/skills/<name>/SKILL.md:本机上的所有项目,且仅限本机。
  • <plugin>/skills/<name>/SKILL.md:随插件发布,在启用该插件的任何地方均可用。

使用 mkdir -p .claude/skills/nginx-config-changes 创建一个技能并编写文件。Claude Code 会监控这些目录,因此编辑现有技能会在运行中的会话内立即生效。如果创建了会话开始时不存在的顶级技能目录,则需要重启会话,因为会话开始时没有可监控的对象。

description 字段是文件中杠杆作用最强的一行

代理在启动时会将每个可用技能的 namedescription 加载到上下文中。它不会加载具体内容。当您的请求到达时,这一行文字是决定该技能是否相关的唯一依据,因此如果描述模糊,即使背后的具体内容再完美,也永远不会被读取。

请使用第三人称编写描述。“安全地测试并重载 nginx”是合适的。不要使用“我可以帮你处理 nginx”,因为这段文本会被注入到系统提示词中,第一人称会被模型理解为它在谈论自身。

描述中应包含两点:技能的功能以及适用的条件。请将重要的用例放在前面,因为 Claude Code 会在 1,536 个字符处截断列表条目。此外还有一个可选的 when_to_use 字段用于添加额外的触发短语和示例请求,它会附加在描述之后,同样受该字符限制。

请使用您实际会输入的词汇。description: Helps with nginx 无法匹配任何内容,因为没人会输入“helps with”。上述版本中提到了 /etc/nginxserver blockreverse proxyTLS (transport layer security) certificate path,这涵盖了任何应当触发该技能的请求中常用的词汇。

以下是描述的测试方法。将这一行文字交给从未看过具体内容的人,同时提供您准备输入的请求,询问他们该技能是否适用。如果他们无法判断,模型也无法判断。

保持主体精简,以维持上下文有效性

当调用某项技能时,其渲染内容将作为一条消息进入对话,并在会话剩余期间一直存在。Claude Code 不会在后续轮次中重新读取该文件。你编写的每一行代码都是整个会话的成本,而非单次回答的成本。

Anthropic 建议将 SKILL.md 控制在 500 行以内,并将细节移至独立文件中。压缩机制说明了该数字并非随意设定。当对话为释放上下文而进行总结时,Claude Code 会重新附加每项技能的最近一次调用,仅保留每项技能的前 5,000 个 token,并从最近调用的技能开始填充总计 25,000 个 token 的预算。过长的技能会在中途被截断。多项长技能会相互挤占,导致部分内容被完全移除。

因此,只需编写模型尚不了解的内容。它已经知道什么是 nginx 以及反向代理的作用。它不知道你关于 reload 优于 restart 的内部规则,而该规则正是此文件存在的唯一理由。

如果技能指示代理运行捆绑脚本,请使用 ${CLAUDE_SKILL_DIR} 命名路径,以便它能在技能安装的任何位置解析,并预先批准该命令,以免运行过程因权限提示而中断。

---
name: nginx-config-changes
description: Tests and reloads nginx safely after a config edit. Use when editing files under /etc/nginx, adding a server block or a reverse proxy, or changing a TLS certificate path.
allowed-tools: Bash(${CLAUDE_SKILL_DIR}/scripts/check-and-reload.sh *)
---

授权仅涵盖调用该技能的当前轮次,并在你发送下一条消息时清除,因此它不会静默转变为永久权限。

如何验证技能是否触发

观察技能加载情况可以确认 Agent 已找到该技能,但这并不代表回答内容已发生变化。请同时检查这两点,并务必在全新的会话中进行测试,因为编写技能时的会话保留了你输入的所有上下文,这些残留信息会掩盖文件中的逻辑漏洞。

  1. 在项目中启动一个带有 claude 的新会话。
  2. 用你日常工作中的习惯用语输入请求,不要提及技能名称。
  3. 观察是否触发了调用。如果技能未触发,请修改描述信息。此时问题尚未出现在主体内容中。
  4. 使用 /nginx-config-changes 手动调用作为对照。如果手动调用时行为正确,而通过请求触发时行为错误,则说明是触发器问题,而非指令问题。
  5. 在关闭技能的情况下运行相同的请求,并对比两次回答。在 /skills 菜单中,选中该技能,按 Space 将其状态切换为 off,然后按 Enter 保存。这会在 .claude/settings.local.json 中写入一条 skillOverrides 条目;完成后再次按 Space 可将其切换回 on
  6. 编写几个不应触发该技能的请求,并确认技能在这些情况下保持静默。

若要自动化该流程,请从官方市场安装 skill-creator 插件。

/plugin marketplace add anthropics/claude-plugins-official
/plugin install skill-creator@claude-plugins-official

如果安装输出显示 Run /reload-plugins to activate.,请运行该命令。然后要求 Claude 按名称评估你的技能。该插件将测试用例存储在技能目录下的 evals/evals.json 中,并在各自的子 Agent 中运行每个用例,因此每次运行都从干净的上下文开始。随后它会生成一份“启用技能”与“禁用技能”的对比报告,这才是真实的数据:即衡量技能在消耗 Token 和时间成本后带来的通过率提升。

故障模式:技能未触发

您输入请求后,代理执行了旧的错误操作,且未显示任何技能行。请按以下顺序排查:

  • 描述中说明了技能的功能,但未说明使用场景,导致您的请求无法匹配。
  • 描述中缺少您输入的关键词。如果您输入 "nginx",描述中必须包含 nginx。
  • 前置元数据中设置了 disable-model-invocation: true。这会将描述完全排除在模型的上下文之外,导致该技能只能通过 /name 由您手动调用。
  • 前置元数据中的 paths 通配符限制了激活范围,而您当前操作的文件不符合匹配规则。
  • 技能位于您起始目录下的嵌套 .claude/skills/ 目录中。此类技能仅在代理读取或编辑该子目录内的文件后才会加载,在此之前该技能不可用。

故障模式:技能频繁触发

相反的问题是描述过于宽泛,导致技能在无关的工作中被触发。“在服务器上工作时使用”几乎匹配服务器仓库中的任何请求。随后,该技能会加载到无法处理的任务中,并在会话的剩余时间内保持在上下文中。

请将描述缩小到实际相关的条件,并指明其涵盖的文件或命令。当技能仅适用于特定文件时,请添加 paths glob。对于任何具有副作用的操作(如部署或提交),请设置 disable-model-invocation: true 并通过 /name 自行调用,以确保代理永远不会自行决定现在是部署的时机。

故障模式:技能应归入规则文件

CLAUDE.mdAGENTS.md 这样的规则文件会在每次会话开始时加载,并应用于所有任务。技能主体仅在触发该技能时才会加载。决策的核心在于频率。适用于存储库中所有任务的事实(例如所使用的包管理器)应放入规则文件。仅适用于少量任务的过程(例如上述 nginx 规则)应放入技能中,这样在无人编辑 nginx 的日子里,它不会产生任何开销。

真正的故障在于将同一指令放入两处。两份副本会逐渐产生差异,当代理执行错误操作时,你无法判断它遵循的是哪一份副本。为每条指令选择一个归属地。技能、MCP 服务器与规则文件之间的界限 一文详细探讨了更复杂的情况,包括当正确方案是使用 MCP (Model Context Protocol) 服务器为代理提供新工具,而非提供新指令时的处理方式。

在验证有效后进行共享

一项能在实际工作中持续使用一周的技能才值得固化。.claude/skills/ 中的项目技能会像代码一样经过评审,并随仓库一同分发,因此克隆仓库的团队成员无需额外配置即可直接使用你的修正方案。若需在不同仓库间迁移技能且避免复制粘贴,请参考 如何跨仓库共享 Agent 技能

关于可移植性的一点说明:Claude Code 支持多种 frontmatter 字段,但 Agent Skills 标准仅允许使用六个:namedescriptionlicensecompatibilitymetadataallowed-tools。如果将包含其他 frontmatter 字段的技能上传至 claude.ai 或打包用于 Skills API,系统会直接报错,而不会忽略多余字段:

Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name

仅使用这六个字段,即可确保同一文件在 Claude Code 及其他支持该标准的工具中正常加载。编写指令时需考虑其在不同模型间的通用性,具体方法请参考 编写适用于任何模型的技能

FAQ

SKILL.md 文件应该有多长?

请保持在 500 行以内,大多数实用的技能文件远比这短。技能一旦被调用,其正文内容就会进入对话上下文并持续存在,因此每一行代码都是持续的成本,而非一次性开销。请将长篇参考资料移至技能目录下的独立文件中,并通过 SKILL.md 进行链接(仅限一级深度),这样代理仅在需要时才会读取它们。捆绑的脚本会被执行而非读取,因此它们仅消耗输出内容的成本。

为什么我的技能从未触发?

通常是因为描述不当,因为这是模型在决策时唯一能参考的技能上下文。请确保描述中说明了何时使用该技能,而不仅仅是它能做什么,并确保其中包含你在请求中实际输入的关键词。如果描述无误,请检查元数据中的 disable-model-invocation: true,它会使技能对模型完全不可见;同时检查 paths 的匹配模式,它可能将技能限制在非当前操作的文件中。如果技能位于起始目录下的嵌套 .claude/skills/ 目录中,它也可能无法触发:只有当代理读取或编辑了该子目录下的文件后,技能才会加载。

应该将其写成技能还是规则文件中的一行?

请评估该任务的适用范围。规则文件会在每次会话中加载,因此应包含适用于所有任务的事实,例如包管理器或分支命名规范。技能仅在触发时加载,因此适合存放仅在少数任务中用到的流程。切勿在两处编写相同的指令,因为两份副本会产生偏差,导致你无法判断代理遵循的是哪一个。

如何确认技能确实起到了作用?

通过基准测试进行对比。收集几个真实的请求,在开启该技能的新会话中运行每一个请求,然后通过 /skills 菜单关闭该技能再次运行,并对比两者的回答。使用新会话至关重要,因为编写技能时的对话中包含你的解释,这会掩盖文件本身的不完整性。skill-creator 插件可以为你执行此对比,并在 Token 成本旁报告通过率。

我可以在不同的代理中使用同一个 SKILL.md 吗?

可以,只要你遵循 Agent Skills 标准定义的字段:namedescriptionlicensecompatibilitymetadataallowed-tools。Claude Code 支持更多字段,并支持其他工具无法运行的正文特性(如 Shell 命令注入)。上传包含标准外字段的技能会触发明确的错误提示,列出允许的属性,因此请尽早决定该技能是仅用于 Claude Code 还是需要跨平台使用。