Agent Skills 使用指南

Agent Skills 使用指南

用一个 Markdown 文件,就能给 AI Agent 注入领域能力。本指南讲清 Skill 是怎么回事、怎么用,以及哪些坑必须避开。

3
技能来源
12
可配置字段
2
调用方式
0
编程门槛

💡 Skill 是怎么回事

一句话:Skill(技能)= 一份带 YAML 元数据的 Markdown 文档(外加可选的 Shell 命令),用来把"某个领域怎么做事"的知识与流程,按需注入到 Agent 的上下文里。

它解决什么问题?

大模型很聪明,但它不知道你的项目约定、不知道你团队的发布流程、也不知道某个内部工具怎么用。Skill 就是把这些"私有知识 + 标准操作流程(SOP)"打包成文件,让 Agent 在合适的时机自动读取、按章办事。

和"直接写进 Prompt"有什么区别?

  • 按需加载:不是把所有知识塞满上下文,而是模型判断需要时才调入对应 Skill,避免认知过载与 token 浪费。
  • 可复用、可分享:一个 Skill 就是一个文件夹,可以提交进仓库、跨项目复用、团队共享。
  • 零编程门槛:写 Markdown 就行,不需要写一行 TypeScript / Python。
  • 能带实时状态:Skill 里可以内嵌 Shell 命令,调用时先在你机器上执行,把真实的 git status、目录列表等"现场信息"一起喂给模型。
把 Skill 想象成给 Agent 的一张"操作卡片":卡片上写着"遇到这类任务时,按这些步骤、用这些工具、注意这些事项"。Agent 抽到对应卡片,就照着做。

🧩 一个 Skill 的构成

每个 Skill 的核心是一个名为 SKILL.md 的文件,由三部分组成:YAML 元数据头、Markdown 正文、可选的内嵌 Shell 命令。此外,文件夹里还能放独立的配套脚本scripts/*.sh 等),由模型在执行时按需调用——这与内嵌 Shell 不同,详见后文对比

SKILL.md --- YAML Frontmatter --- 元数据:name / description / when_to_use / allowed_tools / paths … 1 # Markdown 正文 给模型看的指令、步骤、注意事项 —— 调用时整段注入上下文 2 !`command` (可选内嵌 Shell) 调用前在宿主机执行,把实时输出回填进正文 3
图 1:SKILL.md 的三段式结构 —— 元数据头、Markdown 正文、可选内嵌 Shell

🌐 三种来源

同样一个 Skill,可能来自三种渠道。来源不同,可信度与能力边界也不同 —— 这是后面"安全注意事项"的基础。

本地文件 · 最常用

📂 File-based

放在本地 .claude/skills/ 目录下的 SKILL.md。由你或团队编写,可信度最高,允许执行内嵌 Shell

~/.claude/skills/项目/.claude/skills/
内建打包

📦 Bundled

随 Agent 程序一起打包分发的官方内建技能,开箱即用,无需安装。属于受信任来源。

bundledSkills
协议映射 · 远程

🔌 MCP

来自外部 MCP Server 的能力,被映射成 Skill。来源不可控,出于安全不会执行内嵌 Shell

loadedFrom: 'mcp'
📂 File-based 本地 .claude/skills/ 📦 Bundled 内建打包技能 🔌 MCP 远程协议映射 统一 Command 去重 · 缓存 · 挂入命令系统 三种来源归一 🤖 Agent 按需调用 虚线 = 不执行内嵌 Shell(防 RCE)
图 2:三种来源被归一成统一的 Command 对象供 Agent 调用;MCP 来源的 Shell 执行被切断

📁 目录结构

想新建一个技能,只需在约定目录下建一个文件夹,里面放一个 SKILL.md。Agent 启动时会自动扫描发现它。

📁 .claude/ 📁 skills/ 📁 git-helper/ 📄 SKILL.md ← 必需,技能主文件 📁 scripts/ ← 可选,配套脚本(模型按需调用) 📁 deploy-flow/ 📄 SKILL.md
图 3:技能目录结构 —— 每个技能一个文件夹,文件夹名即技能名,内含必需的 SKILL.md

三个可放置的位置(按优先级与作用域)

位置作用域典型用途
~/.claude/skills/用户级(全局)个人通用技能,所有项目共享
<项目>/.claude/skills/项目级随仓库提交,团队共享的项目专属技能
策略管理目录组织/策略级企业统一下发、不可随意修改的技能
Agent 会并行扫描所有位置,并按真实路径(inode)去重,软链接到同一文件不会重复加载;扫描结果会被缓存,同一工作目录只发现一次。

✍️ 编写 SKILL.md

下面是一个完整可用的示例:一个帮你检查 Git 状态并给出建议的技能。注意它如何用内嵌 Shell 携带真实现场信息。

.claude/skills/git-helper/SKILL.md
---
name: git-status-helper
description: 查看当前 Git 状态并给出提交建议
when_to_use: 当用户询问代码改动、是否该提交、或想了解仓库状态时
allowed_tools: [Bash, Read]
effort: low
---

# Git 状态助手

当前分支与最近提交:
!`git log --oneline -5`

未提交的变更:
!`git status --short`

请根据以上真实信息,帮我判断:
1. 这些改动是否应该拆成多个 commit?
2. 给出建议的 commit message。

逐段解读

  • YAML 头(--- 之间):声明元数据。name 是技能名,description 让模型知道"这个技能干什么",when_to_use 告诉模型"什么时候该用我"。
  • Markdown 正文:就是你想喂给模型的指令。可以写步骤、规则、模板、示例。
  • 内嵌 Shell !`...`:调用技能时,这两条 git 命令会先在你机器上执行,命令本身被替换成真实输出。于是模型拿到的是"此刻仓库的真实状态",而不是空泛的占位符。

📋 Frontmatter 字段速查

YAML 头里可用的全部字段。除 name 外大多可选,但 description / when_to_use 强烈建议写,它们决定模型能否在正确时机找到你的技能。

字段类型作用
namestring技能名,也是用户调用时的 /name
descriptionstring | string[]技能简介,帮助模型理解用途
when_to_usestring触发时机描述,模型据此自主选择是否调用
allowed_toolsstring[]限定该技能可用的工具集合(白名单)
modelstring指定运行该技能使用的模型
effortlow | medium | high任务估时/算力档位
user_invocablebooleanfalse = 仅供模型内部调用,不出现在 / 命令列表
pathsstring[]条件触发的 glob:匹配文件变更时自动激活
versionstring技能版本号
contextinline | fork注入当前上下文,还是派生子上下文执行
agentstring绑定到指定 agent 类型
shellbash | powershell内嵌 Shell 使用的解释器

内嵌 Shell:让 Prompt 携带实时状态

这是 Skill 最强大的特性。Markdown 里嵌入的命令在技能被调用前先执行,输出回填正文,于是模型拿到的永远是"现场"而非"快照"。

两种语法

内联式

inline
当前目录文件:!`ls -la`

代码块式

block
```!
git diff --stat
```
!`git status` 正文里的命令 🔐 权限校验 统一权限体系 💻 宿主机执行 获得真实输出 📝 回填正文 替换后喂给模型
图 4:内嵌 Shell 的执行链 —— 命令 → 权限校验 → 宿主机执行 → 输出回填进 Prompt
安全红线:命令在你的真机上真实执行。每条命令执行前都会走与普通工具相同的权限校验(可被 deny/ask 拦截);并且来自 MCP 的技能完全不执行内嵌 Shell,以防远程服务器注入恶意命令(RCE)。

📜 配套脚本 ≠ 内嵌 Shell

技能文件夹里除了 SKILL.md,还能放独立的脚本文件(.sh / .py / .js …)。它们和正文里的内嵌 Shell 是两种完全不同的东西——最容易混淆,这里专门讲清。

一句话区分

  • 内嵌 Shell !`...`:写在 SKILL.md 正文里,技能加载/调用前由框架自动执行,输出回填进 Prompt。目的是给模型喂"现场状态"。它一定会跑,模型管不着。
  • 配套脚本 scripts/xxx.sh:是文件夹里独立的可执行文件,模型在执行任务时按 SKILL.md 的指示、通过 Bash 工具主动调用。目的是封装可复用、确定性强的复杂逻辑。跑不跑、怎么传参,由模型决定。

逐维对比

维度内嵌 Shell !`...`配套脚本 scripts/*.sh
位置SKILL.md 正文内独立文件,与 SKILL.md 同目录
执行时机技能加载/调用前,自动执行任务执行中,模型主动调用
谁决定运行框架(一定会跑)模型(按正文指示判断)
目的把实时状态回填进 Prompt封装可复用、确定性的逻辑
如何引用直接写命令${CLAUDE_SKILL_DIR}/scripts/xx.sh 定位
能否传参固定命令,不便传参可像普通脚本一样传参、循环、复用
MCP 来源不执行(防 RCE)仍可由模型调用(走 Bash 工具权限)
① 加载 / 解析 Skill 展开变量、回填内嵌 Shell ② 注入 Prompt 含现场状态,喂给模型 ③ 模型执行任务 按指示调用工具/脚本 !`...` 内嵌 Shell 发生在 ①→② · 框架自动执行 · 一定会跑 scripts/*.sh 配套脚本 发生在 ③ · 模型经 Bash 工具主动调用 · 可不跑
图 5:两者执行时机完全不同 —— 内嵌 Shell 在"加载期"自动跑;配套脚本在"执行期"由模型按需调用

正确写法:让模型去调用脚本

注意下面正文里的命令没有 ! 前缀——它不是内嵌 Shell,而是写给模型看的指令。模型读到后会用 Bash 工具去执行这个脚本。

.claude/skills/release-helper/SKILL.md
---
name: release-helper
description: 发布前自动化检查
allowed_tools: [Bash, Read]
---

# 发布助手

发布前,请用 Bash 运行项目自带的预检脚本:

    bash ${CLAUDE_SKILL_DIR}/scripts/preflight.sh

根据脚本输出的检查结果,告诉我是否可以安全发布;
若有失败项,定位到对应文件并给出修复建议。
何时把逻辑放进脚本?当流程步骤多、需要传参、要复用、且结果确定时,把它写成脚本,让 SKILL.md 只负责"告诉模型何时跑、怎么看输出"。这样既稳定可测,又能让正文保持简洁。反之,只是想抓一条实时状态喂给模型,用内嵌 Shell 更直接。
别把脚本当内嵌 Shell。给脚本命令加上 ! 前缀(!`bash …`)会让它在加载期就被框架自动执行,脱离模型控制,也无法根据上下文传参——这通常不是你想要的。

🎯 两种调用方式

写好的技能有两种被触发的途径:你手动调用,或模型自主判断后调用。

显式 · 由你触发

⌨️ 用户调用 /name

在对话框输入斜杠命令,例如 /git-status-helper,直接调起对应技能。可携带参数,正文里的占位符会被展开。

隐式 · 模型决定

🤖 模型自主选择

模型读取所有技能的 descriptionwhen_to_use,在合适时机自动调用。这正是 when_to_use 要写清楚的原因。

调用时正文会被加工

无论哪种方式,技能被调用时,正文都会经过几步处理后才交给模型:

  • 展开参数占位符:把你传入的 CLI 参数填进正文。
  • 替换内置变量${CLAUDE_SKILL_DIR}(技能所在目录)、${CLAUDE_SESSION_ID}(当前会话 ID)会被替换成真实值,方便引用配套脚本。
  • 执行内嵌 Shell:把 !`...` 替换成真实输出(仅受信任来源)。

🔔 条件触发:paths 字段

声明了 paths 的技能叫"条件技能"。当用户操作或修改了匹配该 glob 的文件时,技能会自动激活并注入上下文 —— 一种精准的 Hook 订阅模式。

条件技能示例
---
name: migration-reminder
description: 改动数据库 schema 时提醒同步迁移脚本
paths: ["**/schema.prisma", "**/migrations/**"]
---

检测到你正在改动数据库 schema,请记得:
1. 运行迁移生成命令
2. 检查是否影响线上数据
✏️ 文件被改动 schema.prisma 🎯 glob 匹配 paths 命中 ⚡ 技能自动注入上下文 无需手动调用
图 6:条件技能通过 paths 订阅文件变更,命中时自动激活,避免无关技能常驻占用上下文

⚠️ 注意事项

Skill 强大,也意味着它能在你机器上执行命令、能左右模型行为。下面分安全、性能、易踩的陷阱三类,逐条避坑。

安全谨防代码执行与来源信任

① 内嵌 Shell = 真实 RCE 能力。任何 !`...` 都会在你机器上真实运行。不要直接安装来源不明的第三方 Skill,使用前务必通读其 SKILL.md,确认内嵌命令无害。
② MCP 来源默认不执行 Shell。这是一道关键防线:远程 MCP Server 提供的技能即使写了 !`...` 也不会执行,防止远程注入。不要试图绕过它。
③ 命令走统一权限体系。内嵌命令和普通工具一样受 deny/ask 权限约束。合理配置权限规则,对危险命令(删除、网络外发)保持 ask 或 deny。
最佳实践:allowed_tools 给技能配最小工具白名单,遵循最小权限原则。

性能别让技能拖慢或塞爆上下文

① 正文别写太长。技能正文会整段注入上下文,啰嗦的技能既费 token 又稀释注意力。保持精炼,把长流程拆成步骤要点。
② 内嵌命令要快且无副作用。命令在调用前同步执行,慢命令(大规模扫描、网络请求)会拖慢响应;只读、秒级返回最佳。
③ 善用 effortcontext重任务标 effort: high;需要隔离、不污染主上下文的,用 context: fork 派生子上下文执行。

陷阱容易忽略的隐藏行为

陷阱说明与对策
when_to_use 写得含糊模型据此判断是否调用。写不清就会"该用的时候不用、不该用的时候乱用"。要具体描述触发场景。
user_invocable: false 的隐身技能设为 false 后技能不出现在 / 列表,只能由模型内部调用。排查"命令找不到"时先看这个字段。
paths glob 过宽范围太大会导致技能频繁自动激活、造成认知过载。glob 要尽量精确。
同名技能与缓存发现结果会被缓存,且按真实路径去重。改了技能没生效时,检查是否命中缓存或被同名技能覆盖。
特殊字符替换污染内嵌命令输出若含 $& 等替换符号,需注意被正确转义(实现已用函数式替换规避)。

⚙️ 原理补充:加载全流程

想深入了解底层机制?下面是从"发现技能文件"到"把最终 Prompt 交给模型"的完整流水线。日常使用无需关心,感兴趣可展开。

① 发现 · getSkillDirCommands(cwd) 并行扫描全部技能目录 · memoize 缓存 · realpath 去重 ② 解析 · parseFrontmatter 读取 SKILL.md · 提取 name/paths/effort 等字段 ③ 实例化 · createSkillCommand 封装为统一 Command 对象 · 挂入命令系统 ④ 调用 · getPromptForCommand 展开参数 → 替换内置变量 → 执行内嵌 Shell ⑤ 注入 · 最终 Prompt 交给模型 含实时系统状态的完整提示词 ④ 中:loadedFrom === 'mcp' 时跳过内嵌 Shell 执行
图 6:Skill 从发现到注入的五阶段流水线
src/skills/loadSkillsDir.ts(精简)
export const getSkillDirCommands = memoize(async (cwd) => {
  // 并行加载所有来源:策略级 / 用户级 / 项目级 / --add-dir / 旧版命令
  const [managed, user, project, extra, legacy] = await Promise.all([...])
  // inode 级去重,防软链重复
  return deduplicateByRealpath([...managed, ...user, ...project, ...extra, ...legacy])
})

完整逐行剖析见配套文档 架构深度解析 · Skills 章节