开头:
做AI Agent的项目有一个规律:Demo阶段靠模型聪明,落地阶段靠工程治理。跑通一个漂亮的原型很容易,但当Agent要处理真实的业务场景时,你会发现所有的问题都会汇聚到同一个点上——技能不够清晰、不够复用、不够可控。这也是"agent-skills"这类概念在一线开发者圈子里突然热起来的原因。简单说,Agent Skills就是把Agent能够执行的能力从杂乱无章的提示词中剥离出来,封装成有明确元数据、执行流程、输入输出规范和评估标准的独立单元。它解决的问题非常具体:大上下文里的指令冲突、跨场景复用时提示词互相污染、以及能力边界模糊导致的不可控行为。这篇文章我会从技能单元的构成、设计原则、实操构建流程、技能库治理和实战踩坑五个维度展开,适合正在做Agent产品落地、或者准备用Agent框架搭建自动化方案的开发者参考,无论你用的是哪套框架,这些方法论都能直接套用。
1. 为什么Agent项目最后都会回到"技能"这个问题上
1.1 从"巨型提示词"到"能力单元"的范式切换
早期我做Agent的时候,习惯把所有的指令都塞进一个System Prompt里。客户的业务规则、数据格式要求、输出偏好、工具使用方法,全部用自然语言堆在一起。后果非常直观:上下文越长,模型对指令的遵循率越低;规则之间开始互相打架;改一处逻辑就要对整个提示词做回归测试。那种3000行System Prompt的项目,后期维护成本高到让人绝望。
后来接触到Skills的思路,本质上是把Agent的能力做了一个"函数化"的拆解——每个技能就像代码里的一个函数,有明确的名称、参数、返回值,内部实现细节被封装起来,外部通过描述来"调用"。这个转变看起来只是形式上的变化,实际带来的收益是结构性的:Agent不再被迫从一整篇混杂的规则中推理该怎么做,而是先判断当前场景应该激活哪项技能,再由技能内部定义好的流程去完成工作。
这也解释了为什么大模型在2025年以后,即使推理能力越来越强,一线的Agent工程还是倾向于用技能库来组织能力。因为模型能力解决的是"怎么回答得聪明",而技能体系解决的是"做事是否稳定可靠"。对于生产环境来说,后者往往更重要。
1.2 技能体系的三个核心价值
第一,可复用。同一个技能可以在不同的Agent中使用,比如"文档摘要""数据清洗""JSON校验"这类通用技能,写好一次,随处调用,不必为每个Agent重新写一遍。第二,可测试。技能有明确的输入输出接口,可以围绕单个技能构建测试集,回归时只需要跑对应的测试用例,不用整个端到端流程都重新跑一遍。第三,可演进。业务逻辑变化时,只需要修改受影响的技能,其他技能保持稳定,Agent的整体行为变化是可预期、可控制的。
1.3 为什么现在讨论这件事正当时
我观察到一个很明显的趋势:过去一年里,开源社区和各路Agent框架都在往"技能仓库"的方向收敛。有些叫Tools,有些叫Plugins,有些叫Actions,名字各异,但底层逻辑惊人的一致——都是把Agent的原子能力标准化。这背后的驱动力是Agent的应用场景从聊天助手转到了自动化执行:写代码、管订单、做分析、抓数据、发消息。这些场景要求Agent不仅会"说",还得会"做",而"做"的过程天然需要结构化封装。可以说,技能是Agent从"对话模型"进化成"执行系统"的关键枢纽。
2. agent-skills技能单元的完整构成:不只是写一段提示词
2.1 技能元数据:让Agent知道"什么时候该用我"
一个完整的技能单元,第一层是元数据。元数据里最重要的就是技能名称和描述。千万别小看这两样,它们直接决定了Agent在复杂场景里能不能正确"召回"这个技能。名称要符合直觉,比如"fetch_weather""parse_invoice",让模型一看名字就知道用途。描述则要写得像给一个不太聪明的实习生看的需求说明:什么时候触发、什么时候不触发、输入是什么、输出是什么、有哪些边界条件。
举一个实际的例子,我团队里有个"query_database"技能,最初描述只写了"查询数据库",结果Agent经常在用户闲聊或者问不相关问题时也去调用它。后来我们把描述改成"当用户明确要求查询或分析数据库中的数据,且提供了表名、SQL或明确的数据字段时使用;如果是普通的业务咨询,不要调用本技能"。这个改动的效果立竿见影,误调用率从31%降到了6%。描述里的每一个"不要"都价值千金。
2.2 执行流程:技能的"肉体"
技能的第二层是执行流程。这一层定义了技能被触发之后,Agent具体要按什么步骤做事。流程可以简单也可以复杂,但核心要求是"路径清晰,每一步都有产出"。常见的流程结构包括:前置校验、数据获取、中间处理、结果格式化、质量控制。
举个例子,一个"生成月度报表"技能,执行流程大概是这样的:先校验输入的时间范围和数据源配置是否合法,再分批拉取数据,接着做清洗和聚合,然后把结果渲染成Markdown表格或图表,最后检查数据完整性和关键指标是否在合理区间。每一步都有明确的输入和输出,Agent在执行时就不容易"跑飞"。
流程写得越细,Agent的执行越稳定。但这里有一个度的问题——流程太细也会导致Agent失去灵活性,遇到流程之外的边缘情况反而不知道怎么处理。我自己比较推荐的做法是:把核心路径的步骤写清楚,但对每步骤的"内部实现"不做限制,给模型留出微调空间。
2.3 上下文管理:技能之间如何传"接力棒"
技能的第三个构成是上下文管理。很多Agent项目死在"多技能协作"上,原因就是技能之间数据传递混乱。技能A产出的结果,技能B拿不到;或者技能B需要的数据,技能A根本没保存。这就像两个程序用全局变量通信,时间一长,谁改的、改什么了,完全失控。
我采用的方案是"显式上下文契约"。每个技能声明自己的输入需求和输出产物,输入必须从全局上下文的某个固定字段读取,输出也写入固定字段。技能之间不允许"偷偷"读写未声明的上下文。这样,整个Agent的执行链路像一条流水线,每个工位只碰自己该碰的物料,然后把成品放到指定的托盘上。虽然听起来有点死板,但真实场景下,这种死板恰恰是稳定性的来源。
2.4 质量评估与回退机制:技能的最后一道防线
最后是评估与回退。每个技能定义一份"成功标准",执行完成后Agent要自我检查,输出是否符合预期。如果不符合,技能内部要定义回退策略:是重试一次,还是降级处理,还是直接报错让上层Agent决定。没有这一步,技能就是"盲飞"。我自己习惯在每个技能的产出阶段,加一句强制的自检提示:请对照成功标准逐项检查输出,若有偏差,说明原因并修正;若无法修正,则返回明确的错误信息。
3. 技能设计的三条硬性原则:粒度、描述与边界
3.1 粒度控制:一个技能只做好一件事
技能设计最容易犯的第一个错误,是粒度失控。做粗了,一个技能包罗万象,内部逻辑堆成山,跟巨型Prompt没什么区别;做细了,拆出几百个两三句话就能讲完的小技能,Agent每次决策都要在几百个候选里挑,召回率直线下降,性能也撑不住。
我的经验是拿"代码函数的设计标准"来做参照:单一职责、高内聚低耦合,同时还要考虑"调用频率"。如果一个动作在业务流程中反复出现,比如数据清洗、格式转换、内容校验,就值得单独拆成技能。如果一个动作只是某个大任务里的一小步,单独拆出来反而增加检索负担,那就让它在父技能的流程里自然完成。
实际操作中,我用一个"三七开"的经验法则:一个普通业务Agent,技能数量控制在20到50个之间。少于20个,说明技能太粗,Agent会频繁进入"不知道下一步做什么"的状态;多于50个,技能的召回率会明显下降,而且技能间语义重叠的概率大增,Agent经常选错。这个数字不是拍脑袋,是实测过多个项目后的体感区间。
3.2 描述写作:给Agent一份"好的说明书"
技能描述的质量,直接决定Agent能不能在正确的时机调用正确的技能。我把描述写作拆成四个必需部分:触发条件、输入要求、输出约定、禁忌边界。触发条件描述的是"什么情况下用我",越具体越好,最好带上业务场景里的典型表达;输入要求描述的是"你需要准备哪些材料,如果没有,先去哪里获取";输出约定描述的是"我会给你什么格式的结果";禁忌边界描述的是"什么情况下绝对不要用我,或者用了之后千万不要做什么"。
有个经验值得分享:写描述的时候,不要站在开发者自己的角度,要站在模型的角度。模型判断是否调用技能时,看到的是当前对话上下文与技能描述的语义匹配度。所以描述里应该多写"面向场景的话",少写"面向实现的话"。比如"本技能用于判断用户表述中的时间模糊性"虽然准确,但不如"当用户提到明天、后天、下周一、过两天、月底之前这类不确定的时间表达时,使用本技能进行规范化"来得实用。
3.3 边界意识:明确技能"不做什么"
边界比能力更难定义,但也更重要。一个只有能力描述、没有边界声明的技能,就像一辆没装护栏的车——性能很强,但随时可能冲到不该去的地方。
纯技术层面的边界,比如"本技能只处理JSON格式的输入,不接受XML",这类容易定义。难的是业务层面的边界。我遇到过最典型的坑:一个"自动回复客户消息"的技能,能力描述写得天花乱坠,但没写"涉及退款、投诉、法律纠纷等敏感场景时,必须将对话移交给人工客服"。结果这个Agent真的在用户投诉时给出了不恰当的承诺,给业务造成了麻烦。从那以后,我把"禁忌场景"列为技能描述的必要字段,每项技能必须明确写出不适用或需升级处理的场景。
4. 从零构建一个Skill:完整实操流程
4.1 场景定义与需求拆解
第一步,先把业务场景拆成最小可执行的原子任务。以我最近做的一个"合同审查辅助Agent"为例,客户的需求是"帮法务看一下合同有没有风险"。这个需求太大,直接写技能根本没法写。拆解之后得到这些子任务:合同文本解析、关键条款抽取、敏感信息识别、风险点标注、风险等级评估、审查报告生成、格式规范校验。
拆解的原则是"能否独立产生价值"。每个子任务拆出来之后,都要问一句:如果只完成这一步,对整体目标有没有贡献?有贡献,才值得做成技能。
4.2 编写技能描述与元数据
拆好任务后,逐个编写技能的元数据和描述。这一步不要偷懒,我会建议把描述当成产品文案来写。下面是我常用的一种模板结构:
输入:本技能需要的问题类型、数据字段、前置条件,以及若输入缺失时的处理方式。
输出:本技能会产出的结果内容与格式,以及成功标准。
触发条件:在什么业务场景、什么用户意图下被调用。
边界与禁忌:不适用或禁止处理的情况,以及遇到这些情况的回应方式。
使用时需要注意的事项、针对特定模型行为或常见错误的处理提醒。
这段内容我会反复打磨,直到它的召回率稳定通过测试集。
4.3 实现执行逻辑与工具调用
第三步是写执行逻辑。执行逻辑有两种实现方式:一种是纯提示词驱动,在技能体里用文字描述步骤,让模型自己执行;另一种是绑定外部函数,调用API、执行脚本、查询数据库。在实际生产中,大多数技能是两种方式的混合:外部函数负责确定性操作,比如发送HTTP请求、执行SQL,模型负责判断性操作,比如决定查询条件、解析返回结果、判断是否满足成功标准。
工具调用这块,我强烈建议把所有外部接口封装成标准的"输入输出协议"。比如查询数据库的统一接口,接收JSON格式的查询条件,返回JSON格式的数据集。这样技能编写时不需要关心底层数据库是MySQL还是PostgreSQL,Agent的执行逻辑也保持稳定,后续替换底层实现不会影响技能本身。
4.4 测试驱动的技能迭代
技能写完之后,不要直接上生产。我会为每个技能准备一份评估集,评估集里包含三类样本:应该触发技能的正样本、不该触发技能的负样本、以及触发后各种输入变化下的边界样本。跑一轮测试,看三个指标:召回率(该触发时触发的比例)、精确率(触发时做对的比例)、以及执行成功率(触发后整个流程跑通且输出合格的比例)。
测试结果出来之后,绝大多数情况不是技能的"实现"有问题,而是"描述"和"流程设计"有问题。描述太模糊就改描述,流程缺步骤就补步骤,边界没写清楚就加边界。这个过程通常要迭代好几轮,但每一轮都能看到指标实实在在的上升。我见过太多团队跳过了评估这一步,直接凭感觉调提示词,调了半天也不知道到底改了什么生效、改了什么拖后腿。
5. 技能库治理:组织、版本与可靠性的工程化方案
5.1 技能库的目录结构与命名规范
当技能超过二三十个之后,靠记忆管理就不现实了。我把技能库按业务域和通用能力两层来组织。通用能力放底层,比如数据清洗、格式化、HTTP请求、JSON校验;业务域技能放上层,比如合同审查、工单分类、报表生成。上层技能可以依赖下层技能,但不允许跨业务域互相调用,避免出现"合同审查技能调用了工单分类技能"这种匪夷所思的依赖。
命名规范上,采用"动词+对象"的结构:parse_contract、classify_ticket、generate_report。这样从文件名就能看出技能做什么,也方便Agent在检索时理解技能的目的。
5.2 技能注册与动态发现
大型Agent系统不能靠"把所有技能的描述都塞进Prompt"这种方式工作,token不够,检索精度也不够。正确的做法是技能注册中心加动态发现:技能的元数据注册到一个索引中,Agent每次执行任务前,先在索引中检索与当前意图最相关的少数几个技能(通常3到5个),再将被选中的技能的完整定义注入上下文。
这里有一个关键的工程细节——检索条件不能只用一句用户原始输入去搜,而要用"当前Agent的任务描述+已完成步骤的上下文摘要+用户意图分类"的组合去搜。比如用户说"帮我把这个合同看一下有没有风险",如果只用原话搜索,"合同审查辅助"技能能搜到,但"敏感信息识别"这类子技能就搜不到。用组合上下文检索之后,匹配的准确率高很多,实测下来召回率提升了二三十个百分点。
5.3 版本管理与兼容性
技能是会持续迭代的。同一个技能,这周改了输出的字段格式,下周改了触发条件,月底可能整个流程重写。如果没有版本管理,线上Agent用的技能版本无法追溯,出了问题都不知道是哪个版本引起的。
我现在的做法是:每个技能包都带版本号,并写入一个统一的变更日志;技能更新先发到测试环境,跑完评估集和回归集之后才上生产;生产环境的Agent固定依赖某个技能版本,升级需要显式操作而不是自动拉取最新版。这些看起来是基础的软件工程习惯,但Agent项目里很多人因为觉得"只是改改提示词"就不遵守,结果线上事故频发。
5.4 监控:技能调用的可观测性
最后是监控。每个技能的执行情况都必须有日志:何时被调用、调用时的输入是什么、执行是否成功、走了什么分支、耗了多少token、产出是否符合自检标准。我见过太多Agent系统是一个"黑盒子",用户问一个问题,Agent内部调了哪些技能、每个技能做了什么,完全不可见。这种系统出了错,排查成本高到离谱。
监控数据积累一段时间后,还能反过来优化技能库。我每周会看一次技能调用的统计数据,看哪些技能被高频调用、哪些技能经常失败、哪些技能被调用后又被回退。高频且稳定的技能可以考虑进一步优化性能;频繁失败的技能得专项排查;经常被回退的技能,大概率是描述和实际能力不匹配,要么改描述,要么改实现。这样,技能库就不是一潭死水,而是一个持续进化的系统。
6. 实战踩坑记录与优化手记
6.1 技能描述太宽泛,Agent乱调用
坑一就是我前面提到的"query_database"事件。这个坑几乎每个人都会踩。技能描述写得宽,Agent的召回逻辑就容易"宁可错杀也不放过",不该调用的时候也调用。排查的方法很简单——看调用日志,找"调用了但用户意图明显不匹配"的记录,然后把这类场景写进描述的"边界与禁忌"里。这是成本最低、见效最快的优化手段。
6.2 技能粒度过细,决策链路超时
我还踩过一个反向的坑。早期做一个"内容运营Agent",我把"识别标题风格""判断情感倾向""生成长度建议"都拆成独立技能。结果Agent每处理一篇文章,要先检索技能再注入技能定义,硬生生把决策时间拉长了几秒,而且技能之间频繁切换还容易丢失中间状态。后面我把这些捏合成一个"文章风格分析"技能,内部流程三段走,性能上来了,效果也稳了。粒度的调整永远要结合真实的使用场景,不能为了拆分而拆分。
6.3 技能之间隐式耦合,上下文互相污染
第三个印象深刻的坑是上下文污染。当时两个技能都要读写一个叫"current_data"的上下文字段,技能A写入的是清洗前的原始数据,技能B却假设它拿到的已经是清洗后的数据。因为代码里字段名一样,两个技能都能跑通,但拼起来的结果完全错乱。排查了很久才发现,是字段语义不匹配造成的。从那以后,我给所有上下文字段加命名空间前缀,技能内部使用的临时变量一律以技能名开头,跨技能传递的数据必须走统一的数据缓冲区,并明确标注数据状态。这个改造做完,多技能协作的稳定性明显上了一个台阶。
6.4 缺少评估集,改一次坏一次
最后一个教训是关于评估集的。早期我改技能完全靠"这次感觉差不多"来判断,结果经常出现改A技能修好了场景甲的bug,结果破坏场景乙,上线后用户立刻反馈变差了。后来我开始给每个技能维护一个几十条的评估集,每次改动后面先跑一遍回归。一开始会觉得麻烦,但坚持两个月之后,我的技能改动效率翻了几倍,线上出问题的频率也大幅下降。如果你只能从这个系列文章里带走一个习惯,我强烈建议就是"为每个技能维护一份评估集"。
6.5 个人体验与建议
做了这一路Agent项目之后,我最大的感受是:Agent Skills这套东西,本质上是在用软件工程的思维去治理自然语言带来的不确定性。提示词的本质是不可控的,你可以靠"重试"来祈祷它这次更好;但技能体系的本质是可控的,你可以靠设计、测试和监控来保证它稳定地输出。两者的心态完全不一样。如果你手里的Agent还在被"巨型提示词"压得喘不过气,试着从最小的一个动作开始,把它拆出来、封装成一个技能、给它配好描述、跑几轮评估,你会很快体会到那种"终于可控了"的感觉。最后再分享一个实操中的小技巧:技能库建成之后,我会定期让Agent自己对着历史对话日志做一次"技能覆盖度分析",看看哪些高频场景其实还没有对应技能覆盖。这一步每次都能找出几个之前完全没注意到的能力缺口,算是用Agent管Agent的一种低成本方式。