做 agent 开发有段时间了,最近圈子里讨论最密的一个词就是 agent skills。简单说,它就是给 AI agent 加上一批可复用的专业技能包:遇到特定任务时,agent 会读取技能描述,调用对应的脚本、模板或流程,把原本靠“临时发挥”的对话变成“按套路办事”的稳定能力。我身边不少从 agent 框架、Claude Skills 一路折腾过来的朋友,最近都在聊怎么把技能包设计得更好用。这篇就聊聊我对 agent skills 的理解、开发路径和一些踩过的坑,适合想给 agent 加技能、正在做 agent 框架选型,或者单纯好奇“skills 和 tools 到底啥区别”的读者。
1. agent skills 是什么:从“会说话”到“会干活”
1.1 从一个尴尬场景说起
我在做一个内部流程 agent 的时候,发现模型虽然能听懂“把这份报表转成 PDF”,但每次生成的结果格式都不一样:有时候加密,有时候不加密,有时候页边距很怪,甚至有一次直接输出了一堆 Markdown 文本而不是文件。后来我把“转 PDF 的完整操作步骤加上参数清单”写成一个说明文件,再配一个小脚本,agent 遇到需求时自动读取并执行,输出瞬间稳定了。
这就是 agent skills 的价值:把反复出现的任务固化成可复用、可验证、可分享的能力单元。你可以把它理解成一个“插件包”,但又不完全是插件,因为它不只是代码,而是“模型如何理解任务 + 如何执行任务”的组合体。具体来说,一个技能包通常包含一段给模型看的自然语言说明书,以及一组用来实际干活的脚本或模板。模型负责判断“现在该用哪个技能、怎么用”,脚本负责保证“输出的结果是确定性的”。
1.2 skills 和 tools、插件、工作流到底有什么边界
很多朋友容易把 skills 和 tools 划等号,我一开始也这样。后来在架构设计里反复琢磨,才理清它们之间的相对关系:
- tools 更像是 agent 可以调用的“函数”,颗粒度小,有明确的输入输出,比如发一个 HTTP 请求、读一个文件、执行一段代码。
- skills 是面向“一个完整任务的能力包”,内部可能包含多个 tools、提示词模板、脚本、校验规则,甚至包含子技能。
- plugin 或 workflow 往往更强调平台集成和流程编排,而 skills 更强调“模型如何知道在什么时候用它”。
这个边界不是行业严格标准,不同 agent 框架的实现差异很大。比如有的框架把 skills 直接实现为“带描述的 tool”,有的框架则做成独立的“技能加载器”。但理解这个分层能帮助你设计技能:不要把 skills 做成一个巨大的 tool,也不要把 tools 硬塞进技能里,而是按职责划分。
1.3 为什么“skills”突然成了热门话题
模型能力之外,工程化需求是主因。单个提示词或工具已经无法覆盖复杂任务,比如“整理会议纪要并生成待办事项”这种任务,既需要理解语义,又需要调用日历接口、写文件、格式化输出,如果全靠 agent 临时组合,很容易出错。这时候把“知识、脚本、校验、示例”打包成一个技能,就能大幅提升稳定性。
另一方面,Claude Skills、Codex skills 这类项目把技能包做得像应用商店一样容易安装,社区里开始出现各种 skills 下载平台、推荐清单和教程。甚至有的 agent 框架已经开始内置“官方市场”,你可以一键安装别人写好的技能包,也可以把自己的技能包提交上去。生态起来了,自然讨论就多了。
2. 从零开发一个 agent skill:完整实操过程
2.1 最小技能包的标准结构
以社区常见的约定为例,一个最小技能包通常包含三个部分:
SKILL.md:技能说明文件,用自然语言写清楚技能名称、适用场景、触发条件、使用步骤、注意事项。scripts/:一个或多个可执行脚本,负责真正“干活”,比如生成文件、调用接口、做数据转换。assets/:模板、数据、参考文档等静态资源,供模型或脚本读取。
这种结构为什么会流行?因为它对模型很友好:SKILL.md是自然语言写的索引,模型可以直接理解“什么时候该用、怎么用”;脚本是确定性的执行逻辑,一旦运行,结果不会因为模型心情变化而漂移。两者配合,既灵活又可控。
提示:目录命名推荐用 kebab-case,比如
pdf-converter、web-card-generator。不要用空格和中文路径,很多框架在解析技能目录时会因为路径问题加载失败。
2.2 三步写出第一个可用技能:以“生成网页卡片组件”为例
我们做一个真实能用的前端开发技能,目标:让 agent 根据用户描述生成一个卡片组件的 HTML + CSS 片段。
第一步,写SKILL.md:
# Card Generator 生成响应式卡片组件的 HTML/CSS 代码。 适用场景:用户提到“卡片”“card”“组件”“产品卡片”“用户卡片”等词。 使用步骤: 1. 分析用户描述,确认卡片用途、尺寸、主色调。 2. 参考 assets/card_template.html 中的基础结构。 3. 生成可独立运行的 HTML 片段,内联 CSS 样式。 4. 自检:确认包含 card__header、card__body、card__footer 三个必须类名。 注意事项: - 所有样式类使用 card- 前缀,避免污染全局。 - 不要使用 alert(),不要内联事件处理器。 - 如果用户未指定颜色,默认使用 #4F46E5。第二步,在assets/card_template.html里放一个基础模板,方便模型参考:
<div class="card"> <div class="card__header">标题</div> <div class="card__body">内容区域</div> <div class="card__footer">操作按钮</div> </div> <style> .card { border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; max-width: 360px; } </style>第三步,写一个校验脚本scripts/validate.sh,检查生成的代码里是否包含必须类名:
#!/bin/bash # 校验生成的卡片组件代码 input_file=$1 for class_name in "card__header" "card__body" "card__footer"; do if ! grep -q "$class_name" "$input_file"; then echo "缺少必须类名: $class_name" exit 1 fi done echo "校验通过"把整个文件夹放到 agent 的技能加载目录,重启会话测试。当你对 agent 说“帮我生成一个介绍产品的卡片组件,主调蓝色”,它读取SKILL.md后,会按照步骤生成带正确类名的组件。我实测下来,加了这套结构后,生成的组件代码基本能直接用了。
2.3 安装技能包:手动拷贝、Git 拉取与市场安装
不同 agent 框架对技能安装的方式差别很大,但大体可以归为三类:
- 手动拷贝:把技能文件夹直接放到 agent 配置的
skills目录下。简单直观,适合个人本地调试。缺点是没有版本管理,改坏了只能靠备份。 - 命令安装:比如某些 CLI 工具支持
skills install <git-url>,从远程仓库拉取技能包并自动放到正确位置。适合团队协作,版本可追溯。 - 市场安装:从框架内置的官方市场或第三方技能平台搜索、安装,比如“装一个 GitHub 协作技能”“装一个 PDF 处理技能”。适合快速使用现成能力。
我的建议是:团队内部技能包用 Git 仓库管理,配合 Tag 打版本,依赖清晰;个人实验就用手动拷贝,改起来最快。对外分享时,再考虑发到公共技能平台,让更多人使用。
2.4 前端开发类 skills 的细节优化
前端开发是 skills 里比较热门的方向,但它有几个典型的坑,我一个个说。
第一,模型生成的代码容易“太有自己的想法”。你让它写个按钮,它可能给你写出一套 BEM + CSS Variables + 动画库的复杂结构。解决方法是在SKILL.md里明确技术栈和样式方案,比如“使用 Tailwind utility class,不要使用自定义 CSS 文件”,或者“所有样式内联,禁止引入外部框架”。
第二,缺少可访问性约束。很多模型组件没有 aria-label、没有 alt 属性、对比度也不达标。可以在 SKILL.md 里加一条基础要求:“所有交互元素必须包含可访问性标注,图片必须提供替代文本”。
第三,负面约束比正面约束更有效。我试过写“请输出美观的代码”,结果模型自由发挥;改成“不要使用 alert、不要内联事件处理器、不要引入外部 CDN、不要生成超过 200 行的代码”之后,输出规范多了。因为这些“禁止项”是明确可检查的,模型更容易执行。
3. 底层原理与架构设计:为什么技能包要这么设计
3.1 触发机制:靠描述文件做意图匹配
一个自然的问题是:agent 怎么知道该用哪个技能?绝大多数框架的实现方式是“语义路由”。agent 收到用户消息后,会把当前对话内容和技能目录里的SKILL.md摘要一起交给模型,由模型判断是否需要使用某个技能,以及使用哪一个。
这相当于把“路由决策”交给了模型本身,而不是写死的规则。所以SKILL.md写得越具体,触发就越准确。比如“当用户提到 PDF、转 PDF、导出 PDF 时,优先使用本技能”就比“处理文档”好用得多。我在实践中发现,在描述里加“优先使用”这四个字,能明显降低技能之间的误触发。
3.2 上下文管理:技能包如何影响 token
如果把所有技能内容一次性塞进上下文,token 会很快爆炸。比如你有 20 个技能,每个SKILL.md加脚本 2000 token,光技能描述就吃掉 4 万 token,还没开始干活呢。
主流做法是“两阶段加载”:先把每个技能的“标题 + 一句话描述”注入上下文,模型判断命中某个技能后,再真正读取该技能的完整 SKILL.md 和相关资源。所以技能描述要短小精准,把长内容放到脚本或 assets 里,按需读取。这也是为什么SKILL.md开头必须有一句高度浓缩的“技能摘要”,它直接影响上下文窗口的利用效率。
3.3 安全边界:控制技能到底能做什么
技能包本质上是一段可执行代码 + 模型指令,权限控制做不好会很危险。我见过一个团队因为技能脚本里硬编码了数据库连接串,最后日志里把整个连接信息打出来了。安全设计至少要考虑这几层:
- 只读操作、局部文件写入、网络请求、命令执行等操作,按风险等级分开。
- 对需要执行任意命令的技能,最好放到沙箱容器里,限制 CPU、内存、网络访问。
- 密钥通过环境变量注入,不要写进技能代码。
- 技能脚本里避免使用全局路径,只允许在项目目录内读写文件。
注意:如果你的 agent 能访问生产环境,技能里一定要做好最小权限设计。宁可多写几条校验,也别把“删除数据库”这种操作暴露给模型。
3.4 多技能协作与 agent 架构
复杂任务往往需要多个技能协作。比如“整理会议纪要并发送邮件”,可能需要“文档解析技能”和“邮件发送技能”。架构上有几种常见模式:
- 串行调用:agent 按顺序调用技能,前一个技能的输出作为后一个技能的输入。
- 并行调用:多个独立技能同时执行,最后合并结果。
- 技能路由:由一个“调度技能”决定调用哪些子技能,适合任务分支多的情况。
- 多 agent 协作:每个子 agent 绑定一组 skills,通过消息队列或共享内存协作。
在多 agent 场景中,技能包需要定义清晰的输入输出协议,最好用 JSON Schema 描述参数,避免歧义。比如“邮件发送技能”的入参应该是:
{ "to": "user@example.com", "subject": "会议纪要", "body": "...", "attachments": [] }这样其他技能或 agent 调用时,就知道该传什么字段。没有这个约束,两个技能之间传参全靠模型猜,很容易出问题。
4. 常见问题与排查技巧实录
4.1 技能没有被触发,或者总是选错技能
这是新手遇到最多的问题。原因通常有三类:描述不够具体、多个技能描述相似、模型版本对指令遵循能力不足。我建议按这个顺序排查:
- 简化复现:只保留一个技能,看它是否被触发。如果单独放着能触发,说明是技能之间的描述冲突。
- 检查描述里的触发词是否和用户实际表述匹配。比如用户说“出个图”,你的技能里只有“画图”“生成图片”这些词,就很容易漏触发。
- 对比多个技能的描述,确保场景差异化。比如“生成卡片组件”和“生成表单组件”描述里都写了“组件”,但一个强调“卡片”,一个强调“表单”,模型就能更好区分。
我通常会在SKILL.md开头加一句“当用户提到 XX、XX、XX 时,优先使用本技能”,效果提升非常明显。
4.2 上下文过长,或者指令冲突
技能包文件太多、说明太长,会让模型在读取时抓不住重点。我踩过的坑是:把一个技能的所有参考资料都塞进SKILL.md,结果模型生成时反而忽略了核心步骤。建议每个技能包控制在 10 个文件以内,SKILL.md 不超过 500 字,详细内容放到 assets 或脚本里。
指令冲突通常是“通用约束”和“技能专属逻辑”打架。比如底层 prompt 说“优先用开源方案”,但某个技能为了特定业务写死了“必须用商业组件”,模型就会很纠结。解决办法是把团队级约束上移到底层系统 prompt,技能里只描述自身的专业逻辑,不要重复写全局规则。
4.3 脚本执行环境不一致
本地能跑,放到服务器上就挂,多半是依赖路径、Python 版本、环境变量的问题。我归纳了几个有效做法:
- 技能包自带
requirements.txt或Dockerfile。 - 脚本开头做环境检查,比如“如果 Python < 3.10,退出并提示”。
- 使用
#!/usr/bin/env python3这类 shebang,而不是写死/usr/bin/python。
还有一个很隐蔽的问题:Windows 和 Linux 的换行符不一致,导致 shell 脚本执行时报“bad interpreter”。统一在 Git 里配置*.sh text eol=lf就能解决。
4.4 调试技巧实录
调试 agent skills 比普通程序麻烦,因为中间隔了一层模型推理。我常用的调试方法有这几种:
- 开启 agent 的 verbose 日志,看模型选择了哪个技能,以及实际读取了哪些文件。
- 单独手动执行技能脚本,确认脚本本身没问题,再排查调用层。
- 做“最小复现”:只保留一条用户消息和一个技能,逐步加回复杂度,直到问题复现。
- 记录一次完整运行轨迹,包括模型输出、脚本输出、最终结果,方便改动前后对比。
有时候模型输出看起来合理,但脚本执行报错,这时候问题就在脚本参数解析上。检查技能脚本是否严格按照 SKILL.md 里定义的参数格式接收输入。
4.5 常见问题速查表
| 现象 | 可能原因 | 快速解法 |
|---|---|---|
| 技能不触发 | 描述不够具体 | 补充触发词和典型使用场景 |
| 总是选错技能 | 多个技能描述相似 | 强化差异化关键词,弱化共用词 |
| 输出格式漂移 | 缺少校验脚本 | 加入输出校验和自动修正逻辑 |
| 上下文过长 | 技能内容太多 | 压缩描述,把长内容移入脚本/assets |
| 多次调用结果混乱 | 技能间依赖关系不清 | 定义 JSON Schema 参数协议,或拆分子 agent |
| 脚本权限过大 | 沙箱配置缺失 | 限制网络、文件路径、执行权限 |
| 本地能跑线上挂 | 环境不一致 | 使用固定版本依赖 + 环境检查 |
最后分享一个我自己的经验
刚开始做 skills 时,我总想做一个“万能技能”,把什么功能都塞进去,结果SKILL.md写了三千字,模型反而不知道该听谁的。后来我把技能拆成多个小组件,每个只做一件事,比如“生成卡片”“生成表单”“校验样式”,每个技能几十行说明,命中率高,维护也轻松。
如果你也在折腾 agent skills,一个小建议:从最小可用的技能脚本开始,先解决一个具体的重复性任务,验证流程跑通后再做扩展。别一上来就搞复杂编排,技能和技能之间的协作,永远是在单个技能稳定之后才需要考虑的事。