AI Agent Skill(智能体技能)现在是AI Agent开发里出现频率最高的词之一,但很多人把它当成一段提示词,或者当成普通插件的别称。这个误解会在后面带来一个很直接的问题:模型到底什么时候该用Skill、用错了怎么排查,完全理不清。如果你正准备入门AI Agent开发,或者已经写了几个Agent但总感觉能力复用很乱,我建议先花一篇文章把Skill的定位、组成和设计方法理解清楚。
这篇文章不是从某个框架的官方文档翻译出来的,而是把“理解Skill”这件事拆成几个可以直接用的层面:它在Agent运行逻辑里的位置、它由哪些部分组成、怎么从零定义一个Skill、接入Agent时怎么判断它是否正常工作、新手最容易踩哪些坑。读完你可以拿一个最简单的任务自己跑通一遍。
1. 理解Skill,先看它在Agent运行逻辑里的位置
1.1 Agent为什么会需要“技能”
一个Agent从外部看,很像一个“能自己干活”的程序。但把它拆开看,核心仍然是大模型在做理解和决策。大模型擅长的是语言生成、语义匹配、常识推理,并不擅长稳定执行一套确定的操作步骤。
举个例子。你让一个大模型把一段Markdown表格转成CSV文件。如果不做任何约束,它可能会给你一段Python代码,也可能直接输出一个看起来像CSV的文本块,甚至会在文本里加一句“这是转换结果”。原因是模型在“自由生成”,不是在“执行任务”。
这里的矛盾就在于:Agent要稳定,就必须减少大模型的自由发挥空间。Skill就是用来补这部分确定性的。
我一般会把Skill理解成“给模型的一块能力封装”。模型不需要知道内部怎么实现,它只需要知道:什么情况下可以调用这个能力、需要传哪些参数、调用后返回什么结果。真正的执行逻辑,比如脚本、命令、API请求,都封装在Skill内部。
1.2 Skill、Tool、Plugin、Workflow之间的边界
很多人一上来就混淆这几个概念,这里先给一个通用边界。
- Tool,粒度更细。它通常是一个单一动作,比如“读取文件”“调用某API”“执行一条SQL”。模型在对话过程中按需调用。
- Skill,粒度在Tool之上。它通常对应一个完整任务,比如“把Markdown转成CSV”“把文章批量生成摘要”“整理一份会议纪要”。Skill内部可能包含多个步骤,也可能在内部调用Tool。
- Plugin,更偏平台侧的扩展机制。不同框架对Plugin的定义差别很大,有的Plugin只是一个打包分发单元,里面可以包含多个Skill或Tool。
- Workflow,强调流程编排。它的执行路径往往是固定的、可预测的,适合“每步做什么都很明确”的业务场景。Skill则更强调“让模型根据场景按需调用”。
这里可以看一张简表:
| 概念 | 粒度 | 通常包含的内容 | 解决的问题 |
|---|---|---|---|
| Tool | 细 | 一个函数、一个接口调用 | 单一动作 |
| Skill | 中 | 描述、输入输出协议、执行逻辑、校验 | 完整任务 |
| Plugin | 偏平台 | 多个能力或资源的打包分发单元 | 能力集成 |
| Workflow | 大 | 节点、分支、状态流转 | 固定业务流程 |
但要注意,这只是一个为了帮助理解而画的通用边界。实际不同Agent框架里,Tool和Skill的边界不一定这么清晰,有的框架里Skill就是一组Tool的组合,有的框架里Plugin和Skill是同一个概念。落地时一定要以具体框架文档为准。
1.3 为什么不能把Skill当成一段提示词
这是新手最容易犯的错误。
提示词的本质是“通过语言影响模型行为”。它只能改变模型下一步输出的概率分布,不能保证模型一定按规则执行。你可以在提示词里写“你必须调用某个工具”,模型可能调用,也可能不调用。你可以在提示词里写“输出必须是JSON”,模型可能输出带说明文字的JSON,也可能直接跑偏。
Skill则不一样。它的执行部分不是模型“想”出来的,而是提前写好的脚本或命令。模型只负责选择是否调用、传入什么参数,真正的动作由程序完成。这样就把“不稳定的推理”和“稳定的执行”分开了。
纯提示词方案还有一个问题:不好测试。你很难给一段提示词写单元测试,但你可以给一个Skill写测试用例。这一点在Agent项目复杂起来之后尤其重要。
2. 把一个Skill拆开看,它到底包含什么
2.1 元数据和描述:让模型知道“什么时候用”
一个Skill首先要能被Agent框架发现,并且能在大模型的工具选择阶段做出正确决定。所以它通常需要三样最基本的信息:名称、描述、版本。
名称要短,语义要清晰。比如markdown_to_csv就比mdcsv更容易让模型理解。不要用脱离职责的代号。
描述是最关键的部分。它不只是给人看的,更是给模型看的。描述写得好不好,直接决定模型在遇到相关任务时会不会选中这个Skill。
我一般会把描述写成三段式:
- 用途:这个Skill能做什么。
- 使用条件:出现什么特征时应该调用。
- 不适用场景:出现什么特征时不应该调用。
比如:
把Markdown格式的表格转换为CSV文件。 当输入内容中包含Markdown表格且用户需要导出为表格文件时使用。 如果输入只是普通文本列表,没有表头或分隔线,不要使用。这样写比“Markdown转CSV”好用得多。因为模型做选择时,需要的是“条件匹配”,不是单纯的关键词匹配。
2.2 输入输出协议:让调用不出歧义
Skill要能被模型正确调用,必须把输入输出定义清楚。
输入部分通常用JSON Schema描述。要写明每个字段的类型、是否必填、默认值、字段含义。如果输入是文本,要明确传原文还是传文件路径。如果输入是文件,要明确文件路径规则。如果字段定义模糊,模型就会猜,一猜就容易出错。
输出部分同样重要。不少新手只关注输入,忽略了输出协议。结果Skill执行成功了,但Agent拿不到结构化结果,仍然无法继续处理。输出至少要约定:成功时返回什么、失败时返回什么错误码和错误信息。
一个比较完整的基础输入协议,长这样:
{ "type": "object", "properties": { "markdown_text": { "type": "string", "description": "包含Markdown表格的原文" }, "output_path": { "type": "string", "description": "输出的CSV文件路径" } }, "required": ["markdown_text", "output_path"] }字段越明确,模型填参时越不容易踩坑。尤其是字段的description,看起来不是代码,但对调用成功率影响很大。
2.3 执行逻辑:真正干活的代码
Skill的描述部分只负责“让模型理解”,真正的执行部分必须是确定性的脚本、命令或API调用。
写执行逻辑时,需要注意几点:
- 路径要稳定,尽量基于Skill目录的相对路径,不要写死一个绝对路径。
- 日志要可读。脚本执行成功或失败,都要在标准输出或日志文件里有明确体现。
- 错误要可捕获。不要遇到异常就静默退出,要返回明确的错误信息。
- 资源占用要可控。如果处理的是大文件,要考虑内存和耗时。
这里也不是代码越复杂越好。一个Skill最好只做一件事,不要塞进七八个功能。否则出问题时,你很难判断是哪个环节失败。
2.4 校验和测试:保证可复用
Skill要能被反复调用,就必须有校验和测试。
测试用例至少要覆盖三类输入:
- 正常输入:确认输出结果正确。
- 边界输入:比如空字符串、只有表头、分隔行缺少等。
- 错误输入:比如格式完全不是Markdown表格。
操作顺序我建议这样:先在命令行单独跑脚本,确认脚本本身能输出正确结果。再通过Agent触发Skill,确认模型能正确传入参数。最后连续跑多次,确认结果稳定。
如果Skill输出不稳定,先看日志,不要急着改描述。先确认执行逻辑本身有没有问题,再考虑是不是模型选错或参数传错。
3. 从零定义一个“Markdown转CSV”Skill
这一节用一个最简单的任务演示完整流程:输入Markdown表格文本,输出CSV文件。任务不大,但足以把Skill的定义、配置、执行、测试链路跑通。
3.1 先定任务边界
不要上来就写脚本,先把边界说清楚。
这个Skill接收什么:一段包含Markdown表格的文本。输出什么:一个CSV文件。只处理标准Markdown表格,也就是包含|分隔和表头分隔行的表格。不处理没有分隔线的普通文本列表,不处理复杂嵌套表格。
边界越清晰,后续写描述和写脚本都越轻松。很多Skill做不好,不是代码问题,是任务边界一开始就模糊。
3.2 定义输入输出
输入字段就是两个:
markdown_text:Markdown原文。output_path:输出CSV路径。
输出结果约定为:
- 成功:输出
OK: wrote N rows to <path>。 - 失败:标准错误输出
ERROR: no markdown table found,退出码为1。
这样Agent在调用后,可以明确判断成功还是失败。
3.3 写Skill描述
描述可以这样写:
把Markdown格式的表格转换为CSV文件。 当输入内容中包含以|分隔的Markdown表格,且用户需要导出为CSV时使用。 输入必须是完整的Markdown原文,输出路径必须是带.csv后缀的文件路径。 如果输入只是普通列表,没有表头分隔行,不要使用。这里有一个经验:描述里的“不要使用”不是废话,它能避免模型在模糊场景下误调用。实际测试中,加了“不适用场景”之后,误触发率会明显下降。
3.4 写执行逻辑
下面是一个最小可运行的Python脚本示例。注意这是为了演示,只支持简单的Markdown表格,没有处理转义符。
import argparse import csv import re import sys def parse_markdown_table(text: str): rows = [] for raw_line in text.strip().splitlines(): line = raw_line.strip() if not line.startswith("|"): continue cells = [cell.strip() for cell in line.strip("|").split("|")] if all(re.fullmatch(r":?-{2,}:?", cell) for cell in cells): continue rows.append(cells) return rows def main(): parser = argparse.ArgumentParser() parser.add_argument("--input", required=True, help="Markdown table text") parser.add_argument("--output", required=True, help="CSV output path") args = parser.parse_args() rows = parse_markdown_table(args.input) if not rows: print("ERROR: no markdown table found", file=sys.stderr) raise SystemExit(1) with open(args.output, "w", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerows(rows) print(f"OK: wrote {len(rows)} rows to {args.output}") if __name__ == "__main__": main()这个脚本做了四件事:按行拆分、过滤非表格行、跳过Markdown分隔行、写入CSV。
真实生产环境里,Markdown表格格式会更复杂,比如单元格内包含转义竖线、对齐标记、多行内容等。这个脚本只是一个起步版本,能帮你理解Skill执行逻辑的形态。
3.5 单条测试
先在命令行单独跑一遍:
python md_to_csv.py \ --input "| 名称 | 数量 | | --- | --- | | 苹果 | 3 | | 香蕉 | 5 |" \ --output out.csv跑完后打开out.csv,应该看到三行有效内容:表头、苹果行、香蕉行。
这一步很重要。脚本没验证成功之前,不要放到Agent里去调,否则你分不清是脚本问题还是模型传参问题。
3.6 放入Agent验证触发
脚本跑通后,再把它加进Agent的Skill配置里。完整配置结构在不同框架里不一致,但大致会有下面这些信息:
name: markdown_to_csv description: 把Markdown表格转换为CSV文件 version: 1.0.0 input_schema: type: object properties: markdown_text: type: string description: 包含Markdown表格的原文 output_path: type: string description: 输出的CSV文件路径 required: - markdown_text - output_path execute: command: python scripts/md_to_csv.py启动Agent后,给一句用户指令,比如“帮我把这段Markdown表格转成CSV,保存到/data/result.csv”,然后观察日志。
如果模型没有选中这个Skill,不要急着改代码,先看日志里到底发生了什么。
4. 把Skill接入Agent时需要注意什么
4.1 加载方式和目录约定
不同Agent框架对Skill的加载方式不一样。有的框架里Skill放在skills目录,有的放在plugins目录,有的通过manifest.json声明。不要假设所有框架都一样。
学习阶段,最快的方式是找一个已有示例,看清它的目录结构、配置文件字段、脚本入口,然后照着复制一份改成自己的任务。
还有一个很容易忽略的点:配置加载成功不等于模型一定调用。你应该通过框架提供的能力,查看当前已加载的Skill列表,确认自己的Skill确实进入了候选集。
4.2 描述对触发成功率的影响
模型选择Skill,本质是一个概率决策。描述越模糊,误选和漏选概率越高。
我给你一个对比:
| 弱描述 | 强描述 |
|---|---|
| 把Markdown转成CSV | 把Markdown表格转换为CSV文件;当输入包含表格且需要导出为CSV时使用 |
| 处理Excel | 把xlsx文件中的数据读取并整理成结构化JSON;当用户需要提取Excel内容时使用 |
| 生成摘要 | 对长文本生成200字以内的中文摘要;当输入文本超过500字且需要快速了解核心内容时使用 |
不要觉得“处理Excel”已经够清楚了。对模型来说,“处理”这个词太泛,它不知道是读取、修改、合并还是转格式。描述里必须有明确的触发条件和输出目标。
4.3 日志怎么看
把Skill接入Agent后,最需要盯的是日志。我一般会按这个顺序看:
- 有没有出现Skill名称的匹配记录。
- 模型传入的参数是不是符合输入协议。
- 执行脚本有没有报错。
- Agent有没有正确拿到输出结果。
如果第一步就没有匹配记录,先改描述,不要动执行逻辑。如果第二步参数不对,检查输入字段的类型和description是否清楚。如果第三步报错,单独在命令行跑脚本复现。如果第四步失败,大概率是输出协议和Agent的解析逻辑不匹配。
这个排查顺序能避免一个典型问题:明明脚本没问题,却因为模型没选中Skill,导致你反复改代码浪费时间。
4.4 安全与权限边界
Skill能执行命令、读写文件、调用API,能力越强,越要控制权限。
不要直接加载来源不明的Skill文件,尤其是只给了一个压缩包、没有任何文档的Skill。使用前至少确认它执行了什么命令、访问了哪些文件。
给Skill传参时也要做校验。如果参数会拼进命令行,一定要防止注入。不要把用户输入的原始字符串直接作为命令执行。
在团队项目里,Skill变更应该像代码变更一样走评审。别让一个Skill悄悄带着高风险命令进入生产环境。这一点,很多个人项目不会遇到,但一旦做生产级Agent,就是必须考虑的边界。
5. 新手设计Skill最常见的误区和排查思路
5.1 误区一:Skill越大越好
有人觉得一个Skill能处理的事情越多越强大。实际恰恰相反。Skill职责越单一,模型越容易判断“什么时候该用”,调试时也越容易定位问题。
如果一个Skill描述里要写四五种不同的用途,说明它该拆分了。比如“处理文档”这个Skill,实际上应该拆成“提取PDF文本”“Markdown转HTML”“生成文档摘要”等多个Skill。
5.2 误区二:描述随便写写就行
描述是模型选择Skill的依据,本质上是一种接口文档。描述写得太糙,模型会漏选或误选。
改进方法很简单:给描述增加“使用条件”和“不适用场景”。不要只写“这个Skill能做什么”,还要写“什么情况下必须用”和“什么情况下千万别用”。
5.3 误区三:只写提示词,不写执行逻辑
如果一个Skill只有一大段提示词,没有真正的脚本、命令或API调用,那它本质上还是Prompt,不是Skill。
确实存在一些“纯提示词Skill”,但它们的适用范围很窄,通常只负责输出格式约束,不负责执行动作。凡是涉及文件读写、数据转换、外部系统调用,都应该有明确执行逻辑。
5.4 误区四:不做测试就上线
Skill和普通函数一样,必须有测试。再简单的Skill,也至少要有一个正常样例、一个边界样例、一个错误样例。
没有测试的Skill,可能在第一次调用时看着正常,第二次换一种输入就翻车。等Agent在真实场景里失败时,你连回归验证的手段都没有。
5.5 误区五:忽略输出格式
输入协议写得很细,输出却只有一个“成功”或“失败”,这是常见问题。
Agent拿到输出后还要继续处理,如果输出格式不统一,后续流程很难写。比如输出CSV时,不仅要告诉Agent“文件写好了”,还要给出路径、行数、字段列表。这些会成为Agent后续判断的上下文。
5.6 通用排查顺序
当Skill表现不符合预期时,我建议按以下顺序排查:
- 先看日志中是否出现该Skill的调用记录。如果完全没有,说明模型没选它,优先改描述。
- 再看传入参数。参数为空、字段传错、类型不对,优先检查输入协议和字段描述。
- 再看执行日志。脚本有没有报错、有没有超时、有没有输出异常信息。
- 再看输出结果。结果是否符合输出协议,Agent能否正确解析。
- 最后看安全策略。有些框架或环境会拦截命令、限制文件写入,导致执行被阻断。
很多看起来是“功能问题”的故障,实际都是描述问题或参数问题。不要总是怀疑框架有Bug,先按链路逐层看。
6. 从理解Skill到持续迭代
6.1 先用最小版本跑通
我第一次接触Skill时也犯过类似错误:一上来就想做一个能处理十几种文档格式的复杂Skill,结果光是配置就写了一堆,最后模型还没调通。
现在我更建议换个顺序:先做一个极小极简单的Skill,比如读取一行文本、转成大写、写入文件。先把“描述-入参-执行-输出-反馈”这条链路跑通,再逐步增加复杂度。
链路跑通之后,你已经掌握了Skill的基本骨架。后面再设计复杂能力,无非是往里加执行步骤、加校验、加API调用。
6.2 把Skill当代码维护
Skill不是“写一次就完事”的配置。它会随着Agent需求变化不断调整描述、参数和脚本。
要像维护代码一样维护Skill:
- 放进版本管理,Skill目录和Agent代码放一起。
- 留版本号,方便回滚。
- 写简短的README,说明这个Skill解决什么问题、依赖什么环境。
- 每次修改描述或脚本后,重新跑一遍测试样例。
如果你的Agent项目里有十几个Skill,没有版本管理会非常痛苦。你很难知道某个Skill是什么时候改的、为什么改、当前版本是否可复用。
6.3 关注Skill生态变化
Skill概念现在还在快速演进中。不同框架对Skill的定义、加载方式、编写规范都存在差异,市面上也没有一个完全统一的标准。今天学到的通用思路,落地到不同框架时可能需要做适配。
我的建议是:不必追求一步到位,先把一个Skill的全流程理解透彻。等生态更清晰后,再根据具体平台做迁移和扩展。
如果你正在做Agent相关项目,可以一边学一边积累自己的Skill库。每完成一个能稳定运行的Skill,就保存下来,后续新项目直接复用。时间长了,你会发现自己真正沉淀下来的不是某段代码,而是一套判断“什么时候该封装、怎么封装、怎么验证”的能力。