最近半个月,陆陆续续有朋友在群里聊 Agent Skills,我发现不少人第一反应是“给智能体加几个工具”,然后就开始堆 function calling,结果玩了两天就放弃了。这个理解不能说错,但确实把维度想低了。今天这篇我不聊概念 PPT,也不做框架横评,就从一个正在做 agent 项目的实践者角度,把 Agent Skills 是什么、为什么要把它当成独立层来设计、怎么落地、踩过哪些坑,尽量用大白话讲清楚。
这篇文章适合两类人。一类是已经在用 Claude 或 OpenAI API 做 agent 应用,但觉得项目代码越来越乱、技能越叠加越难维护的人;另一类是刚接触智能体开发,想快速搞懂“技能”这个抽象层到底在解决什么问题、跟工具调用的本质区别在哪的初学者。不管你是哪一类,读完第三部分你至少能照着思路,把一个最小可用的技能从零搭起来,并且理解它背后的设计取舍。
1. 先说清楚:“Agent Skills”到底在解决什么问题
1.1 从“会调用工具的模型”到“拥有技能库的智能体”
在早期的函数调用时代,我们习惯把每个能力封装成一个 function,比如search_news(keyword)、send_email(to, subject, body)。模型收到用户请求后,根据函数描述决定调用哪个函数,然后我们的代码去执行,再把结果回传给模型。这套模式解决的是“模型能触达外部系统”的问题,但它的短板也很明显——每个函数都是一次性的、上下文无关的调用,函数既不知道自己的最佳使用场景,也没有“经验”可以沉淀。
Agent Skills 的思路不一样:它把一组指令、脚本、示例、校验规则和资源文件打包成一个独立技能模块,让智能体像人一样“学会”一项完整工作,而不只是“执行一次操作”。简单说,function 是“拨一下转一下”,skill 是“给了一份操作手册加全套工具”。这个区别看起来不大,实际用下来差距非常大。
我举一个例子。你要让 agent 每周帮团队所有人写周报。如果走 function calling 路线,你得写好几个函数:get_week_events、get_tasks、format_report,还得在主程序里维护一个“什么时候该调用哪个函数”的状态机逻辑。换成 skill 路线,你会创建一个weekly-report技能,里面包含:取数据的脚本、周报模板、写作规范、几篇优秀样例,以及一段告诉模型“必须先看本周数据再动笔、结构必须符合模板、语气要偏业务口语”的说明。agent 拿到任务后自己会按手册走完整套流程,而不是你替它编排每一步。
1.2 技能、工具、插件:这三者到底有啥区别
很多文章把这三个词混着用,导致不少人学完更糊涂。这里我用一张表把区别拉清楚:
| 维度 | 工具 Tool | 技能 Skill | 插件 Plugin |
|---|---|---|---|
| 粒度 | 单次原子操作 | 一整套流程/方法论 | 技能+工具的聚合包 |
| 是否包含知识 | 通常不包含 | 包含指令、示例、模板、规范 | 可包含配置与多技能 |
| 是否带执行逻辑 | 有,后端代码实现 | 可内嵌脚本,也可靠模型执行 | 通常依赖外部实现 |
| 复用方式 | 代码级别复用 | 语义层面复用 | 产品级分发 |
| 典型例子 | get_weather(city) | weekly-report、pdf-analyzer | VS Code 插件、Chrome 扩展 |
一句话总结:tool 是技能里可调用的“肌肉”,plugin 是技能的“分发容器”,skill 则更像一份完整的“能力定义文档+执行资产包”。我们聊 Agent Skills 时,重点在中间这层。
1.3 技能层的核心价值:让经验沉淀下来
我见过不少团队,agent 跑得挺好,但整个系统完全依赖那个巨大的 system prompt。今天加一个规则,明天加一个格式说明,过两周 system prompt 变成 8000 token 的庞然大物,模型表现反而越来越随机。技能层解决的就是这个“经验沉淀”问题——每项工作技能独立成目录,有版本、有更新记录、有单独的功能边界,系统提示词只保留最基础的沟通规范,其他全部外包给技能。
这个思路跟程序员写函数其实一脉相承:你不会把所有业务逻辑全塞进一个几万行的 main 函数里,你会拆模块、做封装、搞复用。Agent Skills 就是在智能体世界里的“模块化”。它让每个能力可以单独测试、单独迭代、单独下发到不同 agent,而不是每次改动都牵一发动全身。这个价值,做过大型 agent 项目的同学应该感触特别深。
2. 拆解一个技能的内部结构:SKILL.md 才是关键
2.1 技能目录的通用布局
我自己的技能仓库一般长这样:
skills/ ├── weekly-report/ │ ├── SKILL.md │ ├── scripts/ │ │ ├── fetch_events.py │ │ └── format_markdown.py │ ├── assets/ │ │ ├── weekly_report_template.md │ │ └── examples/ │ │ ├── good_report_1.md │ │ └── good_report_2.md │ └── requirements.txt ├── pdf-summarizer/ │ ├── SKILL.md │ ├── scripts/ │ │ └── extract_pdf_text.py │ └── assets/ │ └── summary_template.md └── README.md每个技能目录的核心就是那个SKILL.md,其他脚本和资源都是它的“配套”。目录名就是技能名,必须短、清晰、一眼能看出用途,别搞skill_utils_v3_final这种自己都看不懂的命名。README 我建议写上每个技能的适用边界和依赖版本,不然过三个月你自己都不记得当初为什么写这个脚本。
2.2 YAML 元信息:决定智能体能不“发现”你
SKILL.md 最开始的部分一般是 YAML frontmatter,相当于技能的“身份证”。这部分决定 agent 在执行任务时能不能想到调用这个技能,写不好等于白做。
--- name: weekly-report description: 根据本周任务与会议生成结构化周报。当用户提到“周报”、“本周总结”、“weekly report”时使用。 categories: - productivity - reporting metadata: version: 1.2.0 author: your-name tags: [weekly, report, meeting-notes] ---name要跟目录名一致。description是重中之重,务必写清楚“什么时候该用”而不是“它做了什么”。举个例子,与其写“一个生成周报的工具”,不如写“当用户需要汇总一周工作、会议纪要与任务进展并输出 Markdown 周报时使用”。前者描述的是功能,后者描述的是触发场景,模型在规划阶段匹配触发场景的能力远比匹配功能描述要靠谱。
categories 和 tags 在高技能库数量较多时特别好用,有些框架会按分类裁剪搜索范围,能把匹配准确率拉高一个台阶。metadata 里的 version 我强烈建议维护,因为在技能迭代过程中你难免要回滚,没有版本号只能靠 git 历史硬翻。
2.3 指令正文的写作套路:测试驱动你写说明
正文部分反而是最容易被低估的。很多人写完 description 就开始堆脚本,正文里只丢一句“使用 scripts/fetch_events.py 获取数据”,结果模型根本不知道该怎么编排这些步骤。
我的建议是:把正文当成一本给新员工看的 SOP,而不是技术注释。至少包含四块内容:
- 任务目标:这个技能在什么场景下用、输出什么形式的结果。
- 执行步骤:从收集输入到加工到输出的完整流程,越具体越好。
- 规则与限制:哪些是绝对不能做的(比如“不要编造会议内容”),哪些是必须遵守的格式。
- 示例:至少给 2-3 个输入输出对,让模型照葫芦画瓢。
一个反例:“获取任务列表并按模板生成周报”。这句话对模型没有任何执行力,它不知道数据从哪获取、模板长什么样、格式要求是什么。正例则是类似下面这样:
# 周报生成技能 ## 目标 根据用户提供的时间范围,自动获取该周内的任务与会议记录,生成结构化 Markdown 周报。 ## 执行步骤 1. 运行 `python scripts/fetch_events.py --start YYYY-MM-DD --end YYYY-MM-DD`,获取原始事件列表。 2. 使用 assets/ 中的模板文件 weekly_report_template.md 确定报告结构。 3. 将事件按“项目推进”、“会议纪要”、“风险与问题”三类归组。 4. 运行 `python scripts/format_markdown.py` 将结果格式化为最终 Markdown。 ## 规则 - 严禁编造事件条目。原始数据不完整时,明确写出“数据缺失”。 - 每个项目必须写“进展”和“下一步”,不得省略。 - 输出语言默认跟随用户输入语言,模板中英双语。 ## 示例 ### 输入:... ### 输出:...写完之后,你自己就是第一测试者:把这段说明给一个没有见过代码的 agent,看它能不能按步骤跑通。跑不通就回去改说明,而不是改代码。这是“测试驱动写文档”的思路,实测比反复调整描述词有效得多。
2.4 辅助脚本与资源文件:技能真正的执行引擎
skills 定义了“怎么做”,但真正干重活的还是背后的脚本。脚本的重点不在于实现多复杂的逻辑,而在于两点:输入输出都结构化,错误信息足够友好。
我自己的习惯是,每个脚本都做成命令行可单独运行、参数可传、异常时打出人能看懂的提示,这样既方便调试,也方便模型在被中断时把错误信息正确反馈给你。拿前面那个fetch_events.py举例,它至少应该做到:
python scripts/fetch_events.py --start 2025-02-10 --end 2025-02-16 # 输出 JSON 到 stdout,错误时返回非零退出码并打印 stderr 说明这样模型只需要记得“运行命令 + 读输出”,不需要理解脚本内部怎么调 API、怎么解析数据。脚本越无脑,模型跑得越稳。资源文件(模板、示例)则建议用 Markdown,因为模型对这种格式理解最自然。PDF、Word 这类二进制格式不是不能放,但模型没法直接读,每次都要靠脚本先转成文本,链路越长越容易出错。
3. 手把手从零实现一个可复用的技能
3.1 场景选择与技能边界
纸上谈兵没意思,这里我拿一个可复现的小项目来讲透:做一个“会议纪要转行动计划”技能——输入一堆会议文字记录,输出一份带负责人、截止日期、优先级和风险标记的行动计划表。
选这个场景有三个原因:第一,它依赖的“工具”极简,不需要设计复杂的 API 对接,适合第一遍演示;第二,它是绝大多数团队每天都有的真实需求,你学完可以直接拿去用;第三,它的边界足够清晰——“把会议记录变成行动计划”,不涉及权限、不做知识库搜索、不碰多轮对话状态,模型不容易跑偏。
3.2 搭建目录与初始文件
按上面的通用布局,先创建目录:
mkdir -p meeting-actions/scripts meeting-actions/assets然后写一个最简版SKILL.md。新手最容易犯的错是一上来就想把文件写完美,我建议先写一个 60 分的版本,跑通流程后再迭代。首次我一般只写目标、执行步骤和一条规则。
3.3 编写 SKILL.md:从草稿到完整版
第一版大概长这样:
--- name: meeting-actions description: 将会议记录转化为带负责人、截止日期的行动计划表。当用户提供会议记录并要求“生成待办”、“下一步行动”、“行动计划”时使用。 categories: [productivity, project-management] metadata: version: 0.1.0 --- # 会议纪要转行动计划 ## 目标 把原始会议记录转换成 Markdown 格式的行动计划表。 ## 执行步骤 1. 阅读用户提供的会议记录全文。 2. 提取所有行动项,每条行动项必须包含: - 行动描述 - 负责人(若原文未指明则写 TODO) - 截止日期(若原文未指明则写 TBD) - 优先级:高/中/低,根据上下文判断 3. 按优先级从高到低排序输出。 4. 输出格式参照 assets/action_template.md。 ## 规则 - 不得新增原始记录中不存在的行动项。 - 不得臆断负责人或日期,必须明确标注 TODO/TBD。 - 输出表格中必须包含“风险提示”列,若存在风险则简述,否则写“无”。模板文件assets/action_template.md我写了一个极简版:
| 行动描述 | 负责人 | 截止日期 | 优先级 | 风险提示 | | --- | --- | --- | --- | --- | | ... | ... | ... | ... | ... |这一版已经能跑了,但还缺少示例。示例在这类技能里特别关键,因为“提取行动项”有多种风格,有的会议记录是一段话,有的是一长串 bullet points,模型需要从示例里学“如何处理不同格式”。于是我在 SKILL.md 里加了两组示例:
## 示例 ### 输入 “关于 Q1 发布,王丽负责完成新用户引导文档,3 月 15 日前。李强在 3 月底前完成灰度环境搭建。另外法务反馈隐私协议需要修订,暂时没明确负责人。” ### 输出 | 行动描述 | 负责人 | 截止日期 | 优先级 | 风险提示 | | --- | --- | --- | --- | --- | | 完成新用户引导文档 | 王丽 | 03-15 | 高 | 无 | | 完成灰度环境搭建 | 李强 | 03-31 | 高 | 依赖法务协议修订 | | 修订隐私协议 | TODO | TBD | 中 | 无负责人,需尽快指派 |加不加这一步,模型输出质量差距非常明显。没有示例的时候模型经常把“法务反馈隐私协议需要修订”漏掉,因为它是一句“背景描述”而不是典型的“行动指令”;有了示例之后,模型至少知道这类句子也应该尝试提取成行动项。
3.4 注入本地环境,跑通第一个完整流程
不同框架注入技能的方式略有差异,但底层逻辑差不多,都是把技能目录映射到一个可访问路径,然后让模型在规划时读取 SKILL.md。我用的是本地环境模拟的方式,相当于手动模拟 agent 的“技能加载”环节,方便调试。
export SKILL_PATH="/path/to/meeting-actions" cat "$SKILL_PATH/SKILL.md" # 这句话模拟 agent 在执行任务前先“阅读技能手册”的动作然后我把 SKILL.md 的内容和用户输入的会议记录拼在一起,作为一次对话发给模型。这一步相当于一个最小可用的 agent 流程,跑通后再接入具体框架或运行时。实测下来,这一步能帮我隔离绝大多数问题——如果直接拼在 prompt 里都跑不通,那就别指望放到框架里能自动跑通。
3.5 复盘升级:让技能越用越准
第一次跑通之后,技能还远没到“完成”的状态。我每次跑完都会问自己三个问题:哪些输出不对?说明里漏了什么?示例是不是还不够有代表性?
举个例子,我第一版跑下来发现,当会议记录里全是“同步一下进度”“更新一下文档”这种模糊表达时,模型会乱猜负责人。我就在规则里加了一条“若行动描述本身不明确或过于口语化,负责人在原文无法对应时,输出 TODO 并在风险提示列标注‘需澄清’”。同时新增一组“模糊表达”的示例。技能就是从这些真实反馈里一点点磨出来的,一开始写得多完美,不如后边迭代得多勤快。
4. 常见问题与排查技巧实录
4.1 症状速查表:技能“失灵”的四种典型表现
实操中遇到问题,第一反应不是改代码,而是先定位问题属于哪一层。下面这个表是我自己总结的定位清单:
| 症状 | 常见原因 | 优先排查方向 |
|---|---|---|
| 模型完全没调用技能 | description 描述场景不对,或触发词不匹配 | 改 description,加入用户真实会说的短语 |
| 调了技能但输出格式不对 | SKILL.md 正文规则不够具体,缺示例 | 补充格式示例,明确“必须”与“禁止” |
| 调用后报错,且反复重试失败 | 脚本输入输出不规范,边界处理弱 | 命令行手动执行脚本,查看错误信息 |
| 输出内容“好像对,但信息残缺” | 规则里没定义缺失信息处理方式 | 增加 TODO/TBD/数据缺失等兜底规则 |
4.2 描述质量不够,技能成了摆设
这是最常见的问题,没有之一。很多人把 description 写成“A tool that converts meeting minutes into action items”,模型看完根本不知道什么时候该用它。你说“帮我整理一下今天的会议内容”,它不会联想到这个技能,因为“整理会议内容”和“converts meeting minutes”之间是语义跳跃的。
解决办法是回到用户视角:搜集真实需求表达,把用户可能说的 10 种说法都映射到描述里。比如“生成下一步计划”“把会上的决定列出来”“谁负责什么整理一份表”都可能是用户原话。description 里不需要把所有说法堆进去,但至少要涵盖最典型的几种表达。我的习惯是描述里用户口语在前、功能描述在后:
description: 根据会议记录整理行动项。当用户说“整理会议待办”、“下一步谁负责什么”、“把会议决定列成表”时使用,把原始记录转为带负责人、截止日期、优先级的行动清单。4.3 Token 预算失控:指令越长越不可控
很多技能文档越写越长,最后 SKILL.md 快 2000 token,每个技能点都展开写,结果模型执行时被大量信息淹没。技能文档需要精简,但精简不是删内容,而是把“必须做什么”和“背景参考”分开。
我的做法是:核心执行步骤、规则和示例控制在 800 token 内;更详细的背景说明、设计取舍、常见错误案例放到assets/best_practices.md,并在 SKILL.md 里注明“只有在遇到异常情况时才阅读该文件”。这样大多数执行过程模型只看精简版,遇到边界情况才去深入阅读,Token 消耗和稳定性都能保住。
4.4 环境与权限:本地技能的安全底线
技能里带了脚本,就带来两个问题:环境依赖和权限隔离。先讲依赖,技能用到的 Python 包必须在requirements.txt里显式声明,并且脚本启动时最好做一次依赖检查,缺包就报“missing dependency: pandas”,别让模型去猜“cannot import pandas”到底什么意思。
权限方面,我的底线是:技能脚本默认不读取工作目录之外的文件,不写全局配置,不在未经用户确认的前提下发送网络请求。尤其是“发送邮件”“更新数据库”这类有副作用的操作,必须在 SKILL.md 里写明“执行前必须向用户确认”的硬规则。这些规则不是防模型,而是防误操作带来的连锁反应。你永远不想遇到一个“帮我整理会议记录”直接给你把任务管理工具里的数据全部重排的场面。
5. 关于 Skill 开发的几点经验心得
最后分享几个个人体会,不算总结,就是纯粹的使用感触。
第一,技能之间要保持低耦合。每个技能只负责一个清晰的任务域,别做一个“什么都能干”的超级技能。我的项目里曾经有人写了个general-utils,里面既有写周报的脚本又有解析 PDF 的逻辑,最后模型经常选错入口,debug 到怀疑人生。拆开之后一切恢复正常。技能和函数一样,做薄做专,才能稳定复用。
第二,把技能当成产品来迭代,而不是写一次就完事的脚本集合。技能本身有“用户”(大模型)、“输入”(任务)和“输出”(结果),你认真对待它,它就认真回报你。我维护一套技能库半年下来,最明显的变化是:新 agent 接入时不用再从零训练和调 prompt,直接把技能挂上去就能干活,效率提升是全队的。
第三,多花时间在 example 上,而不是规则堆砌上。模型从两三个好例子中学到的东西,经常比二十条文字规则都管用。规则太多反而互相打架,例子多了模型自然能总结出模式。这一点我在多个技能上反复验证过,值得一试。
如果你也正在做 agent 项目,我建议从今天起就把技能单独建库,先拿一个真实场景练手,不要贪多。第一个技能上手了,第二个、第三个就是流水线的事。