在 skills 中使用脚本
Skills 可以指示 Agent 运行 shell 命令,并在 scripts/ 目录中打包可重用的脚本。本指南涵盖了一次性命令、带有自身依赖的独立脚本,以及如何为 Agent 的使用设计脚本接口。
一次性命令
当现有的包已经能满足你的需求时,你可以直接在 SKILL.md 指令中引用它,而无需使用 scripts/ 目录。许多生态系统都提供了在运行时自动解析依赖的工具。
- uvx
- pipx
- npx
- bunx
- deno run
- go run
uvx 在具有激进缓存的隔离环境中运行 Python 包。它随 uv 一起提供。
uvx ruff@0.8.0 check .
uvx black@24.10.0 .
- 未与 Python 捆绑 — 需要单独安装。
- 速度快。缓存激进,因此重复运行几乎是瞬间完成的。
pipx 在隔离环境中运行 Python 包。可通过操作系统包管理器获取(apt install pipx,brew install pipx)。
pipx run 'black==24.10.0' .
pipx run 'ruff==0.8.0' check .
- 未与 Python 捆绑 — 需要单独安装。
uvx的成熟替代方案。虽然uvx已成为标准推荐,但pipx仍然是一个可靠的选项,并且在操作系统包管理器中具有更广泛的可用性。
npx 运行 npm 包,按需下载。它随 npm 一起提供(npm 随 Node.js 一起提供)。
npx eslint@9 --fix .
npx create-vite@6 my-app
- 与 Node.js 捆绑 — 无需额外安装。
- 下载包,运行它,并将其缓存以供将来使用。
- 使用
npx package@version固定版本以确保可重复性。
bunx 是 Bun 中等同于 npx 的工具。它随 Bun 一起提供。
bunx eslint@9 --fix .
bunx create-vite@6 my-app
- 在基于 Bun 的环境中作为
npx的直接替代品。 - 仅当用户环境中使用的是 Bun 而不是 Node.js 时才适用。
deno run 直接从 URL 或说明符运行脚本。它随 Deno 一起提供。
deno run npm:create-vite@6 my-app
deno run --allow-read npm:eslint@9 -- --fix .
- 访问文件系统/网络需要权限标志(如
--allow-read等)。 - 使用
--将 Deno 标志与工具自身的标志分开。
go run 直接编译并运行 Go 包。它内置于 go 命令中。
go run golang.org/x/tools/cmd/goimports@v0.28.0 .
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 run
- 内置于 Go 中 — 无需额外工具。
- 固定版本或使用
@latest使命令更加明确。
在 skills 中使用一次性命令的提示:
- 固定版本(例如
npx eslint@9.0.0),以便命令在不同时间表现一致。 - 在你的
SKILL.md中声明前置条件(例如,“需要 Node.js 18+”),而不是假设 Agent 的环境已经具备这些条件。对于运行时级别的要求,请使用compatibilityfrontmatter 字段。 - 将复杂的命令移至脚本中。 当你调用带有少量标志的工具时,一次性命令非常有效。当命令变得足够复杂,以至于很难一次性写对时,在
scripts/中编写经过测试的脚本会更可靠。
从 SKILL.md 引用脚本
使用相对于 skill 目录根目录的相对路径来引用打包的文件。Agent 会自动解析这些路径 — 不需要绝对路径。
在你的 SKILL.md 中列出可用的脚本,以便 Agent 知道它们的存在:
## Available scripts
- **`scripts/validate.sh`** — Validates configuration files
- **`scripts/process.py`** — Processes input data
然后指示 Agent 运行它们:
## Workflow
1. Run the validation script:
```bash
bash scripts/validate.sh "$INPUT_FILE"
```
2. Process the results:
```bash
python3 scripts/process.py --input results.json
```
相同的相对路径约定也适用于支持文件,如 references/*.md — 脚本执行路径(在代码块中)是相对于 skill 目录根目录的,因为 Agent 是从那里运行命令的。
独立脚本
当你需要可重用的逻辑时,可以在 scripts/ 中打包一个内联声明其自身依赖的脚本。Agent 可以通过单个命令运行该脚本 — 不需要单独的清单文件或安装步骤。
有几种语言支持内联依赖声明:
- Python
- Deno
- Bun
- Ruby
PEP 723 定义了内联脚本元数据的标准格式。在 # /// 标记内的 TOML 块中声明依赖:
# /// script
# dependencies = [
# "beautifulsoup4",
# ]
# ///
from bs4 import BeautifulSoup
html = '<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>'
print(BeautifulSoup(html, "html.parser").select_one("p.info").get_text())
使用 uv 运行(推荐):
uv run scripts/extract.py
uv run 会创建一个隔离环境,安装声明的依赖,并运行脚本。pipx(pipx run scripts/extract.py)也支持 PEP 723。
- 使用 PEP 508 说明符固定版本:
"beautifulsoup4>=4.12,<5"。 - 使用
requires-python限制 Python 版本。 - 使用
uv lock --script创建锁文件以实现完全的可重复性。
Deno 的 npm: 和 jsr: 导入说明符默认使每个脚本都是独立的:
#!/usr/bin/env -S deno run
import * as cheerio from "npm:cheerio@1.0.0";
const html = `<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>`;
const $ = cheerio.load(html);
console.log($("p.info").text());
deno run scripts/extract.ts
- 对 npm 包使用
npm:,对 Deno 原生包使用jsr:。 - 版本说明符遵循 semver 规范:
@1.0.0(精确匹配),@^1.0.0(兼容匹配)。 - 依赖会被全局缓存。使用
--reload强制重新获取。 - 带有原生插件(node-gyp)的包可能无法工作 — 提供预构建二进制文件的包效果最好。
当未找到 node_modules 目录时,Bun 会在运行时自动安装缺失的包。直接在导入路径中固定版本:
#!/usr/bin/env bun
import * as cheerio from "cheerio@1.0.0";
const html = `<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>`;
const $ = cheerio.load(html);
console.log($("p.info").text());
bun run scripts/extract.ts
- 不需要
package.json或node_modules。原生支持 TypeScript。 - 包会被全局缓存。首次运行会下载;后续运行几乎是瞬间完成的。
- 如果目录树的任何上级目录中存在
node_modules目录,则自动安装将被禁用,Bun 会回退到标准的 Node.js 解析机制。
自 2.6 版本起,Bundler 随 Ruby 一起提供。使用 bundler/inline 直接在脚本中声明 gem:
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'nokogiri'
end
html = '<html><body><h1>Welcome</h1><p class="info">This is a test.</p></body></html>'
doc = Nokogiri::HTML(html)
puts doc.at_css('p.info').text
ruby scripts/extract.rb
- 显式固定版本(
gem 'nokogiri', '~> 1.16') — 这里没有锁文件。 - 工作目录中现有的
Gemfile或BUNDLE_GEMFILE环境变量可能会产生干扰。
为 Agent 的使用设计脚本
当 Agent 运行你的脚本时,它会读取 stdout 和 stderr 来决定下一步做什么。一些设计选择可以使脚本对 Agent 来说更容易使用。
避免交互式提示
这是 Agent 执行环境的硬性要求。Agent 在非交互式 shell 中运行 — 它们无法响应 TTY 提示、密码对话框或确认菜单。阻塞在交互式输入上的脚本将无限期挂起。
通过命令行标志、环境变量或 stdin 接收所有输入:
# 错误做法:挂起并等待输入
$ python scripts/deploy.py
Target environment: _
# 正确做法:提供带有指导的清晰错误信息
$ python scripts/deploy.py
Error: --env is required. Options: development, staging, production.
Usage: python scripts/deploy.py --env staging --tag v1.2.3
使用 --help 记录用法
--help 输出是 Agent 了解脚本接口的主要方式。请包含简短描述、可用标志和用法示例:
Usage: scripts/process.py [OPTIONS] INPUT_FILE
Process input data and produce a summary report.
Options:
--format FORMAT Output format: json, csv, table (default: json)
--output FILE Write output to FILE instead of stdout
--verbose Print progress to stderr
Examples:
scripts/process.py data.csv
scripts/process.py --format csv --output report.csv data.csv
保持简洁 — 该输出会与 Agent 正在处理的其他所有内容一起进入其上下文窗口。
编写有帮助的错误信息
当 Agent 遇到错误时,错误信息会直接影响它的下一次尝试。晦涩难懂的“Error: invalid input”会浪费一次交互回合。相反,应该说明出了什么问题、期望是什么以及可以尝试什么:
Error: --format must be one of: json, csv, table.
Received: "xml"
使用结构化输出
优先使用结构化格式(JSON、CSV、TSV),而不是自由格式的文本。结构化格式既可以被 Agent 使用,也可以被标准工具(jq、cut、awk)使用,从而使你的脚本可以在管道中组合使用。
# 空格对齐 — 难以通过编程方式解析
NAME STATUS CREATED
my-service running 2025-01-15
# 分隔符格式 — 字段边界明确
{"name": "my-service", "status": "running", "created": "2025-01-15"}
将数据与诊断信息分离: 将结构化数据发送到 stdout,将进度消息、警告和其他诊断信息发送到 stderr。这使得 Agent 能够捕获干净、可解析的输出,同时在需要时仍能访问诊断信息。
进一步的注意事项
- 幂等性。 Agent 可能会重试命令。“如果不存在则创建”比“创建并在重复时失败”更安全。
- 输入约束。 用清晰的错误拒绝模棱两可的输入,而不是进行猜测。尽可能使用枚举和封闭集合。
- 支持试运行(Dry-run)。 对于破坏性或有状态的操作,
--dry-run标志可以让 Agent 预览将要发生的事情。 - 有意义的退出码。 为不同的失败类型(未找到、无效参数、身份验证失败)使用不同的退出码,并在
--help输出中记录它们,以便 Agent 知道每个代码的含义。 - 安全的默认值。 考虑破坏性操作是否应需要显式的确认标志(
--confirm、--force)或其他适合该风险级别的安全措施。 - 可预测的输出大小。 许多 Agent 框架会自动截断超过阈值(例如 10-30K 字符)的工具输出,这可能会丢失关键信息。如果你的脚本可能产生大量输出,请默认输出摘要或设置合理的限制,并支持像
--offset这样的标志,以便 Agent 在需要时请求更多信息。或者,如果输出很大且不适合分页,请要求 Agent 传递--output标志来指定输出文件,或使用-显式选择输出到 stdout。