skill 脚本

2420 字
12 分钟
skill 脚本

如何在你的技能中运行命令并捆绑可执行脚本。

技能可以指示代理运行 shell 命令,并将可复用脚本捆绑在 scripts/ 目录中。本指南涵盖一次性命令、自带依赖的自包含脚本,以及如何为代理使用设计脚本接口。

1. 一次性命令#

当现有包已经能满足你的需求时,你可以直接在 SKILL.md 指令中引用它,而无需 scripts/ 目录。许多生态系统提供了在运行时自动解析依赖的工具,下面仅列举常用的三种,更多类别参见官网

  1. uvx 在隔离环境中运行 Python 包,并具有激进的缓存。它随 uv 一起提供。
Terminal window
uvx ruff@0.8.0 check .
uvx black@24.10.0 .
  • 不捆绑在 Python 中——需要单独安装。
  • 速度快。积极缓存,重复运行几乎即时。
  1. pipx 在隔离环境中运行 Python 包。可通过操作系统包管理器获取(apt install pipxbrew install pipx)。
Terminal window
pipx run 'black==24.10.0' .
pipx run 'ruff==0.8.0' check .
  • 不捆绑在 Python 中——需要单独安装。
  • uvx 的成熟替代品。虽然 uvx 已成为标准推荐,但 pipx 仍是一个可靠的选择,在操作系统包管理器中的可用性更广。
  1. npx 运行 npm 包,按需下载。它随 npm(随 Node.js 提供)一起提供。
Terminal window
npx eslint@9 --fix .
npx create-vite@6 my-app
  • 捆绑在 Node.js 中——无需额外安装。
  • 下载包、运行它,并缓存以供将来使用。
  • 使用 npx package@version 锁定版本以确保可重现性。

在技能中使用一次性命令的技巧:

  • 锁定版本(例如 npx eslint@9.0.0),使命令在不同时间行为一致。
  • SKILL.md 中说明前提条件(例如“需要 Node.js 18+”),而不是假定代理环境已具备。对于运行时级别的要求,使用 compatibility 前置元数据字段。
  • 将复杂命令移至脚本中。 当你调用带有几个标志的工具时,一次性命令效果很好。当命令变得复杂到难以在第一次尝试时正确执行时,scripts/ 中经过测试的脚本更加可靠。

2. 从 SKILL.md 引用脚本#

使用从技能目录根目录开始的相对路径来引用捆绑文件。代理会自动解析这些路径——无需绝对路径。

SKILL.md 中列出可用脚本,以便代理知道它们的存在:

## 可用脚本
- **`scripts/validate.sh`** —— 验证配置文件
- **`scripts/process.py`** —— 处理输入数据

然后指示代理运行它们:

## 工作流
1. 运行验证脚本:
```bash
bash scripts/validate.sh "$INPUT_FILE"
```
2. 处理结果:
```bash
python3 scripts/process.py --input results.json
```
Info

相同的相对路径约定也适用于 references/*.md 等支持文件——脚本执行路径(在代码块中)是相对于技能目录根目录的,因为代理从那里运行命令。

3. 自包含脚本#

当你需要可复用逻辑时,在 scripts/ 中捆绑一个脚本,并在脚本内联声明其依赖项。代理可以用单个命令运行该脚本——无需单独的清单文件或安装步骤。

多种语言支持内联依赖声明,以 Python 为例:

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(推荐)运行:

Terminal window
uv run scripts/extract.py

uv run 会创建一个隔离环境,安装声明的依赖项,并运行脚本。pipxpipx run scripts/extract.py)也支持 PEP 723。

  • 使用 PEP 508 说明符锁定版本:"beautifulsoup4>=4.12,<5"
  • 使用 requires-python 约束 Python 版本。
  • 使用 uv lock --script 创建锁文件以实现完全可重现性。

4. 为代理使用设计脚本#

当代理运行你的脚本时,它通过读取 stdout 和 stderr 来决定下一步做什么。一些设计选择能让脚本对代理来说更易于使用。

4.1 避免交互式提示#

这是代理执行环境的硬性要求。代理在非交互式 shell 中操作——它们无法响应 TTY 提示、密码对话框或确认菜单。阻塞在交互式输入上的脚本会无限挂起。

通过命令行标志、环境变量或 stdin 接受所有输入:

# 错误:挂起等待输入
$ python scripts/deploy.py
目标环境:_
# 正确:清晰的错误并附带指导
$ python scripts/deploy.py
错误:--env 是必需的。选项:development, staging, production。
用法:python scripts/deploy.py --env staging --tag v1.2.3

4.2 使用 --help 文档化用法#

--help 输出是代理学习脚本接口的主要方式。包含简要描述、可用标志和使用示例:

Usage: scripts/process.py [OPTIONS] INPUT_FILE
处理输入数据并生成摘要报告。
Options:
--format FORMAT 输出格式:json, csv, table(默认:json)
--output FILE 将输出写入 FILE 而不是 stdout
--verbose 将进度信息打印到 stderr
Examples:
scripts/process.py data.csv
scripts/process.py --format csv --output report.csv data.csv

保持简洁——输出会进入代理的上下文窗口,与它正在处理的其他内容一起。

4.2.1 --help 文档化用法与 Docstring 的不同#

问得很好!这个问题触及了 agent skill 设计的核心思维。虽然两者看起来都是”给脚本写说明”,但服务对象和使用方式完全不同:

核心区别:运行时 vs 源码级

--help 文档化用法Docstring("""……"""
获取方式运行命令:python script.py --help读取源码文件内容
面向谁使用者(人类终端用户 + AI agent)开发者(读代码的人)
要什么能力只需要能执行命令需要能读文件
内容聚焦接口契约:参数、选项、示例实现说明:算法逻辑、设计意图

我们可以用一个 agent skill 场景来理解:

假设你的 vault 里有一个数据处理脚本,AI agent 需要调用它。两种方式的过程对比:

方式一:Agent 读 Docstring(读源码)

1. Agent 找到脚本文件路径
2. Agent 用 read 工具读取整个文件
3. Agent 在一堆 import、函数定义中翻找 """...""" 注释
4. Agent 自己推断怎么调用

→ 消耗大量上下文窗口,还要”猜”用法。

方式二:Agent 读 —help(运行时查询)

1. Agent 直接运行: python script.py --help
2. 得到清晰简洁的:
Usage: script.py [OPTIONS] INPUT
Options:
--format json|csv
Examples:
script.py data.csv

→ 零猜测,所见即所得,上下文窗口高效。

一个直观的类比是:

Docstring 像产品说明书印在机器外壳背面——你得走到机器跟前仔细看。

--help 像按一下机器上的”帮助”按钮——机器直接告诉你它能做什么。

对于 agent 来说,按按钮的成本远低于绕到背后读说明书。Agent 的默认行为是执行命令获取帮助,而不是打开源文件阅读。这就是为什么 skill 设计规范强调 --help 作为”主要接口发现机制”。


那 Docstring 扮演的角色

"""数据预处理模块 —— 负责清洗和标准化原始传感器数据。 ← Docstring:给读代码的人看
实现基于滑动窗口的异常值检测算法(详见 RFC-42)。
"""
import argparse
def main():
parser = argparse.ArgumentParser(
description="处理输入数据并生成摘要报告。 ← --help:给使用者看
)

简单原则:Docstring 写”为什么这么设计”,--help 写”怎么用”。Agent 只关心后者。

4.3 编写有用的错误消息#

当代理收到错误时,消息内容直接影响其下一步尝试。含糊的 “错误:无效输入” 会浪费一次尝试。相反,说明哪里出错、期望什么以及可以尝试什么:

错误:--format 必须是以下之一:json, csv, table。
收到:"xml"

4.4 使用结构化输出#

优先使用结构化格式——JSON、CSV、TSV——而非自由格式文本。结构化格式可被代理和标准工具(jqcutawk)消费,使你的脚本在管道中具有可组合性。

# 空格对齐——难以程序化解析
NAME STATUS CREATED
my-service running 2025-01-15
# 带分隔符——明确的字段边界
{"name": "my-service", "status": "running", "created": "2025-01-15"}

将数据与诊断信息分离: 将结构化数据发送到 stdout,将进度消息、警告和其他诊断信息发送到 stderr。这让代理既能捕获干净、可解析的输出,又能在需要时访问诊断信息。

4.5 其他考虑#

  • 幂等性。 代理可能会重试命令。“如果不存在则创建”比“创建并在重复时失败”更安全。
  • 输入约束。 以清晰的错误拒绝模糊输入,而不是猜测。尽可能使用枚举和封闭集合。
  • 试运行支持。 对于破坏性或有状态的操作,--dry-run 标志让代理预览将要发生的事情。
  • 有意义的退出码。 为不同的失败类型(未找到、无效参数、身份验证失败)使用不同的退出码,并在 --help 输出中记录它们,以便代理了解每个代码的含义。
  • 安全的默认值。 考虑破坏性操作是否需要明确的确认标志(--confirm--force)或其他适合风险级别的防护措施。
  • 可预测的输出大小。 许多代理框架会自动截断超过阈值(例如 10-30K 字符)的工具输出,可能丢失关键信息。如果脚本可能产生大量输出,默认使用摘要或合理限制,并支持如 --offset 之类的标志,以便代理在需要时可以请求更多信息。或者,如果输出较大且不适合分页,要求代理传递 --output 标志,指定输出文件或 - 以明确选择 stdout。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

skill 脚本
https://agentskills.io/
作者
HAC
发布于
2026-07-16
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
HAC
观之非易,行且克难
Greetings
欢迎来到我的博客!这里主要分享我的学习笔记与兴趣爱好。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
32
分类
5
标签
13
总字数
79,889
运行时长
0
最后活动
0 天前

文章目录