前面一篇文章介绍了agent skills的基本概念,本文将介绍如何写好一个skill。阅读本文前建议先看agent skills笔记。
Table of contents
Open Table of contents
如何写好body content
首先内容要基于真实的业务场景,来源可以是自己的经验,或者是内部文档、代码 review、真实故障案例等。写清楚具体实行步骤和方案,不要给一堆建议。
内容不要写常识,重点放在:项目特有规则,非显而易见的坑,指定工具/API,特殊流程和约束。一个非常好用的判断标准是:“如果没有这句话,模型会不会做错?”
skill的范围要合适,不能太大也不能太小,写的太细可能会和其他skill冲突,同时很难筛选出当前真正相关的内容,写的太宽泛可能不好精确触发,输出格式不可控。只保留必要信息比如用途、触发条件、入参、输出格式、异常处理。明确什么场景用、什么场景绝对不要用。
高风险任务谨慎操作,开放性任务可以给原则,让模型自己判断;但数据库迁移、批量修改、危险操作这类工作,就应该明确规定步骤甚至禁止自由发挥。
高效指令设计模式
这些是用于组织 Skill(技能)内容的可复用技巧。并非每一个技能都需要全部用上,按需选取适配任务的模式即可。
Gotchas
这个比较常用,主要用于避免执行任务时候踩坑。作用是提前列出风险、易错点、边界 case,避免模型犯常识/逻辑漏洞。比如:
## Gotchas
- users 表采用软删除机制。查询时必须带上条件 WHERE deleted_at IS NULL,否则查询结果会包含已停用的账号。
- 用户 ID:数据库中字段为 user_id,认证服务里叫 uid,账单计费 API 中为 accountId;三者指代同一个用户标识。
- /health 健康检查接口:只要 Web 服务进程在运行,就返回 200 状态码,即便数据库连接异常也依旧返回 200。如需校验服务整体健康状态,请使用 /ready 接口。
Output Template
用于输出格式必须稳定,作用是强制输出结构,防止模型自由发挥乱格式,保障结构化输出一致性。比如:
## Report structure
Use this template, adapting sections as needed for the specific analysis:
markdown
# [Analysis Title]
## Executive summary
[One-paragraph overview of key findings]
## Key findings
- Finding 1 with supporting data
- Finding 2 with supporting data
## Recommendations
1. Specific actionable recommendation
2. Specific actionable recommendation
Checklist
用于多步骤任务,防止漏步骤。作用是把任务拆成清单,模型逐条执行,避免遗漏环节。比如:
##Form processing workflow
Progress:
- [ ] Step 1: Analyze the form (run `scripts/analyze_form.py`)
- [ ] Step 2: Create field mapping (edit `fields.json`)
- [ ] Step 3: Validate mapping (run `scripts/validate_fields.py`)
- [ ] Step 4: Fill the form (run `scripts/fill_form.py`)
- [ ] Step 5: Verify output (run `scripts/verify_output.py`)
Validation Loop
用于做完 → 检查 → 修正 → 再检查。作用是让模型自己校验输出,发现问题自动修复,循环直到合格。比如:
## Editing workflow
1. Make your edits
2. Run validation: `python scripts/validate.py output/`
3. If validation fails:
- Review the error message
- Fix the issues
- Run validation again
4. Only proceed when validation passes
Plan → Validate → Execute
用于批量修改、破坏性操作。作用是先做计划,校验计划是否合理,确认没问题再执行,防止误操作。比如:
## PDF form filling
1. Extract form fields: `python scripts/analyze_form.py input.pdf` → `form_fields.json`
(lists every field name, type, and whether it's required)
2. Create `field_values.json` mapping each field name to its intended value
3. Validate: `python scripts/validate_fields.py form_fields.json field_values.json`
(checks that every field name exists in the form, types are compatible, and
required fields aren't missing)
4. If validation fails, revise `field_values.json` and re-validate
5. Fill the form: `python scripts/fill_form.py input.pdf field_values.json output.pdf`
Scripts 用于模型每次都在重复实现同一段逻辑。作用是把重复逻辑封装成 “脚本”,模型直接调用脚本执行,不用每次重新写逻辑。
好的Skill = 真实经验 + 非显然知识 + 明确默认路径 + 可复用流程 + 验证机制 − 废话
如何写好description
description主要是告诉 Agent:什么时候应该调用这个 Skill。比如”当用户需要分析、清洗、转换或可视化表格数据时使用。”
技巧:
- 1.包含具体的触发关键词
description: 提取 PDF 文本和表格,填写表单,合并 PDF。使用场景:处理 PDF 文件、提取文档内容、填写 PDF 表单、合并多个 PDF、用户提及「PDF」「提取」「表单」「合并」等关键词。
- 2.描述要具体而非笼统
# 不好的描述 - 太笼统
description: 帮助处理各种文件。
# 好的描述 - 具体明确
description: 处理图像文件,包括调整大小、转换格式、应用滤镜。使用场景:处理图片、调整图像尺寸、转换图片格式。
- 3.说明多个使用场景
description: 执行代码审查,检查安全漏洞、代码质量和性能问题。使用场景:代码审查、PR 审查、代码检查、安全扫描、质量评估、用户要求「review」「检查代码」「安全性」等。
- 4.区分相似技能
# 技能 1:PDF 文本提取
name: pdf-extract
description: 从 PDF 文件中提取文本内容和表格数据。使用场景:提取 PDF 文字、读取 PDF 内容、PDF 转文本、用户提及「PDF 提取」「读取 PDF」等。
# 技能 2:PDF 表单处理
name: pdf-form
description: 填写 PDF 表单字段,提取表单数据。使用场景:填写 PDF 表单、填充表单字段、PDF 表单数据处理、用户提及「PDF 表单」「填写表单」等。
- 5.使用”推动性”语言
description:
生成专业的 PowerPoint 演示文稿。当用户需要制作幻灯片、PPT、演讲稿、
汇报材料时,必须使用此 Skill,即使用户没有明确说"用 pptx 格式"。
只要涉及演示文稿的创建或修改,都应触发此 Skill。
长度: 简单技能50-100字符,中等复杂度技能100-200字符,复杂技能200-400字符
注意:描述不是越长越好。过长的描述可能包含过多无关信息,反而干扰代理的判断。
测试:
description写完后要测试,写大概20条贴近真实场景的用户提示词,并提前标注是否应当触发本技能,8‑10 条应当触发,8‑10 条不应当触发。比如说可以写:
- 1.基本功能测试:使用描述中包含的关键词进行测试,确认技能能够被激活。
- 2.边界情况测试:测试一些可能触发但不应该触发的场景。
需要触发的问题:
- 表达方式:部分正式、部分口语化,部分带拼写错误或缩写。
- 显式程度:一部分直接点明技能所属领域(“分析这份 CSV 文件”);另一部分只描述诉求,不提领域名词(“老板需要我根据这份数据文件生成图表”)。
- 信息详尽度:简短提示与信息丰富的提示混合使用,既有简短的 “分析我的销售 CSV 并生成图表”,也有包含文件路径、列名、背景信息的长文本。
- 任务复杂度:步骤与判断节点数量要有变化。既包含单步任务,也包含多步工作流;用来验证:当技能对应的需求被嵌套在一长串任务中时,智能体依然能识别出该调用此技能。
不需要触发的问题:
查询和skill关键词或概念相关的内容,但实际需要的是完全不同的能力,比如“我需要修改 Excel 预算表格里的公式”—— 同样提到 “电子表格”“数据”,但需求是编辑 Excel,不是 CSV 分析。
同一个问题建议问三次,应当触发的查询:触发率高于阈值(默认阈值 0.5)才算通过。不应当触发的查询:触发率低于该阈值才算通过。20 条查询,每条跑 3 次,合计就要执行 60 轮调用。因此需要脚本自动化完成这套流程。
优化:
有的本该触发却没有触发,有的不该触发却意外触发了。优化描述就是针对这些失败的测试用例进行迭代修改。
修复漏触发(该触发没触发)
- 补充意图示例:在描述中增加简短的意图样例,说明该技能会处理哪些类型的用户请求。不用写完整用户问句,简短短语即可。
- 明确适用场景:写明该技能适合解决哪一类问题、面向什么样的任务上下文。
- 重写能力表述:检查你对技能能力的描述,是否写得过于狭窄。避免把实现细节当成使用条件。
修复误触发(不该触发却触发)
- 增加排除条件:清晰写明哪些任务不属于本技能。
- 细化区分边界:说明本技能和其他相似任务之间的差异。
示例:“本技能用于分析 CSV 数据并生成图表;不用于编辑电子表格、写入数据库或运行通用 Python 代码。” - 收紧适用范围:移除描述中过于宽泛的表述。删掉并非本技能核心能力的笼统说法。
不要只针对少数几条失败用例过度调优描述。如果为了搞定个别用例把描述写得极度特殊化,会造成过拟合,推荐训练集60%,测试集40%。
根据失败用例修改技能描述。使用整套评估查询,重新跑完整套测试(每条多次运行)。统计新的触发率,观察假阳性、假阴性是否改善。重复循环,直到绝大多数测试用例达标。
整体流程:
写 description——准备 trigger / non-trigger 测试——运行测试——找误触发 / 漏触发——修改 description——重新测试——看validation
好的description = 用户意图 + 适用场景 + 隐含触发场景 + 清晰边界
一般先写body content,再写description,body content是告诉agent这个skill能干什么,description是告诉agent这个skill什么时候触发。
skill evaluation
写完body content和description后,skill就可以使用了,但是我们需要知道Skill到底有没有真的让结果变好。我们需要对其进行评估,评估不是简单的使用几遍后感觉良好,需要做结构化eval.
设计真实测试案例,每个 test case 包含:用户 prompt、你期待的结果、可选输入文件等。将同一个任务用skill和不用skill进行对比,每次使用干净的session。第一次结果输出后写assertion,用于后续机器判断,好的assertion能明确验证成功标准,不要过于死板,不要太模糊,比如:
- 输出包含 bar chart
- 图里正好有 3 个月份
- X/Y 轴都有标签
- 标题包含 revenue
之后可以通过机器和人工审核来判断,机器用于检测代码以及输出格式等是否正确,人工评审主观质量。机器判断可以根据断言和输出结果来打分,打分要有证据。还要结合tooken消耗量,时间,通过率等判断。判断后即可优化。assertion也很重要,关系着打分是否合理。
关于assertion优化:
| 情况 | 意味着什么 |
|---|---|
| 有 Skill / 没 Skill 都 PASS | Skill 对这件事可能没贡献(可以删除或者修改) |
| 有 Skill / 没 Skill 都 FAIL | 测试有问题,或者 Skill 根本没解决 |
| 有 Skill PASS、没 Skill FAIL | 这是 Skill 真正创造的价值 |
| 同一测试忽好忽坏 | Skill 指令可能模糊或模型随机性较大 |
优化 Skill
获取 3 类信号来修改 skill
- 失败断言:缺失步骤、指令模糊;
- 人工反馈:整体质量、体验问题;
- 执行 transcript 日志:定位 agent 为什么做错(忽略指令、无效步骤)。
把当前SKILL.md + 全部信号交给 LLM,生成修改建议,遵循原则:
- 做通用修复,不要只针对单个测试 case 打补丁;
- 保持 skill 精简,减少冗余指令;
- 多用 “原因解释” 式指令,少用强硬死板规则;
- 重复出现的脚本逻辑,抽入 skill 的scripts/目录。
优化后重复操作,同时和之前对比有无提升。
整体流程:
设计测试案例 → 建立评估标准 → Skill / 无 Skill 对比 → 自动 + 人工评估 → 分析失败原因 → 优化 Skill → 重新验证
scripts/
简单脚本比如一次性命令uv pipx等不需要写在这里面,直接在skill.md里面写即可,锁定版本,保证行为可复现,写明环境依赖即可。如果是复杂脚本,skill.md中写相对于skill.md根目录的相对路径,明确列出可用脚本。不要写交互式脚本,脚本要提供有用的错误信息,比如找不到文件,缺少必须参数等,优先结构化输出,建议每个脚本写一个—help.
scripts/目录下脚本应该尽量自包含,自包含脚本是指脚本中包含了所有必要的依赖,不需要外部环境支持。比如:
# /// script
# dependencies = [
# "beautifulsoup4",
# ]
# ///
from bs4 import BeautifulSoup
# 示例 HTML 内容
html = '<html><body><h1>Welcome</h1><p class="info">RUNOOB 测试</p></body></html>'
# 使用 BeautifulSoup 解析 HTML
soup = BeautifulSoup(html, "html.parser")
# 提取 class="info" 的段落文本
info_text = soup.select_one("p.info").get_text()
print(info_text)
进一步建议
- 幂等性:agent可能会重试执行命令。采用 “不存在则创建” 逻辑,比 “直接创建,重复时报错” 更加安全。
- 输入约束:遇到模糊不清的输入时,返回明确的错误提示予以拒绝,不要自行猜测处理。尽可能使用枚举类型与封闭值集合。
- 试运行支持:对于具备破坏性、会改变状态的操作,提供
--dry‑run(试运行)参数,让agent可以预览将要执行的变更,而不实际生效。 - 有意义的退出码:针对不同失败场景(资源不存在、参数非法、鉴权失败等)使用区分化的退出码,并在帮助输出(
--help)中说明各退出码含义,便于agent识别故障类型。 - 安全默认值:针对破坏性操作,应当评估风险等级,考虑是否需要设置显式确认参数(
--confirm、--force)或者其他防护机制。 - 可预期的输出大小:很多智能体运行框架会自动截断超出阈值(例如 10‑30K 字符)的工具输出,可能丢失关键信息。如果脚本会产生大量输出,默认返回摘要信息或者做合理输出限制;同时提供
--offset这类参数,供智能体按需获取更多数据。如果输出量大且不方便做分页处理,则要求调用方传入--output参数,指定输出文件;传入-代表主动选择输出到标准输出。
参数传递与接收
skill.md里面可以通过自然语言描述,告诉agent需要从上下文中提取哪些信息。agent 会根据这份说明,主动从上下文中提取信息,或在信息不足时向用户追问。比如:
---
name: csv-analyzer
description: 分析用户上传的 CSV 文件,输出统计摘要。当用户提到 CSV、表格数据分析时触发。
---
# CSV 分析器
## 输入要求
用户应提供以下信息(从对话上下文中获取):
- **文件路径**:已上传 CSV 文件的路径(位于 /mnt/user-data/uploads/)
- **分析目标**(可选):用户希望了解什么,如"找出空值"、"统计分布"
- **输出格式**(可选):表格、图表或文字摘要
若上述信息未明确,主动向用户确认后再执行。
也可以通过脚本传递,Skill 脚本在执行前,应始终验证关键参数是否合法。这可以防止因参数缺失或格式错误导致的运行时崩溃。参数验证的失败信息应清晰说明期望什么和实际得到什么,方便 agent 向用户反馈具体原因。