
1. 为什么我会关注 agent-skills从一个跑不通的Agent说起先交代一下背景。前阵子我手头有个活儿给某内部系统做一个能自动处理工单的Agent。需求不复杂——把用户提交的工单分类、提取关键信息、调用几个内部接口、回复处理结果。代码写了一版又一版模型用的是常用的对话模型工具函数调用也配了一堆。结果跑起来之后效果让我挺崩溃的工具调用时灵时不灵模型偶尔干脆不调用工具直接开始自由发挥编答案有的场景下它会反复调同一个工具完全不看返回结果是否满足条件换个字段提法同样的指令立马跑偏。我相信凡是做过实际Agent项目的人看到这里都会有同感。当时我一度以为是模型能力不行换了更大的模型也没明显改善。后来我认真复盘发现问题压根不在模型身上而在我给模型提供的技能描述太粗糙了。我让模型去调用工具却没有把什么时候该用这个工具传入参数要注意什么返回之后要检查哪些字段异常情况怎么兜底这些信息讲清楚。换句话说我把模型当成一个靠名字就能理解工具的开发者但它其实更像一个新来的实习生你光告诉它有电话用电话它是分不清什么时候该拨、拨通了说什么、对方不在怎么办的。agent-skills 这个名字核心思路就是解决这个问题把Agent能执行的操作沉淀成一套结构化的、可复用的技能描述让模型在推理的每一个节点都清楚自己手头有什么、能用什么、用的边界在哪。这篇博文我不会去贴某个特定框架的完整配置而是围绕技能这两个字把我实际搭建、调优、踩坑的经验拆开来讲。内容适合正准备从玩具Agent迈向能稳定干活的Agent的开发者也适合已经在做工具调用但总觉得时而聪明时而智障的人参考。2. agent-skills 到底在解决什么从工具调用到技能封装的思路转变2.1 模型为什么经常不会用工具大模型本身是一个文本预测模型它并不像传统程序那样有一个明确的函数注册表。工具调用的本质是模型根据对话上下文和工具描述生成一段特殊格式的文本这段文本委托外部系统去执行真实动作。所以这里存在一个天然的信息断层模型看到的工具描述只是一段文字它只能靠这段文字推断这个工具是干什么的、参数填什么、返回值意味着什么。如果工具描述写得像一份模糊的接口注释工具名send_email描述发送邮件参数to, subject, body模型拿到这种描述几乎所有不确定的地方都要靠猜。比如to 支持多个收件人吗subject 空着会怎样body 长度有没有限制发送失败会返回什么错误发送成功后要不要提醒用户这一系列问题模型不知道于是它的行为就变得不可控有时候它把参数格式搞错有时候它绕过了工具直接生成我已发送邮件的假象。这不是模型笨而是我们作为开发者没有把语境交代清楚。2.2 技能封装与工具调用的层次差异传统工具调用我们做的事情是定义能干什么技能封装要做的事情是定义在什么情况下干、怎么干、干完之后怎么处理。可以类比成工具是一台电钻技能是一份使用电钻打孔的操作规程——不仅说电钻能打孔还规定了什么材质用什么钻头、转速调多少、打孔前要标记位置、打完后要清理碎屑。agent-skills 的精髓是把下面这些信息全部揉进一段结构化的定义中技能的触发条件什么场景下模型应该考虑调用这个技能什么场景下不该调用。前置校验规则调用前检查哪些字段、哪些条件必须满足。参数规范每个参数的类型、范围、格式、是否必填、默认值。执行过程中的约束比如超时控制、重试策略、并发限制。返回值语义正常返回的字段结构、错误码对应什么含义、哪些返回结果意味着需要进一步操作。后续动作建议根据返回值模型下一步应该继续调用别的技能、还是直接给用户回复、还是请求用户补充信息。当你把工具描述升级成技能描述之后模型不再是一个只会翻工具清单的人而是一个理解工作流程并知道怎么执行的操作员。很多之前看起来是模型能力问题的情况其实都是因为模型拿到的信息不足以支撑正确推理。2.3 什么类型的 Agent 场景收益最大基于我的经验技能封装在以下三类场景中收益最明显多步骤复杂任务比如工单处理、预订流程、数据清洗流程。这类场景需要模型在多个工具之间跳转每个步骤的输出会影响下一步的选择。没有技能描述模型很容易在某个中间步骤上犯迷糊。高风险操作比如删除数据、转账、发送对外消息。这类操作必须设置严格的触发条件和前置校验宁可让模型多问一句也不能让它鲁莽执行。工具数量较多的场景当工具超过二十个以后模型从一大坨描述里准确挑选工具的能力会明显下降。技能封装相当于做了一个目录索引让模型按场景检索而不是在一堆平铺的描述里大海捞针。3. 一次完整的技能定义以自动查天气并提醒带伞为例光讲概念肯定不够我直接拿一个最简单的例子——查天气然后提醒用户是否带伞——把一条完整技能定义拆给大家看。这个例子虽然简单但涵盖了技能封装的所有核心要素方便理解后面更复杂的用法。3.1 先看一个糟糕的版本如果你只是给模型注册一个工具描述大概长这样工具名query_weather 描述查询城市天气 参数city字符串城市名实际跑起来会出现什么状况我问明天上海下雨吗我用不用带伞模型确实调用了 query_weather但它只传了上海没有传日期因为描述里根本没提日期参数。返回结果只有上海 晴 25°C模型看到没有雨就直接回答不用带伞。问题是它查的是今天的天气用户问的是明天——这就是技能定义不全导致的连锁错误。3.2 一个好版本的技能定义我建议把一条技能写成这种结构技能名称query_weather_with_reminder 技能描述 查询未来指定的日期范围内的天气情况并根据降水概率给出是否需要带伞的建议。 仅在用户明确询问天气或用户在计划出行、户外活动时考虑调用。 若用户没有提供城市名主动询问用户所在城市若用户没有提供日期默认查询今天 若用户询问的是明天或多天后必须将日期参数显式转换为 YYYY-MM-DD 格式后传入。 参数 - city: string必填。城市中文名称或拼音示例北京、shanghai。 - date: string可选。查询日期格式YYYY-MM-DD。不传时默认当天。 - granularity: string可选。取值hourly/daily默认daily。 返回值语义 - 返回字段 precipitation_probability 为 0-100 之间的整数 - 若该值大于等于 70视为高降水概率建议提醒用户携带雨具 - 若该值大于等于 40 且小于 70视为可能有零星降水提醒用户根据出行时间判断 - 若小于 40提醒时不强调带伞。 后续动作建议 - 若查询日期与用户询问日期不符需重新查询正确日期 - 若返回值为空或出现超时错误告知用户天气服务暂时不可用请稍后再试不要编造天气数据。看到区别了吗糟糕版本只告诉模型有锤子好版本告诉模型什么时候抡锤子、用什么姿势、砸完看什么迹象。这段描述写清楚之后模型的工具选择准确率和参数填充完整率会明显上升出现幻觉式回复的概率也会降低。3.3 一个容易被忽视的关键点把判断逻辑写进技能描述很多人会问带不带伞的判断逻辑不是应该在代码里写死吗为什么要把判断逻辑塞给模型我的答案是看这个逻辑是确定性的还是依赖上下文的。如果你已经拿到了降水概率那么概率大于70就提醒带伞这个规则确实可以在代码里写成 if-else。但模型的价值在于判断是否满足触发条件以及组织最终回复比如用户说我今天要在户外跑五公里你看天气行不行模型需要综合天气、时间、用户意图来决定怎么说。这部分逻辑无法用固定规则覆盖写进技能描述可以引导模型做出合理的推理。我在实际项目中采用的做法是把规则分两层硬性规则写在代码里比如参数格式校验、超时重试、错误码映射这些不应该依赖模型柔性引导写进技能描述比如什么时候调用、根据返回结果如何组织话术、异常时如何措辞。很多团队一开始把所有规则都塞给模型结果模型被一堆约束压得推理混乱又有团队把所有逻辑都写死在代码里结果模型变得死板稍微换个表达方式就不知道怎么办。找到中间那条线是做好 agent-skills 的核心平衡点。4. 技能描述如何设计才能让模型真正看懂4.1 描述的三大常见误区在给我自己的项目做技能封装时我总结出了新手最容易犯的三大误区。第一个是描述过于简短。有些人觉得工具描述写个一句话就够了反正模型能力强能理解。但模型没有读心术你不说清楚边界它就倾向于自由发挥。更反直觉的是给模型更多上下文通常会提升它的推理质量而不是干扰它。第二个是描述中混杂了相互矛盾的指令。比如技能描述里先写仅在用户明确许可时调用后面又说若用户表达出相关内容即可自动调用模型看到这种冲突表述行为就会变得不稳定。我建议一条技能描述内的所有约束保持逻辑一致如果要修改规则从全局角度重写而不是打补丁式叠加。第三个是过度追求大而全。我一个新人阶段的通病是把某条技能描述写成了一篇几百字的小作文恨不得把各种可能情况全塞进去。实际跑下来过长的描述会导致模型在解析时失焦反而忽视关键触发条件。我后来给自己定了一个经验标准一条技能描述主体部分控制在150到250字之间参数和返回语义另用结构化方式写不用全堆进自然语言里。4.2 优秀的技能描述应该具备哪些特征结合这几个项目的实践我总结了四条判断标准主谓宾明确描述里要说得清谁在什么条件下通过什么方式完成什么目标。触发条件与不触发条件同样重要明确写什么情况下不要调用比只写什么情况下调用更能约束模型。比如技能描述里加一句若用户仅询问气温而无其他需求不要附带建议就能减少模型多余的发挥。参数说明要具体到示例级别每个参数最好带一个真实示例比如city北京、shanghai若用户说我家这边需要先明确城市名再查询。这能大幅降低参数幻觉。返回值要有人话翻译模型在调用工具后通常需要根据返回值组织回复如果你只告诉它返回JSON结构它不一定知道每个字段意味着什么。把字段含义和常见取值翻译成人话模型的后续行为会稳定很多。4.3 从用户视角反向设计描述还有一个很实用的方法我称之为反向设计。在写一条技能描述之前先不急着动笔而是设想要让一个聪明的实习生拿着这份描述去执行任务。如果这个实习生看完描述后能准确回答以下问题那么这条描述基本合格什么时候应该用我什么时候不该用我该给我什么信息哪些信息必须给我哪些可以猜我返回的结果要怎么理解哪些数据意味着出问题了我拿到结果之后下一步该干嘛这个实习生测试看起来土但非常有效。很多时候我们写描述时是站在系统调用者的视角只关注参数和接口切换成实习生的视角之后才会注意到上下文、边界、异常处理这些模型真正需要的信息。5. 在真实项目里落地 agent-skills 的完整路径5.1 技能目录怎么组织按领域分还是按动作分技能数量一多目录结构就需要提前设计。我见过两种模式按领域分工单技能、数据分析技能、邮件技能和按动作分查询类技能、写入类技能、删除类技能。按领域分的好处是模型在特定场景下面对的技能集合较小比如工单处理Agent只加载工单领域的技能检索压力小、描述可以更聚焦。按动作分的好处是跨领域复用性强例如所有场景的查询类技能共用一套参数规范和错误处理逻辑模型更容易形成统一的调用范式。我的建议是混合使用从顶层按领域分组组内按动作组织技能列表。实际在配置层面可以把领域信息写进技能描述的前缀比如工单系统技能让模型在意图识别阶段就能锁定到正确的技能集合。如果你用的是那种允许动态加载技能元数据的框架可以在意图初判之后再把对应领域的技能注入上下文减少模型在大量无关技能里做选择的负担。5.2 技能描述纳入研发流程像写接口文档一样评审很多人都没意识到技能描述其实是一种需要维护的产品资产而不是随便写写就完事的配置。我在团队内部推动过一个做法技能描述必须经过和接口文档一样的评审流程。具体来说我的流程是三步撰写者通常是后端开发者或Agent开发者先按业务方的真实需求起草技能描述。评审会上由另一个人扮演刁钻用户按照描述跑一遍对话看描述是否能支撑所有合理的用户输入变体。上线后再根据实际日志持续修正描述。这听起来有点重但Agent项目的调试成本远高于传统接口联调。你在描述阶段多花半小时后面可能少调三天的模型参数。哪些技能描述频繁导致工具调用错误哪些技能明明存在但模型一直不调用这些都可以作为评审的输入。5.3 一条务实的调试路径从单体技能到技能编排初学者在做技能封装时容易一上来就设计一大套技能编排图结果模型根本不按编排路径走最后只能不停加约束。我的建议是先从单体技能开始每个技能独立可测输入输出边界清楚模型能稳定触发、执行、处理返回值。这一步跑不稳后面的编排全是空中楼阁。等单体技能稳定之后再开始做技能与技能之间的组合。比如查天气技能和推荐穿搭技能可以组成一个新的技能天气穿搭建议技能。组合规则需要描述模型如何在第一个技能返回的基础上选择合适的第二个技能以及处理中间状态的逻辑。这个阶段通常需要给模型提供一份步骤提示例如先调用查询天气技能获得结果后再决定是否调用推荐穿搭技能。实测下来这种渐进式路线比一上来就搞复杂编排的路线稳定得多排错也要容易得多。6. 实测结果与典型翻车场景复盘6.1 技能封装前后的一组对比数据我在某模拟项目里做了一个对照组实验项目是智能日程助手功能包括创建日程、查询日程、取消日程、查找空闲时间。工具数量一共8个。第一周我以普通工具描述运行第二周我改成技能描述运行。为了让变量单一模型、参数、系统提示词都没动只改了工具描述文本。主要变化如下观测指标普通工具描述技能描述工具调用准确率74%91%参数完整率61%88%未调用工具直接回复的比例16%4%用户平均修正次数2.3次0.8次这组数据不算严谨的科学实验但趋势非常明显。值得注意的是参数完整率的变化说明很多情况下模型不是不会填参数而是你根本没告诉它需要填哪些参数。纯粹加大模型参数量不如先把技能描述写到位。6.2 三个典型的翻车场景与修复过程场景一模型反复调用同一个技能。用户问这周有哪些会议助手调用查询日程技能后返回了空列表但模型没有给用户返回本周暂无会议而是再次调用同一个查询技能来回循环七八次。原因在于技能描述里没有写如果返回结果为空直接告知用户无需重复查询。补上这个返回语义后问题消失。场景二模型在错误的语境下调用技能。用户只是闲聊了一句今天天气不错模型却把查天气技能调用了。我检查发现技能描述只写了用户提到天气时调用却没有排除闲聊场景。后来我在描述中加了一句仅在用户询问预报或需要基于天气预报做决策时调用准确率立刻上来了。场景三模型篡改参数。用户要求给某联系人发邮件收件人字段是邮箱地址但模型直接把联系人姓名填了进去导致接口报错。修复办法是在参数说明里写清楚若参数值是姓名而非邮箱地址必须先在通讯录中查询转换为邮箱地址后再调用。这就是把前置校验和转换规则写进技能描述的典型用法。6.3 日志与可观测性不能只靠肉眼调技能封装之后问题定位的难度反而可能上升因为你不仅要追踪模型输出还要追踪技能调用链路。我建议至少记录以下信息模型当前使用了哪条技能描述模型生成了什么参数工具实际返回了什么模型根据返回值生成了什么最终回复用户后续是否提出了修正性反馈这样一旦出现行为偏离就可以往回追是模型没有正确理解技能描述是描述本身有歧义还是工具的返回值格式与描述不一致我见过太多团队花了大量时间调提示词最后发现是工具返回值里的一个字段名和描述里写的不一致模型不是不听话而是被错误的数据结构带偏了。7. 技能描述与系统提示词两者分工才是关键7.1 哪些内容该放进系统提示词哪些放进技能描述很多初学者会把技能描述和系统提示词混为一谈一股脑把所有说明都塞进系统提示词。这里有一个更合理的分工原则系统提示词负责人的身份和全局规则比如你是一个日程助手回答要简洁不要编造数据。技能描述负责具体操作和动作边界比如查询日历时传入日期格式应该是YYYY-MM-DD返回空列表时告知用户暂无日程。系统提示词面向的是模型的人格和通用行为风格而技能描述面向的是具体业务动作。如果把技能细节全部写进系统提示词场景一多就会产生信息冲突和上下文膨胀因为系统提示词往往是每次请求都加载的。技能描述则可以按需加载某些框架允许在意图识别后再动态注入相关技能描述这样既保留了上下文空间又能给模型提供足够的信息。7.2 动态技能检索的必要性随着技能库扩大把所有技能描述一次性塞进上下文是不可行的。上下文窗口再大模型也会在大量相似描述之间出现注意力分散。我在一个模拟项目中尝试过把五十条技能描述全部注入结果模型频繁选错技能准确率不到六成。后来我引入了简单的技能检索机制先用模型做一次粗粒度意图识别输出一个领域标签再根据标签加载该领域下的技能描述集合。实际操作里我用的是基于关键词偏好加一个轻量级分类模型的方案效果已经足够了。更复杂的方案还可以引入向量检索把用户意图和技能描述做语义召回。但这一步不是必须的技能数量在三十条以内时分组按需加载就能拿到不错的效果。8. 技能封装之后Agent 调试的思维转型8.1 从调模型参数到调技能描述做完 agent-skills 重构后的最大感受是Agent调试的重心从钻研提示词魔法转移到了描述清晰、语义准确、边界明确。模型本身当然重要但大多数业务场景下模型能力的瓶颈不在懂不懂自然语言而在你给它提供了什么信息组织方式。我自己调试时固定用一套排查顺序先看技能描述是否覆盖了当前失败场景-再看返回语义是否解释清楚-再看参数说明是否有歧义-最后才考虑是不是模型选型问题。很多时候问题出在最前面两步这一步排查顺序帮团队省下过大量无用的大模型参数实验。8.2 版本管理与回归测试技能描述不像普通代码它作用于模型语义空间改动之后的副作用往往不是立竿见影的。所以版本管理非常重要。我的做法是技能描述文件纳入Git管理每条技能一个独立文件字段包括技能名称、生效版本、描述文本、参数定义、返回语义。每次修改必须写改动说明记录改动什么、为什么改、预期影响哪个场景。维护一份回归测试集涵盖高频场景和过去踩过坑的场景每次改动后先跑回归再上线。线上用灰度发布策略技能描述更新只对部分流量生效观察日志后再全量放量。这些做法看起来像传统软件工程的流程但放到Agent项目里同样适用。技能描述就是Agent的一份隐式代码它不直接执行却决定了模型的行为边界不认真管理一定会被埋下的坑教训。在多个项目里走过一遍之后我的体会是 agent-skills 背后代表的不是某个具体工具或框架而是一种工作思维的转变把模糊的让模型学会干活变成给模型提供清晰、完整、可验证的操作语境。如果你现在也被工具调用不准、参数幻觉、行为反复折磨我建议先从一条技能描述开始改起。你不需要一次性重写整个Agent只需要找一条最常用、最让你头疼的工具把它从接口说明改写成一份操作手册然后跑上几十轮对话对比一下。这一步做完你大概率会想回头把其他工具也全部重写——我已经替你们踩过这个坑了效果确实值得。