做Agent开发的人,最近应该都躲不开“agent-skills”这个词。我最早看到这个概念是在某海外大模型团队发布新功能时,当时没太当回事,但后来在实际项目中反复踩坑、不断重构之后才回过味来:这套东西解决的不是某个API好不好用的问题,而是AI Agent从“玩具”走向“工具”的关键一步。这篇文章我想用自己真实的开发经历,聊聊Agent Skills到底是什么、内部结构怎么拆、怎么落地,以及那些文档里通常不会写出来的坑。
如果你正在做基于大模型的自动化应用、企业内部知识助手、数据报表生成工具,或者单纯好奇AI Agent的能力边界是怎么被一步步撑起来的,这篇文章应该能给你一些可以直接参考的方案。我会尽量少讲虚的,多讲实操,把我踩过的坑和优化过程都摊开来讲。
1. 我为什么盯上Agent Skills:这轮AI开发范式到底变了什么
先说说我自己的背景。过去一年多,我一直在做面向企业内部的知识库问答和报表自动化项目,底层是通用大模型加上一套工具调用逻辑。最开始大家觉得只要把工具列表塞给模型,模型就会自己调用,结果真正跑起来完全不是那么回事。模型经常在错误的时候调用工具,或者在正确的时候选错参数,甚至完全忽略工具而靠记忆瞎编。整个系统调试起来非常痛苦,因为所有工具的注册信息都堆在一起,互相干扰。
后来我尝试把工具的注册信息改写得更详细,加上长描述和示例,情况有所好转,但很快又遇到了新问题:当工具越来越多,单次请求要携带的上下文越来越大,模型在长上下文中反而更容易出错,响应速度也大幅下降。我开始意识到,问题不是出在某个工具的封装上,而是缺少一个把“能力”和“调用方式”打包成一等公民的抽象层。Agent Skills正是在这个方向上给了我很明确的思路。
这个概念的核心并不复杂,它就是让你把一组相关的提示词、代码逻辑、资源文件和说明文档打包成一个独立的技能目录,让模型像“点菜”一样选择和使用这些技能。你可以把它理解为:以前我们教模型怎么做一件事,是把食谱写在对话里,模型一边看一边做,容易手忙脚乱;而Agent Skills是直接把一道菜的完整做法和所需食材封装成一个“料理包”,模型只需决定点什么,开火翻炒即可。
这个抽象带来的改变是显著的。第一,技能的描述、参数和调用逻辑可以独立维护和测试,不用每次改动都要重新经历一轮漫长的Prompt调试;第二,模型不需要在上下文中看到所有技能的全部细节,只需要理解每个技能的“菜单说明”就能做出选择;第三,不同团队和不同项目之间的能力复用变成了可能,一个写好的技能可以像库一样被引用。
从开发范式上看,Agent Skills的意义在于它把“写Prompt”升级成了“写程序”。Prompt还是那个Prompt,但它的组织方式、维护方式、测试方式和发布方式都向软件工程的标准看齐了。这对我来说是一个很大的转变,因为此前总觉得AI应用的开发很“玄学”,改几个字效果就变,而把技能封装成代码后,至少我可以做单元测试了。
1.1 一个尴尬的旧方案:为什么我之前总在乱成一团
拿我之前做过的一个财务数据查询系统举例。最初设计方案时,我注册了十几个工具,从“查询利润表”“查询现金流量表”到“生成柱状图”“发送邮件提醒”,全都在一份工具清单里。测试时发现两个很典型的问题:一是工具描述之间语义重叠,模型有时候把“查询利润表”调成“查询资产负债表”,因为两者的描述里都有“财务报表”字样;二是当用户问题表述比较绕,比如“上季度我们钱花超了吗”,模型不知道应该先调“查询现金流量表”再调“计算同比”,经常只做一半就直接开始生成结论。
如果按照Agent Skills的思路改造,我会把“现金流量分析”整个做成一个技能,技能内部自己封装三个步骤:取数、计算口径、生成结论文案。模型只需要知道这个技能能回答“花钱超没超”这类问题,不需要了解内部步骤细节。调用链短了,出错率也自然降下来了。
1.2 Agent Skills解决的本质问题:让模型学会“做事”而不是“回答问题”
站在更宏观的视角看,Agent Skills针对的是大模型应用中一个根本性的错位:模型本质上是一个预测下一个词的系统,而我们要它完成的是多步骤、有依赖关系的任务。如果只是把需求写进Prompt里让模型一次生成,效果非常不稳定,因为模型是在“猜”而不是在“执行”。Skill通过把过程性知识(怎么做一件事)变成外部代码和资源,让模型可以越来越少地“自由发挥”,越来越多地“按图索骥”。
实际上我现在写复杂Agent任务时,心态已经变了:Prompt里只保留目标描述和约束条件,具体的执行逻辑全部下沉到一个或多个Skill里去做。模型在这里承担的是“路由决策者”的角色,而真正的干活儿由藏在技能里的代码完成。这种思路让我后期调试系统的重心从上万字的Prompt里找错,转移到了某个具体技能的内部逻辑优化,效率完全不一样。
2. 技能拆解:一个Agent Skill内部到底装了什么
要动手做Agent Skill,第一步是理解它的目录结构和每个文件的定位。虽然不同框架在细节上略有差异,但核心组件是统一的:技能说明文件、代码实现、资源文件。大多数实现里还会支持可选的依赖声明和测试样例。
从抽象角度看,一个技能本质上就是一个带着“使用手册”的代码模块。“使用手册”是给模型看的自然语言描述,“代码模块”是给执行环境看的具体逻辑。两者必须配合好,才能让模型既知道什么时候用、又知道怎么跑通。
我习惯把一个技能目录组织成这样:
my-skill/ SKILL.md script.py requirements.txt assets/ template.html tests/ test_case.jsonSKILL.md是灵魂,它决定了模型会不会在正确的场景下调用这个技能。script.py是真正干活的,它必须健壮且输出结构稳定。requirements.txt用于声明依赖,assets用来存放技能运行时需要的静态资源,tests用来做离线验证。这个结构看起来很机械,但正是这种机械性让人安心:我可以像维护普通代码库一样维护技能,有版本管理、有测试、有依赖控制。
2.1 技能说明文件的写法与哲学
SKILL.md的核心目标是让模型在“合适的时机”调用“合适的技能”,并且以“正确的方式”传参。它通常包含以下部分:
- 技能名称:简短、唯一、面向能力而不是任务。命名上我一般都避免“查询数据库助手”这种带具体工具名或技术栈的名字,因为模型更容易理解“查询订单状态”这种按能力命名的技能。
- 功能描述:两三句话说明技能负责什么。关键是写清边界:能做什么、不能做什么。
- 适用场景与触发条件:列出该技能适合处理的问题类型,最好附几个典型用户问题的示例,让模型的语义匹配有据可依。
- 参数定义:包含参数的名称、类型、是否必填、取值枚举和说明。参数不宜过多,一般不超过5个,否则模型在选参阶段就很容易出错。
- 使用示例:给出1到2个完整的调用示例,包括用户问题到结构化参数的映射。
写这份说明时,我最大的心得是:模型像是一个转岗来的新同事,聪明但对你这个行业的黑话一无所知。你不能只写“查询订单”,你要写清楚“当用户提供了订单号或者客户名,并想知道订单当前的处理进度时使用”。技术文档忌模糊,给模型看的文档更是如此。
2.2 代码封装:把规则变成可执行逻辑
技能内部的代码部分就是我常说的“最后一公里”。很多被吹上天的“智能”最终都落到了干净利落的Python函数、SQL查询或者文件处理逻辑上。这里有一个很微妙的设计点:技能内部代码不应该具备“大模型智能”,而必须追求确定性和可控性。如果某一步确实需要小模型或者大模型的灵活性,应当单独封装成内部子调用,而不是混在技能代码里让整体逻辑变得不可预测。
我做技能内的代码一般遵循几条原则:
- 所有输入参数必须在函数入口处做校验和归一化,避免脏数据直接带进核心逻辑。
- try-except必须覆盖主流程,并且异常信息要友好,因为异常信息最终会作为反馈回传给模型,好的错误信息可以帮助模型在下一轮自行纠正参数。
- 返回的数据结构必须是JSON友好的,尽量扁平化,字段命名要有语义,别用a、b、c这种缩写。
- 如果技能内部要请求外部API或读文件,注意设置超时和重试,避免技能卡死导致整个Agent会话卡死。
还有一个容易忽略的点是幂等性。Agent可能会因为超时原因重复调用同一个技能,如果技能本身有副作用(比如发送邮件、创建订单),那必须考虑重复执行造成的伤害。我后来处理这类问题时,会在技能内部加一个简单的幂等控制:比如用请求ID做去重,或者在执行前检查是否已存在同批次的执行记录。
2.3 语义标注:让模型知道什么时候该用
这一步是我认为Agent Skills中真正的技术含量所在。一个技能写得好不好,很大程度上取决于模型的语义匹配能不能精确命中。为了做到这一点,除了在SKILL.md里写清楚适用场景,我还建议做两点额外工作:
- 给技能打标签。标签可以是领域标签和任务标签的组合,例如“finance”“dashboard”“email”。标签能让Agent在粗粒度路由时候快速过滤掉明显无关的技能,减少语义干扰。
- 在SKILL.md里增加“不适用场景”的说明。这看起来有点反直觉,但实际上非常有效。比如一个“订单查询”技能,写明“该技能不处理订单退款,退款请联系售后”后,模型在遇到退款问题时,会更倾向于明确拒绝而不是强行调用技能。
这些语义标注的思路,其实和传统软件工程中的接口文档很类似——你不可能让每个调用方都读懂你的实现,但你必须让他们一眼就知道“这个接口和我有关”。Agent Skills把这种接口语义从人读变成了“模型读”,即使模型的能力在变强,清晰标注依然是降低误用率的最直接手段。
3. 实操:做出你的第一个Agent Skill
理论说那么多,不落地都是空的。我用一个实际场景完整走一遍:把“生成业务周报”做成一个技能。这个技能在企业里很常见,也比较有代表性。它涉及到数据读取、模板渲染、文本生成这几个步骤,可以很好地展示一个技能该如何拆解和组装。
3.1 选场景与边界设计
业务周报的痛点在于:周报格式繁琐,每个人写的风格不统一,数据核对费时。要把它技能化,我首先把需求边界定下来:
- 输入:起始日期、结束日期、部门、是否包含增长分析。
- 输出:一段Markdown格式的周报正文。
- 不处理:不负责发送邮件、不负责自动生成图表、不负责多部门横向对比。
定边界很重要。最初我做技能时总想着把能做的功能都塞进去,结果技能描述臃肿,模型在各种功能之间摇摆不定,反而什么都不好使。后来我学会一个口诀:一个技能只做好一件事,多一事不如少一事。如果确实需要发送邮件,那就再做一个“发送周报邮件”技能,两者组合使用,而不是强行揉成一个。
3.2 具体落地步骤:从SKILL.md到代码
我先创建目录结构,然后写SKILL.md。这里的描述我前后改过好几版。最初我写的是“生成业务周报”,后来实际测试中发现模型不够明确,改成:
“生成指定部门在指定时间范围内的业务周报。适用于用户要求汇总本周业绩、复盘目标完成情况、输出周报文本的场景。输入必须包含部门名称与日期范围。输出结果为Markdown格式,包含业绩概览、目标完成率、亮点与问题、下周计划四个模块。”
比较关键的是我甚至写了一句负面描述:“该技能不负责解答具体业务问题,只生成周报文本本身。”加这句话之后误用率降低了不少。
接着写代码。核心逻辑分三步:第一步从数据库读取指标数据;第二步把数据填入周报模板;第三步调用大模型润色文字。需求一旦明确,代码本身并不复杂。但我会特别要求函数返回的文本必须严格按模板结构输出,且每一个数据字段都带上数据来源说明,比如“目标完成率=实际值/目标值”。
代码写完后,我会做几组离线测试,确保在合法输入下输出格式稳定。我的测试样例一般是模拟数据,重点关注边界情况:空数据日期段、部门名不存在、日期格式非法等。如果这些情况没有提前处理,在线调用时模型一旦传错参数,技能就会直接报错,而模型通常不会自己修复,最终反馈给用户的就是一段非常不友好的错误信息。
3.3 集成进Agent框架:注册、加载与调用
技能写好之后,需要把它注册到Agent运行的上下文中。不同框架集成方式略微不同,但大体都是先把技能目录路径加入技能仓库配置,再让Agent在启动时加载该技能。加载后,你可以通过调试日志或者可视化面板查看Agent到底看到了哪些技能,以及最终选择了哪个技能。
一个我在集成期踩过的坑:技能加载后,Agent会同时看到内置技能和我自定义的技能,如果两者在语义上存在重叠,模型可能会优先选择内置技能而不走我的技能。针对这种情况,我后来会在描述里明确加一句“如果用户提到……,应当使用自定义技能,而不是内置技能”,必要时还可以调整内置技能的优先级或干脆禁用某个内置技能。
集成完成之后,一定要做一次端到端的联调。用一个真实的用户问题走通全流程,观察模型是否正确选择了技能、正确传参、正确解析输出。我第一次调试时,模型选择了技能但把日期参数传成了自然语言“上周一”,而不是标准日期格式,导致技能内部解析失败。后来我在SKILL.md的参数说明里明确写了“日期必须为YYYY-MM-DD格式”,并在参数示例里专门放了一条“2025-04-01”这样的值,问题才得以解决。
4. 把Agent Skills跑崩的5个现场:避坑实录
技艺的提升往往伴随着不断踩坑。这里我把最近几周调试Agent Skills过程中最有代表性的几个问题和对应的解决方法整理出来,应该能帮你省不少时间。
4.1 问题一:描述写得像散文,模型把它当成聊天对象
第一次写技能描述时,我用词比较自由,比如“这个技能超级厉害,能帮你处理所有和数据相关的问题”。结果模型常常在没有明确意图时就调用了这个技能,比如用户说“数据很重要”,模型直接调用了技能,然后技能内部因为没有具体参数而报错。问题的根源在于技能描述的触发条件过于宽泛。
解决方法:重写描述为触发条件导向,像写正则表达式一样明确输入必须包含什么特征。我后来在写描述时都会自问一句:“一个从未见过此技能的模型,看到这段话后,会不会准确判断何时调用、何时不调用?”如果答案不确定,那这段描述还不够好。
4.2 问题二:把一个大任务塞进一个技能,导致技能内部逻辑混乱
一开始我试图把“数据提取、清洗、分析、生成日报、发送提醒”都做成一个超级技能。测试结果惨不忍睹:技能内部代码几百行,各种条件分支交织,模型调用时常常漏传关键参数,而且一旦某个子任务失败,整个技能就崩溃。后面我按职责把大任务拆成了四个独立技能:
- 数据提取技能
- 分析计算技能
- 日报生成技能
- 消息推送技能
每个技能内部都很简单,参数不超过4个。模型可以按编排逻辑依次调用多个技能,也可以由上层Agent负责编排,或者我在技能外部再做一个流程编排层。实践经验是:技能越小,复用的可能性越大,出错的可控性也越好。这种“小而专”的思路,和微服务架构的拆分哲学是一致的。
4.3 问题三:参数设计走极端,模型频繁传错
参数设计上我有过两种极端,都走不通。一种是无脑把所有字段都作为必填参数,导致模型为了凑齐参数经常瞎编;另一种是参数设计得过少,导致技能内部逻辑只能用猜测来补全信息。正确的做法是在两者之间找平衡:必须由用户问题明确提供的信息才设为参数,能从上下文或数据库推导出来的信息一律不设参数。
比如“生成周报”技能,我最初需要用户提供“目标完成率”,后来发现这个值可以从数据库里算出来,于是我就把它从参数中去掉了,改为技能内部自动计算。参数减少后,调用成功率明显提升。参数不如规则,规则不如自动推断——这是我在反复调参中总结出来的经验。
4.4 问题四:输出的格式不稳定,下游处理全部扑街
当技能输出用于下游二次判断时,输出的结构化程度就直接决定了整个链路是否稳定。早期我在技能里返回的是自然语言段落,看起来方便阅读,却让Agent难以判断“周报是否成功生成”“是否需要发送提醒”等后续步骤。后来我统一把所有技能的输出改成了带状态码和结构化数据的JSON格式。
例如:
{ "status": "success", "report": "### 周报正文\n...", "metric": { "completion_rate": 0.86, "confidence": "high" } }这样下游无论是解析展示还是再做判断,都非常清晰。这个改变是我在调试一个多技能协作任务时被迫做出来的——当时后续技能经常因为读不懂上一个技能的返回值而报错,把结果改成JSON后再也没出现过这类问题。
4.5 问题五:技能之间的边界重叠,模型选错技能
当技能库扩大后,最强的噩梦就是边界模糊。我有两个技能:“查询销售额”和“销售分析报告”,模型经常把分析任务发给查询技能,或者把简单查询发给分析技能。为了解决边界重叠,我在两个技能的SKILL.md中互相对照写。
具体做法是:在“查询销售额”里明确“只返回数字或表格,不做文字分析,用户若需要结论性分析请调用销售分析报告技能”;在“销售分析报告”里明确“该技能执行深度分析,至少需要三个维度的输入,不应在只有单一查询需求时使用”。这种“互相甩锅”式的描述看似麻烦,效果却奇好,因为它主动帮助模型缩小候选集。最终技能的调用准确率从最初的68%左右提升到了90%以上。
5. 技能的进化:从单技能到技能族的规模化之路
单个技能跑通只是一个开始。真正让Agent Skills发挥价值的是把它推向规模化,像管理软件产品一样管理一整套技能库。这里我讲讲在技能版本管理、组合编排和评测上的一些探索。
5.1 技能的版本管理与依赖控制
技能也是代码,因此天然需要版本管理与依赖控制。我习惯为每个技能维护一个CHANGELOG文件,记录每个版本的行为变化。当某个技能的行为调整后,以前依赖它的上层Agent可能会受影响,所以在升级时我会先跑一遍回归测试集,确保所有调用链路上的行为一致。
依赖控制上,一个技能最好只依赖明确声明的第三方库,不要隐式依赖环境里恰好安装的包。我遇到过一个很隐蔽的问题:技能A依赖某个数据分析库,另一个技能B也依赖它,但B要求的版本更高,升级后A的行为悄悄改变了。后来我统一在每个技能目录里放requirements.txt,并对技能运行环境做虚拟隔离,这个坑才算填上。
5.2 技能组合:让Agent成为技能编排器
目前我在实验中尝试过一种更高级的形态:把几个基础技能封装成一个复合技能。复合技能内部可以编排多个子技能,自己只暴露一个更上层的接口。比如“生成季度经营分析”这个复合技能,内部会调用“数据提取”、“指标计算”、“图表渲染”、“文本润色”四个基础技能,而复合技能对外只要求输入季度和业务线名称两个参数。
这种分层的技能体系带来两个好处:一是让模型面对的决策空间更小,上层的路由更稳定;二是子技能可以被其他上层技能复用,避免重复开发。从架构上讲,这就像把单体函数重构成一个服务树,所有节点都职责单一、可测试、可组合。
实现上,复合法就是在SKILL.md里声明子技能列表,然后在代码里用Agent框架提供的工具调用接口逐个调用子技能。值得注意的是,子技能之间的数据传递要非常明确。我会为每一个复合技能定义一套中间数据结构的Schema,子技能之间靠这份Schema衔接,避免任何靠字符串拼接来传数据的脆弱做法。
5.3 评测:怎么证明你的技能真的有用
技能做出来之后,如果没有可量化的评测,一切改进都是盲人摸象。我建立了一个简单的评测集,大概包含100条真实用户问题和每条期望的技能调用路径。评测时,我会让Agent独立运行,记录它选择了哪个技能、传参是否正确、返回内容中是否包含期望的关键信息。
评测指标主要有三个:
- 技能命中率:用户的问题是否触发了正确的技能。
- 参数准确率:被调用的技能收到的参数是否符合预期格式。
- 输出质量分:对技能返回的文本或结构化数据做人工打分或规则校验。
通过这个评测集,我能量化每一次改动对整体效果的影响。比如我前面提到在SKILL.md里加“不适用场景”描述后,命中率提升了大概7个百分点。如果没有评测集,这种改善很难被感知到,也不会激励我持续做这种细节优化。
我个人认为,Agent Skills评测这件事,越早做越好。评测不必追求很大的规模,关键是覆盖要全:既要有标准案例,也要有边界案例。这些案例会伴随技能迭代不断更新,最终形成你整个Agent系统最有价值的资产之一。
在做这整套Agent Skills改造的过程中,我最大的感触是:AI Agent的能力上限,其实不在于模型本身选得多强,而在于你有没有把该固化的规则固化下来,该拆解的任务拆解干净。技能不是银弹,但它是目前我见过的、把大模型从“单轮问答工具”推向“多步骤执行体”的最务实路径。如果你正在焦头烂额地调试工具调用,不妨换个思路,把能力拆成技能,把技能变成工程,也许你很快就会发现,之前那些玄学问题,大多都消失了。