图片

本文约9500字,建议阅读15分钟还在为AI助手不懂你的业务场景而头疼?一个轻量级的技能包,就能让通用模型听懂你的“黑话”。

“帮我处理一下这个PDF表单,提取里面的订单信息,然后合并成一份报告。”

当你向AI助手提出这样的需求时,它可能会手忙脚乱地尝试各种库,或者干脆告诉你“我做不到”。但你有没有想过,如果能给AI一份“操作手册”,告诉它“用这个工具,按这个步骤,注意这个坑”,它就能轻松搞定?

这就是 Agent Skills 要做的事情。

想象一下,你的AI助手就像一个刚入职的新员工。他聪明、学习能力强,但不知道你们公司的业务流程、代码规范、以及那些只有老员工才知道的“潜规则”。Skills,就是一份 岗位职责说明书+操作SOP+避坑指南 的合集。

本文将深入拆解Agent Skills这个轻量级的开放格式,从原理到实践,再到如何评估和增强技能,教你如何让通用大模型秒变你的领域专家。

到底什么是Agent Skills?


从本质上讲,一个Skill就是一个文件夹。这个文件夹里最关键的文件是一个叫 SKILL.md 的文件。它就像一份“任务说明书”,里面不仅告诉AI这个技能是干嘛的(元数据),还详细描述了“怎么做”(操作指令)。

一个典型的Skill目录结构长这样:

my-skill/├── SKILL.md          # 必须:包含元数据和核心指令├── scripts/          # 可选:可执行脚本(Python/Bash等)├── references/       # 可选:参考文档(API说明、详细指南等)└── assets/           # 可选:静态资源(模板、图片等)

你可能会问:“这和直接把所有东西塞进Prompt里有什么区别?”区别大了!这就涉及到了Agent Skills的核心设计哲学——渐进式披露。

渐进式披露:像外卖骑手一样高效


“渐进式披露”这个概念听起来很玄乎,但你可以把它想象成外卖骑手接单的过程:

  • 第一步:发现 🚴‍♂️
    外卖平台打开时,骑手(AI)只会看到所有可接订单的概要信息:比如“XX餐厅的订单,距离2公里,预计收入8元”。这对应Skill的 name 和 description 字段。系统只加载这些轻量信息,来判断是否与该任务相关。

  • 第二步:激活 ✅
    当骑手看到这个订单并决定接单时,他才会点进去,看到完整的订单详情:具体菜品、客户地址、备注要求(“不要香菜”)。这就对应当Agent发现某个任务匹配Skill的描述时,它会将完整的 SKILL.md 文件加载到上下文中。

  • 第三步:执行 🏃‍♂️
    骑手根据详细地址出发,必要时查看地图(参考文件)或者联系客户(执行脚本)。这对应Agent按照Skill的指令,按需加载 references/ 中的文档或执行 scripts/ 里的代码。

这种设计的好处显而易见:AI的“大脑”(上下文窗口)不会被所有Skill的细节塞满,只有在需要时才会加载必要的信息,从而保持思考速度,且能应对更复杂的任务。

SKILL.md 文件长什么样?


这是Skill的核心。它由两部分组成:YAML 前置元数据 和 Markdown 正文。

---name: pdf-processingdescription: 从PDF中提取文本和表格、填写表单、合并文件。当用户需要处理PDF文档时使用此技能。license: Apache-2.0metadata:  author: your-team  version: "1.0"---# PDF 处理技能## 何时使用此技能当用户提到“PDF”、“表单”、“提取文本”等关键词时,应激活此技能...## 如何提取文本1. 使用 `pdfplumber` 库进行文本提取,这是首选方案。2. 对于扫描件(无法直接提取文本),需回退到 `pdf2image` + `pytesseract` 进行OCR识别...## 常见坑点(Gotchas)- ⚠️ 注意:部分PDF文件是扫描生成的,直接使用 `pdfplumber` 会得到空结果。此时必须判断文件是否包含文本层,若无,则自动切换到OCR流程。- 填写表单时,务必先运行 `scripts/analyze_form.py` 获取所有字段列表,不要凭猜测填写。

元数据字段详解


字段

是否必须

说明

name

技能标识符。只能包含小写字母、数字和连字符(-),不超过64字符。必须与父目录名一致。

description

核心!用于告诉Agent“何时使用”。要包含关键词,描述做什么和什么时候用。最多1024字符。

license

许可证信息。

compatibility

环境要求。如“需要 Python 3.10+ 和 uv 包管理器”。

metadata

自定义键值对,用于额外元数据。

allowed-tools

(实验性)允许技能使用的预批准工具列表。

⚠️ 注意:这里90%的人会踩坑

description 字段非常关键。如果描述不准确或不包含关键词,Agent根本不会激活你的Skill,写得再好也没用。例如,“处理PDF” 远不如 “从PDF中提取文本、填写表单、合并文件。当用户需要处理PDF文档时使用此技能。” 有效。

实战:如何写出一个高质量的Skill?


光知道格式还不够,怎么写才能让AI真正用起来?这里有几个来自官方的最佳实践,非常值得借鉴。

1. 从真实经验中提炼,而非凭空想象


很多人写Skill时,会依赖AI自己的“常识”,结果写出一堆废话。比如“处理错误”、“遵循最佳实践”这种空话。真正有价值的Skill,来源于你的真实项目经验。

怎么做?


你可以先和AI协作完成一个任务,在对话中不断提供上下文、纠正错误、强调偏好。然后,把这个过程中的成功步骤、你的修正、输入输出格式,提炼成Skill。

或者,从你的项目文档中“喂”给AI来生成。比如,把你团队的事故复盘报告、API设计规范、代码审查意见喂给AI,让它帮你提炼出“代码审查”Skill,这样的Skill会包含你们项目特有的错误模式,远比通用版本强。

2. 像写函数一样设计Skill的边界


Skill的边界要清晰。它应该像函数一样,完成一个“连贯的工作单元”。

  • 范围过小:一个任务需要激活多个Skill,增加开销,且指令可能冲突。

  • 范围过大:激活条件难以精准,容易误触发,且内部指令臃肿。

比如,一个“查询数据库并格式化结果”的技能是合理的。但如果这个技能还试图管理数据库集群,那就太“胖”了。

3. 写“方法”而不是“答案”


Skill应该教会AI 如何思考,而不是给出一个特定问题的答案。这样它才能举一反三。

糟糕:只适用于这一个场景
将 `orders` 表与 `customers` 表在 `customer_id` 上关联,筛选 `region = 'EMEA'`,并求和 `amount` 列。
优秀:可复用的方法 
1. 从 `references/schema.yaml` 读取数据库Schema,找到相关表。2. 按照 `_id` 外键约定关联表。3. 根据用户请求,使用 `WHERE` 子句应用筛选条件。4. 按需聚合数值列,最终以Markdown表格输出。

4. 预设“默认值”,而不是提供“菜单”


当有多种工具可选时,直接给出你的推荐,而不是罗列所有选项。这能显著减少AI犹豫不决的时间。

太多选项,AI会困惑
你可以使用 pypdf, pdfplumber, PyMuPDF 或 pdf2image...
明确的默认方案,加上备选方案
使用 `pdfplumber` 进行文本提取:```pythonimport pdfplumber

如果遇到无法提取文本的扫描件,再回退到 pdf2image 配合 pytesseract。

### 5. 必杀技:“坑点(Gotchas)”章节这是最有价值的内容。把那些“只有踩过坑才知道”的细节写下来。AI不会知道你们公司数据库的 `users` 表使用了软删除(`deleted_at IS NULL`),除非你明确告诉它。```markdown## 坑点(Gotchas)- `users` 表使用了软删除。所有查询**必须**包含 `WHERE deleted_at IS NULL`,否则结果会包含已注销的账户,导致数据错误。- 用户ID在数据库中是 `user_id`,在认证服务中是 `uid`,在计费API中是 `accountId`。它们都指代同一个值,但字段名不同。- `/health` 端点只要Web服务器在运行就会返回200,即使数据库已宕机。请使用 `/ready` 端点来检查服务的**完整健康状态**。

5. 提供输出模板和检查清单


当需要AI输出特定格式时,直接给它一个模板。这比用自然语言描述要可靠得多。

## 报告结构请使用此模板生成最终报告:```markdown# [分析标题]## 执行摘要[关键发现的一段落概述]## 主要发现- 发现1,附上数据支撑- 发现2,附上数据支撑## 建议1. 具体、可执行的建议2. 具体、可执行的建议
对于多步骤流程,一个显式的检查清单可以帮助AI跟踪进度,避免遗漏关键步骤。```markdown## 表单处理工作流进度跟踪:- [ ] 步骤1:分析表单(运行 `scripts/analyze_form.py`)- [ ] 步骤2:创建字段映射(编辑 `fields.json`)- [ ] 步骤3:验证映射(运行 `scripts/validate_fields.py`)- [ ] 步骤4:填写表单(运行 `scripts/fill_form.py`)- [ ] 步骤5:验证输出(运行 `scripts/verify_output.py`)

如何让你的Skill被AI“看”到?


写好了Skill,但AI助手就是不用,怎么办?问题很可能出在 description 上。

Agent在启动时,只会加载所有Skill的 name 和 description,来判断“这个任务,要不要用这个Skill?”。如果描述不准确,Skill就永远躺在角落里吃灰。

这里有一个 优化 description 的系统性方法:

  1. 设计测试用例:准备20个左右的提示词,其中一半应该触发该Skill,一半不应该触发。

  2. 运行测试:用一个脚本让AI处理这些提示词,并记录它是否激活了你的Skill。注意,AI的输出有随机性,所以每个测试用例最好运行3次以上,取触发概率。

  3. 分析并优化:

    如果“应该触发”的用例没触发,说明你的描述太窄了。可以拓宽范围,或者补充更多关键词。

    如果“不应该触发”的用例触发了,说明你的描述太宽了。需要增加特异性,明确边界。

  4. 迭代:不断重复这个过程,直到通过率满意为止。

一个糟糕的描述 vs. 一个优秀的描述:

# 糟糕:范围太窄,关键词太少description: 处理CSV文件。# 优秀:范围清晰,关键词丰富,明确边界description: >  分析CSV和表格数据——计算摘要统计、添加派生列、生成图表、清理脏数据。  当用户有CSV、TSV或Excel文件,并想探索、转换或可视化数据时使用此技能,  即使他们没有明确提到“CSV”或“分析”。


评估:你的Skill真的有效吗?


你写了一个Skill,试了一个提示词,它看起来有效。但它在各种提示词下、在边缘情况下、在没有这个Skill的情况下,表现如何?运行结构化评估(evals)能回答这些问题,并为你提供一个系统性的改进反馈循环。

设计测试用例


一个测试用例包含三个部分:

  • 提示词:一个真实的用户消息——用户真正会输入的那种。

  • 预期输出:对成功样貌的人类可读描述。

  • 输入文件(可选):Skill工作时需要的文件。

你可以在Skill目录中创建一个 evals/evals.json 文件来存储它们:

{  "skill_name": "csv-analyzer","evals": [    {      "id": 1,      "prompt": "我有一份月度销售数据的CSV文件,在 data/sales_2025.csv。你能找出收入最高的3个月并画一个柱状图吗?",      "expected_output": "一张柱状图图片,显示收入最高的3个月,坐标轴和数值都有标签。",      "files": ["evals/files/sales_2025.csv"]    },    {      "id": 2,      "prompt": "我下载目录里有个 customers.csv,有些行缺了邮箱——你能清理一下并告诉我缺了多少个吗?",      "expected_output": "一个清理后的CSV文件,处理了缺失的邮箱,并统计了缺失的数量。",      "files": ["evals/files/customers.csv"]    }  ]}

编写好测试提示词的技巧:

  • 从2-3个测试用例开始。在还没看到第一轮结果前,不要投入太多。

  • 变化提示词。使用不同的措辞、详细程度和正式程度。有些提示词可以很随意(“嘿,能帮我清理下这个CSV吗?”),有些则更精确(“解析 data/input.csv,删除B列为空的行,将结果写入 data/output.csv”)。

  • 覆盖边缘情况。至少包含一个测试边界条件的提示词——比如一个格式错误的输入、一个不寻常的请求,或者Skill指令可能产生歧义的情况。

  • 使用真实的上下文。真实的用户会提到文件路径、列名和个人背景。像“处理这个数据”这样模糊的提示词测试不出什么。


运行评估


核心模式是对每个测试用例运行两次:一次带着Skill,一次不带Skill(或者使用旧版本)。这给了你一个可以对比的基线。

组织你的工作空间:

csv-analyzer/├── SKILL.md└── evals/    └── evals.jsoncsv-analyzer-workspace/└── iteration-1/    ├── eval-top-months-chart/    │   ├── with_skill/    │   │   ├── outputs/       # 运行产生的文件    │   │   ├── timing.json    # Token数和耗时    │   │   └── grading.json   # 断言结果    │   └── without_skill/    │       ├── outputs/    │       ├── timing.json    │       └── grading.json    ├── eval-clean-missing-emails/    │   ├── with_skill/    │   │   ├── outputs/    │   │   ├── timing.json    │   │   └── grading.json    │   └── without_skill/    │       ├── outputs/    │       ├── timing.json    │       └── grading.json    └── benchmark.json         # 汇总统计数据

编写断言


断言是关于输出应该包含什么或达成什么的可验证陈述。在你看过第一轮输出后再添加它们——通常直到Skill运行过后你才知道“好”是什么样子。

好的断言:

  • “输出文件是有效的 JSON” —— 可编程验证。

  • “柱状图有坐标轴标签” —— 具体且可观察。

  • “报告包含至少3条建议” —— 可计数。

弱的断言:

  • “输出很好” —— 太模糊,无法评分。

  • “输出准确使用了‘总收入:$X’这个短语” —— 太脆弱,正确的输出用不同措辞就会失败。

不是所有东西都需要断言。有些品质——写作风格、视觉设计、输出“感觉是否对”——很难分解成通过/失败检查。这些更适合在人工审查中捕捉。把断言留给那些可以客观检查的事情。

向 evals/evals.json 中的每个测试用例添加断言:

{  "skill_name": "csv-analyzer","evals": [    {      "id": 1,      "prompt": "我有一份月度销售数据的CSV文件,在 data/sales_2025.csv。你能找出收入最高的3个月并画一个柱状图吗?",      "expected_output": "一张柱状图图片,显示收入最高的3个月,坐标轴和数值都有标签。",      "files": ["evals/files/sales_2025.csv"],      "assertions": [        "输出包含一张柱状图图片文件",        "图表恰好显示了3个月",        "两个坐标轴都有标签",        "图表的标题或说明提到了收入"      ]    }  ]}

评分


评分意味着根据实际输出来评估每个断言,并记录 通过 或 失败 及具体证据。证据应该引用或参考输出,而不仅仅是陈述观点。

最简单的方法是把输出和断言给一个LLM,让它评估每一条。对于那些可以用代码检查的断言(有效的JSON、正确的行数、文件存在且尺寸符合预期),使用验证脚本——脚本对机械性检查更可靠,且可在迭代中复用。

{  "assertion_results": [    {      "text": "输出包含一张柱状图图片文件",      "passed": true,      "evidence": "在outputs目录找到 chart.png (45KB)"    },    {      "text": "图表恰好显示了3个月",      "passed": true,      "evidence": "图表显示了3月、7月和11月的柱状图"    },    {      "text": "两个坐标轴都有标签",      "passed": false,      "evidence": "Y轴有'收入 ($)'标签,但X轴没有标签"    },    {      "text": "图表的标题或说明提到了收入",      "passed": true,      "evidence": "图表标题写着'收入最高的3个月'"    }  ],"summary": {    "passed": 3,    "failed": 1,    "total": 4,    "pass_rate": 0.75  }}

评分原则:

  • 要求通过时有具体证据。 不要给“可能性”加分。如果断言说“包含摘要”,而输出有一个“摘要”部分,里面只有一句模糊的话,那就是失败——标签在那儿,但实质内容没有。

  • 审视断言本身,而不仅仅是结果。 评分时注意那些太容易(不管Skill质量如何总是通过)、太难(即使输出很好也总是失败)或无法验证(仅从输出无法检查)的断言。在下一轮迭代中修复它们。

💡 小提示

要比较两个Skill版本,试试盲测:将两个输出交给一个LLM裁判,不透露哪个来自哪个版本。裁判根据它们自己的标准来评估整体品质——组织、格式、可用性、精细度——这样就不会有“哪个版本应该更好”的偏见。这可以补充断言评分:两个输出可能都通过了所有断言,但在整体质量上存在显著差异。

聚合结果


一旦本轮所有运行都评分完毕,为每种配置计算汇总统计数据,并将它们保存到 benchmark.json 中:

{  "run_summary": {    "with_skill": {      "pass_rate": { "mean": 0.83, "stddev": 0.06 },      "time_seconds": { "mean": 45.0, "stddev": 12.0 },      "tokens": { "mean": 3800, "stddev": 400 }    },    "without_skill": {      "pass_rate": { "mean": 0.33, "stddev": 0.10 },      "time_seconds": { "mean": 32.0, "stddev": 8.0 },      "tokens": { "mean": 2100, "stddev": 300 }    },    "delta": {      "pass_rate": 0.50,      "time_seconds": 13.0,      "tokens": 1700    }  }}

delta 告诉你Skill的代价(更多时间、更多Token)和它的收益(更高的通过率)。一个增加13秒但将通过率提高50个百分点的Skill可能是值得的。一个Token用量翻倍但只换来2个百分点提升的Skill可能就不太值。

分析模式


聚合统计数据可能隐藏重要的模式。计算完基准后:

  • 移除或替换那些在两种配置下总是通过的断言。 这些断言告诉你没有有用信息——没有Skill模型也能处理。它们会抬高带Skill的通过率,而没有反映实际的Skill价值。

  • 调查那些在两种配置下总是失败的断言。 要么断言本身有问题(要求模型做不到的事),要么测试用例太难,要么断言在检查错误的东西。在下一轮迭代前修复它们。

  • 研究那些带Skill时通过但不带Skill时失败的断言。 这就是Skill明显增加价值的地方。理解为什么——是哪些指令或脚本起了作用?

  • 当结果在运行间不一致时,收紧指令。 如果同一个评估有时通过有时失败(在基准中表现为高标准差),可能是因为评估本身不稳定(对模型随机性敏感),或者Skill的指令足够模糊,以至于模型每次解读都不同。添加示例或更具体的指导来减少模糊性。

  • 检查时间和Token异常值。 如果某个评估花费的时间是其他评估的3倍,阅读它的执行日志(模型在运行期间做的完整记录)以找到瓶颈。


用人工审查结果


断言评分和模式分析能捕捉很多问题,但它们只检查了你想过要写断言的东西。人工审查者带来新视角——捕捉你未曾预料到的问题,注意到输出技术上正确但没抓住重点的情况,或者发现难以表达为通过/失败检查的问题。对每个测试用例,审查实际输出和评分。

为每个测试用例记录具体反馈,并将其保存在工作空间中(例如,与评估目录并列的 feedback.json):

{  "eval-top-months-chart": "图表缺少坐标轴标签,月份是按字母顺序而不是时间顺序排列的。",  "eval-clean-missing-emails": ""}

“图表缺少坐标轴标签”是可操作的;“看起来不好”则不是。空反馈意味着输出看起来没问题——那个测试用例通过了你的审查。在迭代Skill时,把你的改进集中在有具体问题的测试用例上。

迭代Skill


在评分和审查之后,你有三个信号来源:

  • 失败的断言指向具体的差距——缺失的步骤、不清晰的指令,或Skill不处理的情况。

  • 人工反馈指向更广泛的质量问题——方法不对、输出结构差,或Skill产生了技术上正确但无帮助的结果。

  • 执行日志揭示了为什么出错。如果Agent忽略了某个指令,该指令可能模糊不清。如果Agent在无用的步骤上花了时间,这些指令可能需要简化或移除。

将这些信号转化为Skill改进的最有效方法是把三者——连同当前的 SKILL.md——交给一个LLM,让它提出修改建议。LLM可以综合失败断言、审查者抱怨和执行日志行为中的模式,这些手动连接会很繁琐。在提示LLM时,包含这些指导原则:

  • 从反馈中泛化。 Skill将用于许多不同的提示词,而不仅仅是测试用例。修复应从根本上解决广泛的问题,而不是为特定例子添加狭隘的补丁。

  • 保持Skill精简。 更少、更好的指令往往胜过详尽的规则。如果日志显示有浪费的工作(不必要的验证、不需要的中间输出),移除那些指令。如果尽管增加了更多规则,通过率仍停滞不前,Skill可能被过度约束了——尝试移除一些指令,看看结果是否保持或改善。

  • 解释为什么。 基于推理的指令(“做X是因为Y往往导致Z”)比僵化的指令(“总是做X,永远不要做Y”)效果更好。模型在理解目的时能更可靠地遵循指令。

  • 打包重复工作。 如果每次测试运行都独立编写了类似的辅助脚本(一个图表生成器、一个数据解析器),这是一个信号,表明应该将该脚本打包进Skill的 scripts/ 目录。

循环:

  1. 将评估信号和当前的 SKILL.md 交给一个LLM,让它提出改进建议。

  2. 审查并应用更改。

  3. 在一个新的 iteration-<N+1>/ 目录下重新运行所有测试用例。

  4. 评分并聚合新结果。

  5. 用人工审查。重复。

当你对结果满意、反馈一直为空,或迭代间不再有显著改进时,就可以停止了。


为Skill编写脚本:从一次性命令到可复用逻辑


Skill可以指导Agent运行Shell命令,并在 scripts/ 目录中打包可复用的脚本。这一节涵盖一次性命令、自带依赖的自包含脚本,以及如何为Agentic使用设计脚本接口。

一次性命令


当现有的包已经做了你需要的事,你可以直接在 SKILL.md 指令中引用它,而不需要 scripts/ 目录。许多生态系统提供在运行时自动解析依赖关系的工具。

这里有几个常用的工具:

工具

说明

示例

uvx

运行Python包,在隔离环境中,带积极缓存。与 uv[1] 一同提供。需要单独安装。

uvx ruff@0.8.0 check .

pipx

在隔离环境中运行Python包。可通过OS包管理器获取。是 uvx 的成熟替代品。

pipx run 'black==24.10.0' .

npx

运行npm包,按需下载。与npm(与Node.js一起提供)一同提供。

npx eslint@9 --fix .

bunx

Bun版本的 npx。与 Bun[2] 一同提供。

bunx eslint@9 --fix .

deno run

直接从URL或说明符运行脚本。与 Deno[3] 一同提供。

deno run npm:create-vite@6 my-app

go run

编译并运行Go包。内置在 go 命令中。

go run golang.org/x/tools/cmd/goimports@v0.28.0 .

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

  • 固定版本(例如 npx eslint@9.0.0),确保命令在不同时间行为一致。

  • 在 SKILL.md 中说明前提条件(例如“需要 Node.js 18+”),而不是假设Agent的环境就有。对于运行时级别的需求,使用 compatibility 前置元数据字段)。

  • 把复杂的命令移到脚本中。 一次性命令适合用几个标志调用一个工具。当命令变得复杂到难以一次做对时,一个经过测试的 scripts/ 中的脚本更可靠。


从 SKILL.md 引用脚本


使用从Skill目录根开始的相对路径来引用打包的文件。Agent会自动解析这些路径——不需要绝对路径。

在你的 SKILL.md 中列出可用的脚本,让Agent知道它们存在:

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

然后指导Agent运行它们:

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

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

自包含脚本


当你需要可复用的逻辑时,在 scripts/ 中打包一个声明自己依赖的脚本。Agent可以用一个命令运行它——不需要单独的清单文件或安装步骤。

Python(PEP 723):使用 # /// script 标记内的TOML块声明依赖:

# /// script# dependencies = [#   "beautifulsoup4",# ]# ///from bs4 import BeautifulSouphtml = '<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

Deno:Deno的 npm: 和 jsr: 导入说明符默认使每个脚本自包含:

#!/usr/bin/env -S deno runimport * 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

Bun:当没有找到 node_modules 目录时,Bun会在运行时自动安装缺失的包。在导入路径中直接固定版本:

#!/usr/bin/env bunimport * 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

Ruby:使用 bundler/inline 直接在脚本中声明gems:

require 'bundler/inline'gemfile do  source 'https://rubygems.org'  gem 'nokogiri'endhtml = '<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

为Agentic使用设计脚本


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

避免交互式提示。这是Agent执行环境的一个硬性要求。Agent在非交互式Shell中运行——它们不能响应TTY提示、密码对话框或确认菜单。一个阻塞等待输入的脚本会无限期挂起。

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

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

用法:scripts/process.py [选项] 输入文件处理输入数据并生成摘要报告。选项:  --format 格式    输出格式:json, csv, table(默认:json)  --output 文件    将输出写入文件而非stdout  --verbose        向stderr打印进度示例:  scripts/process.py data.csv  scripts/process.py --format csv --output report.csv data.csv

保持简洁——输出会进入Agent的上下文窗口,和它正在处理的其他所有东西一起。

编写有帮助的错误消息。当Agent收到错误时,消息直接影响它的下一步尝试。模糊的“错误:无效输入”浪费一次尝试。相反,说明哪里错了,期望什么,以及可以尝试什么:

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

使用结构化输出。优先选择结构化格式——JSON、CSV、TSV——而不是自由文本。结构化格式可以被Agent和标准工具(jq、cut、awk)使用,使你的脚本在管道中可组合。

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

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

进一步考虑:

  • 幂等性。Agent可能会重试命令。“如果不存在则创建”比“创建,重复时失败”更安全。

  • 输入约束。用清晰的错误拒绝模糊的输入,而不是猜测。尽可能使用枚举和封闭集。

  • 空运行支持。对于有破坏性或状态性的操作,一个 --dry-run 标志让Agent可以预览将要发生什么。

  • 有意义的退出码。为不同的失败类型使用不同的退出码(未找到、参数无效、认证失败),并在 --help 输出中记录,以便Agent知道每个代码的含义。

  • 安全的默认值。考虑破坏性操作是否需要显式的确认标志(--confirm、--force)或与风险级别相适应的其他保护措施。

  • 可预测的输出大小。许多Agent工具会自动截断超过阈值(如10-30K字符)的工具输出,可能丢失关键信息。如果你的脚本可能产生大量输出,默认输出摘要或合理限制,并支持 --offset 等标志,以便Agent在需要时请求更多信息。或者,如果输出很大且不适合分页,要求Agent传递一个 --output 标志来指定输出文件或 - 显式选择输出到stdout。


写在最后


Agent Skills 是一种非常优雅的“技能封装”范式。它不改变模型本身,而是通过结构化的上下文注入,让通用模型在特定领域瞬间变得专业、可靠。

它的核心理念——“渐进式披露”——不仅适用于AI Agent,对我们人类开发者同样有启发。写代码、写文档、甚至做PPT,都应该遵循“先给概要,按需深入”的原则,这才是高效协作的本质。

现在,你可以试着从你手头最繁琐、最重复的工作入手,把解决它的流程、工具、坑点,整理成一个Skill。然后,通过评估和迭代,让它真正成为你AI助手的“肌肉记忆”。你会发现,这不仅是给AI写说明书,更是对自己知识的又一次深度梳理。

你在开发过程中,最希望AI能学会的“坑点”或“内部规范”是什么?或者,如果让你为自己的团队写一个Skill,你会从哪个痛点开始?欢迎在评论区分享你的想法。

参考资料

[1] uv: https://docs.astral.sh/uv/

[2] Bun: https://bun.sh/

[3] Deno: https://deno.com/

编辑:于腾凯

校对:邱婷婷

关于我们

数据派THU作为数据科学类公众号,背靠清华大学大数据研究中心,分享前沿数据科学与大数据技术创新研究动态、持续传播数据科学知识,努力建设数据人才聚集平台、打造中国大数据最强集团军。

新浪微博:@数据派THU

微信视频号:数据派THU

今日头条:数据派THU

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐