在折腾了大半年Agent开发之后,我越来越觉得:真正拉开智能体能力差距的,不是模型选得多强,也不是提示词写得有多华丽,而是你往它身上挂了多少靠谱的"技能"。
这个感悟,正好和"agent-skills"这个方向完全对应。
很多人以为Agent就是"大模型+提示词+工具",上手就把一堆API插进去,结果模型要么找不到正确工具、要么把参数传错、要么在简单任务上反复横跳。原因很常见也很扎心:你只是在给模型递工具,而没有在做技能封装。技能封装的本质,是把"怎么调用工具"这件事,从模型临时发挥变成了一套有章法、可复用、可评测的工作流。
这篇文章我想好好聊聊技能集的设计思路和落地实操。包括技能体系怎么搭、每个技能内部应该写什么、怎么把零散工具组合成可靠技能链,以及我踩过的几个典型坑。适合同样在做Agent应用开发、想让自己的智能体从"能聊天"变成"真能干活"的开发者,尤其是已经有基础框架、但苦于工具效果不稳定的朋友。
1. 内容整体设计与思路拆解
1.1 技能和工具的本质区别
先理清一个最容易混淆的概念:工具和技能到底差在哪。
工具(Tool)是单一能力,比如"执行一段Python代码""发一个HTTP请求""查一下数据库"。技能(Skill)则是面向一类任务的方法封装,它内部通常编排了多个工具,并配有一套完整的调用策略、输入输出约定和失败兜底逻辑。
举个例子。光给Agent一个"获取当前时间"的工具,它只能查时间。但如果建一个"日程安排技能",技能内部不只是查时间,还包括读取日历、判断空闲时段、创建提醒事件、处理冲突、输出结果摘要。Agent只需要告诉技能"明天下午开会",技能负责把后面所有工具调度理顺。
这个区别对开发的影响很大。工具挂多了,模型的选择负担重,出错概率高。技能则像酒吧吧台后面的"经典酒单",把复杂的调制过程预封装好,模型照着点单就行,不需要它理解威士忌和苦艾酒的比例要求。
所以,技能体系设计的第一个原则是:不是工具越多越好,而是把高频、稳定、可沉淀的调用链,封装成一个个带边界、带规范、带容错的技能包。
1.2 为什么技能化能显著提升Agent稳定性
模型在自由选择工具时,实际效果远没有Demo演示中那么稳定。原因主要有三类:
一是选择空间过载。给模型50个工具,模型每次决策都要做一次近乎全局搜索的组合匹配,想象空间一大,幻觉就多。二是上下文碎片化。自由调用链中,中间步骤的输入输出会不断挤占上下文窗口,长任务还没跑完,模型注意力已经被中间结果冲散。三是错误恢复无据可依。工具调用一旦出错,模型只能靠临场发挥,经常在一个错误分支里死循环。
技能化正是针对这三个痛点:技能把工具数量从几十个收敛到几个,模型只需要做低维的"技能路由";技能内部承载了中间状态的处理,上下文里只保留关键信息,避免碎片化;技能本身内置了异常恢复策略,比如重试、降级、明确报错,模型不需要自己现场想解决方案。
这套逻辑类似于从一个自由职业者小组,重组为一个带固定工作流程的交付团队。单看个体能力没变,但协作质量大幅提升。
1.3 整体技能集设计的四层结构
我落地技能体系时,习惯把技能划分成四层:
- 基础原子技能层:每个技能只负责一类最小可复用操作,比如执行代码、读写文件、搜索网页、调用外部API。它们是积木中的单体零件。
- 业务组合技能层:把同一业务场景内多个原子技能串联,比如"数据清洗技能"内部包含读取文件、正则处理、格式转换、写出结果。
- 决策编排技能层:面向复杂任务,Agent先把任务拆成子目标,再按依赖关系调用下层技能完成各步骤。这层技能往往写在高阶系统提示词或编排配置里。
- 元技能层:用来提升Agent自身能力,比如任务重规划、技能学习、结果自评。这类技能比较特殊,属于"知道自己会什么、不会什么"的支撑体系。
划分清楚层级以后,写技能的时候就不会把所有东西塞进一个大块头里。每一层职责明确,出现问题也好定位。实际项目中,前三层是日常工作的主力,第四层更多用于复杂场景升级。
2. 核心细节解析与实操要点
2.1 技能定义文件的核心字段
一个技能要想被模型稳定使用,不能只有一段功能说明。我常用的技能定义结构包含以下几块:
- 技能名称:简短、动词开头,一眼能看出功能边界。
- 技能描述:解释该技能解决什么问题、适合什么场景、不建议在什么场景使用。
- 参数Schema:声明需要哪些输入参数、类型、必填可选、取值范围。
- 内部工具列表:技能会调用哪些工具,调用顺序是否固定。
- 执行规则:包含步骤策略、超时策略、重试策略、错误码约定。
- 输出规范:明确输出结构,便于模型把结果接入后续步骤。
参数Schema这块特别重要。我第一次写技能参数时,只是粗略写了"输入一个日期",结果模型一会儿传"明天"、一会儿传字符串"2026年1月1日"、一会儿传时间戳,下游工具全部解析失败。后来我改成严格的JSON Schema,用枚举、format、pattern约束,并明确示例值,情况立刻好转。
{ "name": "schedule_meeting", "description": "创建日程会议,需要提供会议主题、参与者列表和时间范围。适合在用户给出会议意图时使用。", "parameters": { "type": "object", "properties": { "topic": { "type": "string", "description": "会议主题,例如:Q3需求评审" }, "attendees": { "type": "array", "items": { "type": "string" }, "description": "参与者邮箱列表" }, "start_time": { "type": "string", "format": "date-time", "description": "ISO 8601格式的开始时间,例如2026-01-12T14:00:00" }, "duration_minutes": { "type": "integer", "minimum": 15, "maximum": 240, "default": 60 } }, "required": ["topic", "attendees", "start_time"] } }2.2 如何写出让模型不误解的技能描述
技能描述的核心挑战,是让模型的语义匹配足够精准。我的经验是描述里必须包含三要素:触发条件、能力边界、禁用场合。
触发条件让模型知道"什么时候该选我"。比如一个订单查询技能不要只写"查询订单",而是写"当用户需要查看订单状态、物流信息、历史订单列表时使用"。能力边界是明说"这个技能不做什么"。比如"本技能只负责查询,不处理退款"。禁用场合往往是防止误调用的关键,比如"当用户只是闲聊订单话题,不涉及具体订单号时,不要调用本技能"。
不少开发者担心描述写太细会把上下文撑爆。实际上,技能描述一般稳定在150字到300字之间,不会对上下文造成明显压力。相比模型误选工具后走弯路消耗的token,这点开销非常划算。
2.3 技能内部工具的选型标准
同一个功能往往有多种工具实现,怎么选才是合理的?我给三个优先级:
第一优先级是确定性优于生成性。能通过固定代码、固定API实现的,就不要让模型自己写代码动态实现。比如格式转换、数学计算,直接用封装好的函数,不要给模型开放代码执行让它现场写。第二优先级是专有工具优于通用工具。比如查天气,有天气API就直接调,不要让模型去网页搜索再总结。第三优先级是带结构化输出的优先于自由文本输出。返回JSON的工具比返回自然语言的工具更容易被下游解析。
这三个标准背后是同一个原则:尽量把不确定环节往后推,把确定环节往前封。Agent系统的可靠性,本质上取决于可控部分占多高比例。
2.4 技能失败兜底的三级策略
技能执行不可能永远一次成功,我的兜底策略分为三级:
- 第一级:参数修正重试。如果收到"日期格式错误""缺少必填字段"这类异常,先根据报错自动修正参数再重试一次。这个重试必须限制次数,避免陷入死循环。
- 第二级:降级方案。主工具失败时,切到同功能的备用通道。比如主数据库连接超时,切到只读副本查询。降级要提前预设,并且要在技能输出中明确标注"本次结果来自降级通道"。
- 第三级:明确失败。两级兜底都失败,不要再硬撑,直接返回结构化错误,内容包括错误码、原因、建议模型向用户说明情况。这样模型至少能把当前状态交代清楚,而不是停留在一个看似成功实则虚假的结果上。
这里贴一个我在项目中常用的错误归一化结构:
{ "status": "failed", "skill": "fetch_weather", "error_code": "THIRD_PARTY_TIMEOUT", "attempts": 2, "fallback_used": true, "suggest_action": "告知用户天气服务暂时不可用,建议稍后重试" }3. 实操过程与核心环节实现
3.1 从零搭建一个可落地的技能体系
搭建技能体系我推荐按五步走,每一步都有明确的产出物,方便团队协作和代码评审。
第一步是任务盘点。把目标Agent的高频使用场景列成任务清单,标明每个任务的频率、复杂度、确定性、依赖资源。比如"生成周报"高频且半结构化,"解析合同"低频但复杂。这一步的核心目标是找出最值得技能化的前几个场景,而不是急着把所有场景都做成技能。
第二步是流程拆解。对选出的高频场景,把任务执行过程拆成5到10个可独立描述的子步骤。比如"制作数据报表"可以拆成:数据接入、清洗预处理、指标计算、图表选择、排版输出。拆解时要注意,每个子步骤的能力边界必须清晰,子步骤之间的输入输出要有明确约定。
第三步是工具匹配。每个子步骤对应当前已有的或需要新建的工具,记录工具名称、输入输出Schema、失败模式。没有现成工具的子步骤,标记为"待开发"。注意控制工具的功能边界,一个工具尝试做几件事,很快就会变脆。
第四步是技能封装。把子步骤按依赖关系编排进一个技能定义,写清技能描述、参数、内部流程、兜底策略和输出结构。这个阶段最需要投入精力反复打磨描述和参数,宁可多花一小时写清楚,也不要上线后让模型不断误调用。
第五步是评测验证。准备一组测试任务,包括正常任务、边界任务、异常输入,跑完看成功率、失败模式和token消耗。测试不通过就回到前序步骤修改定义,迭代三轮左右才真正稳定。
3.2 一个真实技能从设计到跑通的完整示例
我用一个"业务周报生成技能"来做完整演示。这个场景很典型:多数据源、需要格式化输出、用户需求变化多。
先做流程拆解,整个任务分为四步:数据采集、指标计算、周报起草、格式生成。数据采集阶段从三个来源取数,包括项目管理系统、销售数据库、工单系统。指标计算统一计算交付率、延期率、环比变化。周报起草用大模型根据数据指标生成段落。格式生成最后把内容渲染为固定模板。
工具侧则对应新建三个原子工具:一个数据库只读查询工具、一个数据分析工具、一个模板渲染工具。模板渲染工具接收结构化数据,用预置的周报模板生成Markdown文档。
技能定义的核心部分大概是这样的:
name: weekly_report_generator description: | 根据项目数据、销售数据和工单数据自动生成业务周报。 当用户要求"周报""本周情况""交付进展"时使用。 不处理月报、不生成图表、不发送邮件。 parameters: period: type: string description: 周区间,格式YYYY-MM-DD至YYYY-MM-DD focus: type: array items: string description: 需要在周报中重点突出的指标维度 steps: - collect_data: tool: query_business_data timeout: 30s retry: 2 - compute_metrics: tool: compute_weekly_metrics timeout: 10s - draft_summary: model_prompt: 基于指标数据生成周报叙述文字,语言简洁,结论先行 - render_report: tool: render_markdown_template output_schema: title: string metrics: object summary: string content: string fallback: - retry_with_shortened_period - error_response: 数据源不可用,请稍后重试跑通这个过程后,我发现实际效果与最初预期差异很大。第一版技能由于没有约束数据采集时间范围,模型经常默认取最近一天而非整周数据,导致指标严重偏差。后来在参数中明确要求推算自然周起始和结束日期,并提供示例值,问题才解决。
3.3 技能工作流的编排策略
单个技能内部流程相对固定,但多个技能如何组合使用,直接决定Agent能否处理复杂任务。我的编排策略是任务级拆解加子技能串联。
当收到一个复杂任务时,编排层先做任务拆解,产出子任务列表和依赖关系。这一步可以采用思维链式拆解,但拆解出来的子任务必须对应到现有技能。然后按依赖顺序逐一调用技能。每完成一个子任务,把结果摘要写回上下文,为下一个子任务提供必要输入。
需要注意的编排细节有三个:
第一是子任务之间通过结构化数据传递,尽量用JSON格式而不是自然语言描述。结构化数据让下游技能解析准确,自然语言则容易引发信息损耗。第二是控制并行度。无依赖的子任务可以考虑并行执行,但每个技能的执行时间需要做上限控制,避免个别任务拖垮整个流程。第三是中间结果需要做长度管理。长任务执行过程中,所有中间结果都塞进上下文马上就会溢出,建议每个技能只保留输出摘要,详细信息存到临时文件或外部存储。
3.4 技能体系的版本管理与灰度上线
技能定义本质上是一份可迭代的配置,版本管理必须跟上,否则改了一版后回归问题完全无法定位。
我的做法是把每个技能定义为独立文件,纳入版本控制系统管理。修改走至少两级评审,改完先跑单元测试集和回归测试集,再灰度放量到一定比例的实际流量,观察模型路由准确率和任务完成率后再全量上线。
灰度上线这一步特别重要。有一次我优化了一个技能描述,自测通过率很高,但放量后发现部分任务被模型错误路由到这个技能上,分析发现是描述里新增的一个例子与另一个技能场景碰巧重叠。还好灰度期间及时发现回滚,没有造成大面积体验问题。
实践中建议每个技能文件维护变更日志,记录每次修改的动机、影响范围、验证结果。这在持续迭代多轮后价值极大,能帮你快速找出某个行为变化是哪次调整导致的。
4. 常见问题与排查技巧实录
4.1 模型总是选错技能,怎么定位
模型选错技能,大概率是技能描述存在语义重合或边界不清。排查路径固定:先收集一批错选样本,统计每个样本命中的错误技能与正确技能。
如果错误集中在"相似的动词、接近的对象"上,比如把"生成文档"选成"搜索文档",说明两个技能描述的区分度不足。应对方法是强化区分特征,新增"何时不使用"的说明。如果错误与输入语言有关,比如英文指令更容易误选,考虑给技能描述增加英文别名或翻译。
更隐蔽的问题是技能粒度过大。一个技能涉及多个子任务时,模型容易只看到其中一个子任务就触发调用。解决办法是拆分技能,让一个技能只对应一类意图。我一般直接看技能在100条测试任务上的路由准确率,低于90%就拆。
4.2 技能内部报错导致任务中断,如何处理
技能内部报错中断,最常见的原因有三个:外部依赖超时、参数校验不到位、工具返回数据不匹配预期结构。
外部依赖超时主要是网络问题,需要设置合理的超时阈值,并在技能定义中写明重试策略。参数校验不到位,往往发生在用户提供的信息不够完整,技能内部第一步骤就传了空值。对此要在技能入口做参数补全或明确要求模型向用户追问。工具返回数据不匹配预期结构,属于接口契约问题,需要在开发期就把工具的响应Schema固定下来,并在技能包装层做字段映射。
排查这类问题最有效的手段是日志。技能执行的关键节点都打印结构化日志,内容包括入参、工具名、耗时、返回状态、错误码。没有日志做支撑,出了问题就只能靠猜,效率极差。
4.3 多技能上下文相互干扰,怎么隔离
Agent同时挂着多个技能时,技能描述本身会占用上下文,技能执行产生的中间结果也会混入会话历史。实践中常见的干扰现象是模型在处理任务A时,突然受到任务B相关上下文的提示,输出内容发生偏移。
解决办法是上下文分段管理。会话历史中只保留当前活动技能的信息,以及技能执行结果的关键摘要。更进一步的方案是为技能配置独立的执行上下文,技能运行结束只回传结构化结果,避免内部细节污染主对话流。
我实测中发现,上下文隔离做得越好,模型的任务聚焦度越高,长任务成功率提升非常明显。尤其涉及多个不同业务领域技能时,不做隔离基本没法稳定工作。
4.4 技能评估指标怎么定才有效
技能好不好,不能只看"跑通没跑通",需要用一套评估指标持续观测。我固定采用四类指标:
- 路由准确率:正确技能被选中的比例,反映技能描述质量。
- 任务完成率:技能完整执行并产出有效结果的比例,反映内部流程质量。
- 平均执行轮数和耗时:衡量效率,观察是否有冗余循环。
- 失败分布:失败集中在哪个工具、哪个步骤,指导后续优化优先级。
每轮迭代后,这四个指标必须全部重新测量。只盯着任务完成率容易忽视路由层的问题。比如测试集里30个任务都是简单场景,完成率再高也说明不了复杂场景的表现。
另外建议对测试集做分层设计:常规场景占60%,边界场景占25%,完全超出预期的场景占15%。这样最能暴露技能的脆弱点。我常遇到的情况是,常规场景全通过,边界场景一片失败,而正是这些边界场景决定了用户体验的上限。
4.5 踩坑清单
- 技能描述里不要用"等等""可能"这类模糊词,会给模型留下自行发挥的空间。
- 不要把技能参数设计得过少而撒手让模型自由发挥,参数越明确,成功率越高。
- 技能内部工具调用时,中间结果尽量使用摘要,避免长文本直接回流到会话。
- 外部依赖增加时,同步修订技能超时时间和降级策略,否则旧技能会突然变慢甚至不可用。
- 每次修改技能定义,必须更新版本号并跑回归测试,防止"改好一个,打坏一个"。
结尾
我个人在实际操作中最大的体会是:技能体系建设的核心瓶颈不在模型,也不在技术框架,而在需求拆解的清晰程度。同一个工具,放进一个鲁莽封装的技能和放进一个精心封装的技能,效果天差地别。前期多花时间去澄清触发场景、约束边界、参数规范、异常兜底,后期节省的排查时间远超过前期的投入。
最后再分享一个小技巧:每次上线新技能前,我用一组固定的"刁钻问题"去测试它,专门问那些描述边界最含糊的输入。比如一个日历技能,我就会问"如果没有特定时间,你能建议我什么时候开会吗"。这样的压力测试在正式环境暴露问题之前,就已经帮我把技能打磨到比较稳定的状态。
这个方向后续还可以做很多扩展,比如技能的自学习、技能评估自动化、多智能体间的技能复用。先把眼前的技能封装做扎实,智能体就从一个只会"接话"的程序,逐步变成一个"真能干活"的同事。