乐于分享
好东西不私藏

「AI杂记」创建高效 Agent Skill:关于脚本

「AI杂记」创建高效 Agent Skill:关于脚本

0. 回顾skill中的脚本

skill 的脚本目录 scripts/ 可选,存放可执行脚本(Python/Bash等),用于确定性固定性任务的执行或结果的获取(Agent直接调用,脚本内容不会载入上下文中,0 token消耗)

When iterating on a skill, compare the agent’s execution traces across test cases. If you notice the agent independently reinventing the same logic each run — building charts, parsing a specific format, validating output — that’s a signal to write a tested script once and bundle it in scripts/.

1. 一次性命令

如果某个功能已经有成熟的命令行工具(比如代码格式化工具 ruffblackeslint),你可以直接在 SKILL.md 的指令中让 Agent 运行这些工具,无需把工具逻辑封装成 scripts/ 下的脚本。

When an existing package already does what you need, you can reference it directly in your SKILL.md instructions without a scripts/ directory. Many ecosystems provide tools that auto-resolve dependencies at runtime.

所使用的这些命令行工具的特点是:在运行时自动解析和安装依赖,无需提前配置环境。

这里“无需提前配置环境”的意思是:你不用手动执行 pip install xxx 或 npm install -g xxx 这类命令,就能直接运行那个工具。

这些命令会在背后自动处理依赖的下载、安装和临时隔离,运行完后,可能缓存下载版本的依赖包(这是之后再次运行相同指令时,提升运行速度),但用于执行该命令的临时运行环境(如进程、临时文件夹、虚拟环境)会在命令结束后被清理掉,防止污染系统PATH、留下无用的进程或环境变量。

例如,你有一个 src/index.js 文件,使用 ESLint 检查其中的代码是否符合规范,执行一次性命令npx eslint@9.0.0 src/index.js

这个过程发生以下操作:

  1. 1. npx 自动从 npm 仓库下载 eslint@9.0.0 及其依赖(首次运行时稍慢,之后使用缓存)。
  2. 2. 在临时隔离环境中执行 eslint src/index.js
  3. 3. 命令输出:
    • • 无问题:无输出(或显示“All good”风格信息),退出码 0。
    • • 有问题:输出具体错误/警告的行号及规则(例如“’fetch’ is not defined”、“Missing semicolon”等),退出码非 0。
  4. 4. 临时进程结束后,不会在系统 PATH 中残留 eslint 命令,但下载的包文件会缓存在 ~/.npm/_npx 中,供下次快速复用。

关于使用一次性命令的注意事项:

  1. 1. 锁定版本:如 npx eslint@9.0.0,避免版本变化导致行为不一致。
  2. 2. 声明前置条件:在 SKILL.md frontmatter的compatibility字段写明所需运行时环境要求(如“需要 Node.js 18+”),不要假设 Agent 环境已具备。
  3. 3. 复杂命令改写成脚本:如果命令参数太多、容易出错,就应该把逻辑封装到 scripts/ 里,用测试过的脚本更可靠。假设你需要 Agent 从一个 CSV 中提取特定列,过滤掉空值,按日期排序,再转成 Excel 格式,最后对数值列做汇总统计。如果用 Shell 管道 + awk + sort + xlsx 工具,一行命令会非常复杂:

    cat data.csv | awk -F',''NR>1 && $3!="" {print $1","$2","$3}' | sort -t',' -k2 | xlsx --input-format csv --output result.xlsx --sheet "summary" && python -c "import pandas as pd; df=pd.read_excel('result.xlsx'); print(df.describe())"

    这条命令:

  4.             • 参数多、依赖多个工具(awksortxlsxpython)。
    • 非常容易写错(比如遗漏引号、搞错分隔符、输错列索引)。
    • Agent 从自然语言“帮我处理这个 CSV”生成这样一行精确命令,成功率很低。 但如果你先把正确的逻辑写成一个 Python 脚本 scripts/process_data.py
    # scripts/process_data.pyimport pandas as pdimport sysinput_file = sys.argv[1]output_file = sys.argv[2]df = pd.read_csv(input_file)# 过滤空值、选择列、排序等df_clean = df.dropna(subset=['col3'])[['col1''col2''col3']].sort_values('col2')df_clean.to_excel(output_file, sheet_name='summary', index=False)print(df_clean.describe())

    然后在 SKILL.md 里,Agent 只需要执行:

    python scripts/process_data.py data.csv result.xlsx
    • • 命令极短,几乎不可能写错。
    • • 复杂的逻辑已经在脚本里测试好了,Agent 只需要正确调用。
    • • 即使以后需求调整,也是改脚本,不用重新教 Agent 如何构造复杂命令。

2. 自包含脚本

2.1 什么是自包含脚本?

首先我们需要介绍下什么是自包含脚本,自包含脚本(Self-contained scripts)是指:脚本文件和它所需的依赖库声明在同一个文件内,只需一条命令即可运行,无需外部配置文件(如 requirements.txt)或手动安装依赖。

这里的自包含体现在:

  • • 不依赖外部文件:所有信息(代码 + 依赖声明)都在一个文件中。
  • • 不依赖预先配置的环境:即使一个全新的、只有 Python 基础环境(安装了 uv)的机器,也能直接运行它。
  • • 可移植:把这个文件复制到任何地方,运行方式不变。

这里我们以使用 PEP 723 格式的python脚本为例

注:不是只有python脚本才有自包含的形式,其他类型的脚本也有自包含的形式,比如Deno、Bun 和 Ruby 实现自包含脚本的方式各不相同,但目标一致:让脚本更易于分享和运行。

# /// script# dependencies = [#   "requests",#   "rich"# ]# ///import requestsfrom rich importprintresponse = requests.get("https://api.github.com/repos/astral-sh/uv")print(f"GitHub 星标数: {response.json()['stargazers_count']}")

运行命令:

uv run script.py

uv run 会自动创建一个虚拟环境,安装 requests 和 rich(根据脚本中以 # /// script 开头,以 # /// 结尾中声明的依赖),然后执行脚本。运行结束后环境可被丢弃(但会缓存包以加速下一次)。

PEP 723是一项Python官方标准,其全称为“内联脚本元数据”(Inline script metadata)。它的目标是为单个Python脚本文件提供一个官方、统一的标准。通过在脚本内部以特定注释块的形式声明依赖项,它让工具能够无需外部文件即可自动配置环境来运行脚本。

可以将它理解为脚本环境的「自白书」,一次声明,随处可运行。这项标准已在2024年1月被正式采纳,解决了长久以来脚本共享与运行中的依赖管理难题。

2.2 在 skill 中使用自包含脚本

在 Agent Skill 中使用“自包含脚本”分为两步:

  1. 1. 在scripts/下,编写一个声明了自身依赖的脚本(如,上方2.1中PEP 723格式的python脚本)
  2. 2. 在 Skill 的指令中指引 Agent 去运行它(如,uv run scripts/xxx.py

在这里我们需要明确的是不是所有 scripts/ 下的脚本都需要做成“自包含依赖”的形式。是否需要,取决于脚本的复杂度和依赖情况:

  • • 如果脚本很简单,没有第三方依赖,或是依赖已被环境满足时无需使用自包含的形式。
  • • 当脚本需要第三方依赖时,优先使用自包含脚本,因为这与 “使用脚本” 的最佳实践一致——简化 Agent 的指令,减少环境假设。

自包含脚本让技能真正“带上自己的环境”,不再依赖 Agent 的预先配置,实现“一次编写,到处运行”。

在 Agent Skill 中使用“自包含脚本”(Self-contained Scripts,如 Python 的 PEP 723 格式),主要目的是解决以下两个核心问题:

  1. 1. 消除环境依赖的假设:技能可能运行在任意 Agent 环境中,你无法预知该环境是否已安装脚本所需的第三方库。自包含脚本将依赖声明直接写在脚本内,配合 uv run 等工具,能自动创建隔离环境并安装依赖,无需人工预装或额外指令。
  2. 2. 简化技能指令(降低 Agent 执行摩擦):如果没有自包含脚本,Agent 需要先执行 pip install -r requirements.txt(甚至要先创建虚拟环境),再运行脚本 —— 多步操作,容易出错。而自包含脚本允许你在 SKILL.md 中只写一条命令(如 uv run scripts/process.py),Agent 一条指令即可完成依赖安装 + 脚本执行全流程,极大提升了可靠性。

此外,它还能带来两个附加好处:

  • • 隔离性:不同技能的依赖互不冲突(例如技能 A 需要 pandas==1.5,技能 B 需要 pandas==2.0)。
  • • 可移植性:技能包复制到任何安装了 uv 的机器上,都能直接运行。

3. 设计Agent友好的脚本

3.1 避免涉及交互的提示词

Agent 运行的环境中,没有人类来输入密码、确认选项或回答“y/n”。如果脚本里写了会等待用户输入的命令(比如 input()read -pConfirm? (y/n)),Agent 就会卡死在那里,直到超时。

为什么 Agent 不能处理交互式提示?

  • • Agent 的执行环境通常是非交互式 shell(non-interactive),没有 TTY(终端)。
  • • 即使有 TTY,Agent 也没有“人类”来输入内容;它只能发送预设的命令行输入,无法响应动态提示。
  • • 因此,任何依赖 stdin 等待用户输入的脚本都会导致 Agent 进程永久阻塞

避免交互式提示 = 脚本的所有行为都由命令行参数、环境变量或输入文件决定,绝不依赖人类在终端中的实时输入。

3.2 提供关于脚本的--help 文档

本质上就是让agent快速准确地了解脚本是如何使用的,当然你也可以在SKILLmd中直接用自然语言去说明,只要能让agent理解这个脚本是做什么的就行。

Agent 无法像人一样“猜”出脚本的用法,也不能去读源码。--help 是 Agent 理解脚本接口的唯一可靠文档。详细、结构化的帮助信息能让 Agent 正确构造命令行参数,减少试错,提升任务成功率。

3.2.1 什么是--help

--help(有时也用 -h)是一个命令行程序的通用约定参数。当你在终端中运行一个程序时,加上 --help,程序不会执行其主要功能,而是输出一段帮助文本,告诉你:

  • • 这个程序是做什么的
  • • 它可以接受哪些参数(位置参数、可选参数、选项)
  • • 每个参数的含义和默认值
  • • 一些使用示例

--help 不是写在 SKILL.md 里的文字,而是写在脚本代码里、由脚本自身提供的动态帮助功能。

不是所有脚本都自动支持 --help--help 是一个约定,而不是系统级的内置功能。只有当脚本的作者显式编写了处理 --help(或 -h)参数的逻辑时,脚本才能响应它并输出帮助信息。

不同语言实现方式不同,但原理相同。例如 Python 用标准库 argparse,你在代码里定义了所有参数,argparse 会自动生成 --help 的输出格式和内容。你不需要手动写字符串。其他语言(bash、Node.js、Ruby)也有类似库。

我们这里以一个简易的”自我介绍“python脚本(self_introduction.py)为例:

(如果不会写脚本的–help也不用担心,你只需要用自然语言向ai描述清楚脚本的作用,入参,出参,使用方法,让ai为你生成即可)

#!/usr/bin/env python3"""一个简单的问候脚本,演示如何使用--help功能"""import argparsedefmain():# 创建参数解析器    parser = argparse.ArgumentParser(        description='这是一个简单的问候脚本',        epilog='使用示例: python demo_script.py --name Alice --age 25'    )# 添加参数    parser.add_argument('--name',type=str,        required=True,help='你的名字(必需)'    )    parser.add_argument('--age',type=int,        required=False,help='你的年龄(可选)'    )    parser.add_argument('--city',type=str,        default='未知城市',help='你所在的城市(默认值:未知城市)'    )    parser.add_argument('--greeting',type=str,        default='你好',help='问候语(默认值:你好)'    )# 解析命令行参数    args = parser.parse_args()# 构建问候信息    message = f"{args.greeting},我叫 {args.name}"if args.age:        message += f",今年 {args.age} 岁"    message += f",来自 {args.city}"print(message)if __name__ == '__main__':    main()

执行python3 self_introduction.py --help

usage: demo_script.py [-h] --name NAME [--age AGE] [--city CITY] [--greeting GREETING]                                                                                                              这是一个简单的问候脚本                                                                                               optional arguments:          -h, --help           show this help message and exit       --name NAME          你的名字(必需)                                                                                 --age AGE            你的年龄(可选)                                                                                 --city CITY          你所在的城市(默认值:未知城市)                                                                 --greeting GREETING  问候语(默认值:你好)                                                                   使用示例: python demo_script.py --name Alice --age 25    

3.2.2 Agent 如何使用 --help

在 SKILL.md 中你只需要指引 Agent 先通过 --help 了解脚本用法,然后构造正确的调用命令:

  1. 1. 主动出发:当Agent需要调用你的脚本,但又不知道具体用法时,它会先主动执行 python your_script.py --help
  2. 2. 阅读分析:Agent会解析返回的结构化文本,获取脚本的用途、参数、选项和示例等全部使用信息。
  3. 3. 决策执行:Agent根据分析结果,构造正确的命令来执行实际任务。如果没有帮助,Agent可能用错参数导致任务失败。

3.3 脚本要提供有用的错误提示信息

一个好错误消息需要准确说明出错原因、预期收到的值以及提供具体的修正建议,从而引导 Agent 做出正确的下一步尝试,直接决定了 Agent 能否从失败中自动恢复。

要素
说明
示例
指出错误位置
具体是哪个参数、哪个文件、哪一行
错误: 文件 'data.csv' 不存在
解释为什么
违反了什么规则或缺少了什么条件
列 'sales' 不存在,可用列: date, revenue, region
给出修正建议
Agent 可以据此调整命令
请检查文件路径,或使用 --delimiter 指定正确的分隔符

为什么对 Agent 特别重要?

Agent 是非交互式运行,无法像人那样“猜”哪里不对。一个精确的错误消息能让 Agent:

  • • 自动重试(比如缺少 --column,Agent 可以补上)
  • • 修正参数(比如列名拼写错误,Agent 可以改用正确的列名)
  • • 向用户解释(比如“文件不存在,请你提供正确的路径”)

例如:

  • • ❌ 差:Error: invalid input
  • • ✅ 好:Error: Column 'sales' not found. Available columns: date, revenue, region. Please use --column with one of these.

3.4 脚本的输出要采用结构化格式

我们首先要认识到以下几点:

  • • Agent 默认从 stdout 中读取脚本的“执行结果”
  • • 如果脚本正常执行但没有向 stdout 输出任何内容,Agent 就会认为没有有效数据返回(可能视为空结果或失败)。
  • • stderr 的内容不会被 Agent 当作“结果”,而是作为诊断信息(日志、警告、错误)单独捕获,用于排查问题或反馈给用户。

stdout 和 stderr 是标准数据流的概念,几乎所有现代操作系统和编程语言都支持这一机制。

  • • stdout(标准输出):程序输出正常结果的通道。
  • • stderr(标准错误):程序输出错误、警告、诊断信息的独立通道。

无论 Python、Bash、Node.js 还是 C++,操作系统在启动进程时都会为其准备 stdin(标准输入)、stdoutstderr 这三个标准文件描述符(文件句柄)。只是不同语言的使用形式不同:

  • • Python:print() 默认输出到 stdoutsys.stdout.write() 明确写 stdoutsys.stderr.write() 写 stderr
  • • Bash:echo 默认输出到 stdoutecho >&2 重定向到 stderr
  • • Node.js:console.log() 输出到 stdoutconsole.error() 输出到 stderr

脚本的输出要采用结构化格式(如 JSON、CSV),并且把“有效数据”和“诊断信息”分开输出到不同的流中。

优先使用结构化格式:推荐 JSON、CSV、TSV 等有明确字段边界的格式,而不是对齐的空格表格或纯文本。结构化数据可以被 Agent 解析,也可以被 jqcutawk 等标准工具直接处理,使脚本易于组合进更大的处理流水线(支持多种工具的组合)。

例如:

  • • ❌ 表格形式(靠空格对齐):难以用程序可靠解析(字段宽度可变、无法区分空值等)

    NAME          STATUS    CREATEDmy-service    running   2025-01-15
  • • ✅ JSON 格式:字段边界明确,任何语言和工具都能精确读取

    {"name":"my-service","status":"running","created":"2025-01-15"}

分离数据与诊断信息:

  • • 标准输出(stdout):只输出结构化、可解析的有效数据(例如查询结果、计算结果)。
  • • 标准错误(stderr):输出人类/Agent 可读的诊断信息(进度消息、警告、错误提示等)。

这样 Agent 可以干净地捕获 stdout 获取数据,同时在需要排查问题时仍能查看 stderr 中的信息,二者互不干扰。

虽然技术上所有脚本都有这两个流,但不是所有脚本都正确地区分它们。很多脚本习惯把所有输出都扔到 stdout(比如 print("Processing...") 和 print(result) 混在一起)。这在交互式命令行下没问题,但在自动化场景(Agent 调用脚本)中会带来麻烦:

  • • Agent 希望捕获 stdout 作为纯净的数据(如 JSON),但混入了无关的进度信息,导致解析失败。
  • • 无法在不丢失信息的情况下区分“有效结果”和“执行日志”。

例如:add_num.py

#!/usr/bin/env python3import argparseimport jsonimport sysdefmain():    parser = argparse.ArgumentParser(description="两数相加,输出 JSON")    parser.add_argument("--a"type=float, required=Truehelp="第一个数字")    parser.add_argument("--b"type=float, required=Truehelp="第二个数字")    args = parser.parse_args()    result = {"a": args.a, "b": args.b, "sum": args.a + args.b}# 数据到 stdout,诊断到 stderr    sys.stderr.write(f"计算 {args.a} + {args.b}\n")print(json.dumps(result, indent=2))if __name__ == "__main__":    main()

执行脚本:

$ python scripts/add_num.py --a 3.5 --b 2.7

stdout:

{"a":3.5,"b":2.7,"sum":6.2}

stderr:计算 3.5 + 2.7

3.5 脚本执行要保证幂等性

多次执行同一个脚本,产生的结果与执行一次完全相同,且不会引发错误。Agent 可能会因为网络波动、超时等原因重试命令。如果脚本不是幂等的(例如“创建资源,已存在则报错”),重试就会导致失败或副作用累积。

问题
非幂等操作
后果
重试后原本成功的操作却报错
创建已存在 → 失败
Agent 误以为任务失败
重试导致重复数据
插入/追加,无去重
数据重复、逻辑错误(副作用累积)
重试导致资源浪费
重复计算/重复请求
消耗配额、性能下降(副作用累积)
重试导致状态不一致
例如“增加计数”未做幂等
计数被多次增加(副作用累积)

幂等性让 Agent 可以安全地重试,而不用担心副作用或误判结果。

3.6 输入约束

对输入参数进行严格校验,拒绝模糊或无效的输入,并给出清晰的错误信息。

Agent 不能像人类一样“猜测”意图。如果脚本对模糊输入(例如部分匹配、近似值)自行推测,可能产生非预期的结果,且难以调试。

例如:

  • • 使用枚举类型(choices=['csv', 'json'])限制输出格式。
  • • 对范围、格式、存在性做校验。
  • • 错误信息应列出有效选项:错误:未知格式 'xlsx'。有效格式:csv, json, parquet

3.7 支持模拟运行

提供一个 --dry-run 标志,让脚本只输出“将要做什么”,而不实际执行任何破坏性或状态变更操作。

Agent 在处理高风险任务(删除文件、修改数据库、发送邮件)前,可以先执行 --dry-run 预览结果,确认无误后再去掉该标志正式执行。这大幅降低了自动化操作的风险。

实际执行时去掉 --dry-run 即可。

--dry-run 也需要像 --help 一样,由脚本显式实现,不是自动提供的功能。如果没有在脚本代码中定义,脚本就不认识 --dry-run,执行时会报“unrecognized arguments”错误。

3.7.1 在脚本中实现 --dry-run :

你需要在参数解析部分添加 --dry-run 或 --dry-run 标志(通常是一个 store_true 动作),然后在脚本的主要执行流程中判断该标志:

parser.add_argument("--dry-run", action="store_true"help="仅显示将要执行的操作,不实际执行")args = parser.parse_args()if args.dry_run:print("[dry-run] 将会执行以下操作:...")# 只输出计划,不执行实际变更    sys.exit(0)# 实际执行变更操作...

3.7.2 如何让 Agent 模拟运行脚本

Agent 本身没有内置逻辑自动先执行 --dry-run 再请求确认。它只会严格按照 SKILL.md 中的指令行事。

如果你在技能描述中只写了“运行 python script.py --do-something”,Agent 会直接执行,不会额外加 --dry-run

需要在 SKILL.md 中显式写出这个流程。例如:

## 工作流程1.**先预览**:运行 `python script.py --dry-run`,查看将要执行的操作。2.**向用户确认**:将干跑输出的内容展示给用户,并询问:“以上操作是否执行?请回复‘确认’或‘继续’。”3.**实际执行**:如果用户确认,再运行 `python script.py`(不带 `--dry-run`)。

如果按照上述指令,Agent 会先执行 --dry-run,把预览结果告诉你,然后等待你的确认。

你回复“确认”后,Agent 再执行真正的命令。

3.8 有意义的退出码

脚本退出时返回不同的整数值,表示不同的失败原因(或成功)。

Agent 可以检查退出码来判断脚本是否成功,以及失败的具体类型,从而决定下一步动作(重试、修正参数、报告用户)。如果所有错误都返回同一个非零值,Agent 无法区分“文件不存在”和“权限不足”。

例如(像http协议的响应状态码一样):

退出码
含义
0
成功
1
通用错误
2
参数无效(如缺少必需参数)
3
输入文件不存在
4
权限不足
5
数据格式错误

注意:这些约定需要在 --help 中明确说明,Agent 才能知道如何解读。

3.9 安全的脚本默认行为

对于可能产生破坏性影响的脚本,默认行为应该是保守的、非破坏性的,需要用户(或 Agent)显式传递确认标志(如 --confirm--force)才能执行危险操作。

避免因 Agent 的误解或一次错误的参数传递导致不可逆的数据丢失或服务中断。

例如:

  • • 一个删除数据的脚本:默认只输出将要删除的记录,不实际删除;必须加上 --confirm 才真正执行。
  • • 一个覆盖文件的脚本:默认报错“输出文件已存在,请使用 --force 覆盖”。
  • • 一个重启服务的脚本:默认要求 --yes 或交互式确认(但 Agent 无法交互,因此需要 --yes 标志)。

3.9.1 skill 中如何使用?

在 Skill 中的使用体现为两个层面:脚本内部的设计 + SKILL.md 中对 Agent 的调用指引

核心目标是:对于可能造成不可逆变更的操作,默认不做任何破坏性行为,要求 Agent 显式传递确认标志才能执行。

假设有一个脚本 delete_old_records.py,用于从数据库中删除过期数据:

#!/usr/bin/env python3import argparsedefmain():    parser = argparse.ArgumentParser(description="删除过期记录")    parser.add_argument("--days"type=int, required=Truehelp="保留最近N天,删除更早的记录")    parser.add_argument("--confirm", action="store_true"help="确认执行删除操作")    args = parser.parse_args()# 安全默认:不传递 --confirm 时,只输出预览,不实际删除ifnot args.confirm:print(f"[DRY-RUN] 将会删除 {args.days} 天之前的记录。如需实际删除,请添加 --confirm 标志")return# 实际删除逻辑...print(f"已删除 {args.days} 天之前的记录")
  • • 没有 --confirm 时,脚本只会输出预览,不执行任何修改。
  • • Agent 必须显式加上 --confirm 才能触发真正的删除操作。

在 SKILL.md 中指引 Agent:你需要在技能描述中明确要求 Agent 先获取用户授权,再使用 --confirm

---name: db-cleanupdescription: 当用户要求清理数据库中的旧数据时使用此技能。---## 工作流程1.**先预览**:运行 `python scripts/delete_old_records.py --days 30`(不带 `--confirm`),将输出的预览信息展示给用户。2.**请求确认**:询问用户:“以上将会删除 30 天前的记录,是否确认执行?请回复‘确认’。”3.**执行删除**:如果用户确认,再运行 `python scripts/delete_old_records.py --days 30 --confirm`## 注意事项- 绝对不要在未获得用户明确确认的情况下添加 `--confirm` 标志。- 如果用户拒绝,停止操作并告知用户已取消。

实际执行示例:

用户:“帮我删除 30 天前的数据库记录”

  • • Agent 执行 python scripts/delete_old_records.py --days 30输出:[DRY-RUN] 将会删除 30 天之前的记录。如需实际删除,请添加 --confirm 标志
  • • Agent 将这条信息呈现给用户,并询问:“以上操作是否确认执行?请回复‘确认’。”
  • • 用户回复“确认”
  • • Agent 再执行 python scripts/delete_old_records.py --days 30 --confirm

3.9.2 与–dry-run的区别

维度
--dry-run
Safe defaults
触发方式
用户/Agent 显式添加--dry-run 标志
脚本默认采用安全行为,需要显式授权(如 --confirm)才能执行破坏性操作
默认行为
默认实际执行(没有 --dry-run 时)
默认不执行破坏性操作,或只执行只读操作
目的
提供预览,让调用方确认影响范围
防止意外或误操作导致的数据损坏、状态变更
典型标志 --dry-run --confirm

--force--yes--no-preserve
适用场景
批量操作、复杂变更(如迁移、删除大量数据)
任何可能产生不可逆后果的操作(覆盖、删除、创建重复资源)

--dry-run:调用方主动说“先让我看看会做什么,别真做”。

Safe defaults:脚本设计者说“默认我不动任何东西,除非你明确告诉我‘动手’”。

在 Agent Skill 设计中,推荐优先采用 Safe defaults,因为 Agent 可能会因为重试、误解或指令不完整而意外触发破坏性操作。加上 --confirm 守卫可以让 Agent 在获得用户确认后才传递该标志,大大提升安全性。而 --dry-run 则更多用于复杂决策前的预览,两者可以同时存在。

脚本可以同时支持 --dry-run(强制预览,即使有 --confirm 也不实际执行)和 --confirm(授权实际执行)。

3.10 可预测的脚本输出大小

考虑到 Agent 环境往往对工具输出有长度限制(例如 10KB–30KB),脚本输出应控制大小,避免截断导致信息丢失。

如果脚本一次性输出数千行日志或一个巨大的 JSON,Agent 可能只收到前 30KB,后半部分被截断,导致解析失败或关键数据丢失。

实践示例(在SKILL.md中向agent说明):

  • • 默认只输出摘要或前 N 条记录,并提供 --offset/--limit 分页参数,让 Agent 按需请求更多数据。
  • • 对于可能产生大输出的命令,要求 Agent 通过 --output file 将结果写入文件,而不是输出到 stdout。
  • • 或者通过 --format 选项选择更紧凑的格式(如 CSV 代替 JSON 漂亮打印)。

让脚本主动适应 Agent 的限制,而不是依赖 Agent 去处理截断问题。

4. 参考

Anthropic官方文档:https://agentskills.io/skill-creation/using-scripts

PEP 723 – 内联脚本元数据:https://peps.pythonlang.cn/pep-0723/