写这篇东西的起因很简单我发现很多人做 Agent 应用实现了一堆 Function Call最后却撑不起一个像样的复杂任务。不是模型能力不行也不是框架不够好而是团队把精力全放在了“调用”上忽略了“技能”这件事本身该怎么设计。今天想聊的是围绕“agent-skills”这个主题我在实际项目里趟出来的经验不吹不黑都是能直接用的东西。1. 重新理解 Agent 技能它不只是 Tools而是一套能力系统1.1 为什么技能库是 Agent 工程的核心最早我写 Agent思路很简单模型需要外部数据我给它挂一个搜索接口需要操作数据库我再挂一个 SQL 查询接口。结果开发了十几个接口之后问题出来了——Agent 经常在“调哪个工具”这件事上反复纠结甚至选错工具、传错参数。后来我才意识到问题出在“工具”和“技能”这两个概念被我混淆了。工具是一个原子操作它没有上下文感知能力。而技能是一套完整的解决方案它内部封装了感知、决策、执行和反馈的闭环。以“agent-skills”命名的这套体系本质上就是在做一件事把碎片化的工具调用重构成结构化的能力单元。我举个实际例子。如果你给 Agent 一个“查询天气”的接口这是工具。但如果你的技能库里有一条“天气分析与出行建议”它就不仅仅调用天气接口了它还会根据温度、降水概率、风力这些数据结合当前日期和用户所在城市自动生成一套“今天要不要带伞”“路面会不会结冰”的输。你看同样的底层接口技能化之后任务的成功率和用户体验完全不一样。从我接触过的项目来看单纯依赖模型“现想现编”工具调用方式在任务简单时效率尚可任务一复杂就频繁出错。技能库则相当于给了 Agent 一份“经验手册”每个技能都代表一类已验证的解决方案模型只要做组合不需要重新发明轮子。1.2 技能化思维和传统 Tools 调用的本质差异我这里整理了一个对比表帮助你们把思路从“工具思维”切换到“技能思维”对比维度传统 Tools 调用技能化体系组织粒度单一函数/接口多步骤能力闭环上下文管理每次调用独立技能内部自带状态流转状态记录由外层编排维护技能内部可自主记录失败重试调用方处理技能内置降级与兜底策略模型依赖度高度依赖模型推理减少模型对流程的判断负担结果质量结构正确即可追求目标达成率允许过程中自行调整复用方式接口复用场景级能力复用说个直观体会。之前做个内部运维机器人最初用 Tools 思路写了十几个 API 对接监控系统Agent 经常把“查 CPU 使用率”和“查磁盘 IO”搞混参数名稍微变了就调用失败。后来重构为技能模式每个操作从数据获取、指标解析到报告生成全部封装在一个技能里准确率直接拉满也是从那次开始我再也没回到过纯 Tools 的写法。1.3 技能库设计的前提摸清场景需求边界在动手设计技能库之前我建议你先把场景边界理清楚。不是所有功能都要做成技能技能库越大模型做选择时的负担也越大。我在实践中养成了一个习惯凡是超过三次重复出现的任务闭环才会考虑沉淀为技能一次性场景直接编排逻辑就好。比如智能客服场景用户“查订单状态”“申请退款”“修改收货地址”这类高频高价值动作需要技能化。而那些一年碰不上几次的长尾问题不需要单独做技能交给通用对话能力去兜底就好了。成本节省是次要的关键是精度和延迟。当我把这个原则跟某个做电商客服系统的团队聊过之后他们当场就砍掉了三个使用率不到 5% 的“伪技能”模块技能选择的准确率反而提升了。所以记住这句话技能库设计的第一原则不是大而全而是精准。2. 把技能拆到多细才算合适粒度和边界的把控2.1 技能粒度选择的两个极端教训技能粒度这个事听起来简单实际操作最容易翻车。我见过把整条业务链路做成一个技能的做法比如“订单全流程处理”里头从下单到退款什么都有。这种“巨石技能”问题很明显——Agent 只要触发一次就必须走完整个流程中间想切换或分支就非常僵硬而且技能内部的错误追踪也难做往往一个环节卡住全盘卡死。还有一个相反方向的坑就是拆得太碎。曾经有人把“打开页面”和“点击按钮”都分别做成技能几十个细颗粒技能堆在一起结果模型在技能选择的环节频繁出错每次决策多消耗了大量上下文任务延迟肉眼可见地上升。我自己的经验标准是一个合理的技能应包含“输入判断、执行步骤、异常处理、结果输出”这四个要素且只搞定一个完整的业务目标。拿“退款处理”来说它就包含订单状态校验、退款原因分类、金额计算、执行退款操作、结果回传确认这几个环节但它不应该把“发送优惠券”这种补偿动作也强行塞进去那是另一个业务目标应该拆出去。2.2 用“输入输出契约”约束技能边界我认为技能边界最核心的约束方法是明确输入输出契约而且要用自然语言写好不只是写几个参数名。很多团队开发技能的时候对入参和出参的定义非常随意导致模型理解偏差甚至 Agent 卡在“参数怎么填”这一步反复重试。我习惯的写法是这样的每个技能定义里必须有一块说明文字大致描述该技能的适用范围、输入要求、输出结构和特别注意事项。举个例子“天气与出行建议”技能输入不光有“城市”和“日期”还应该写明“城市名必须是中文全称”“日期格式必须是 YYYY-MM-DD 且默认当天”。这些东西看着啰嗦但少了它们模型就可能传一些奇奇怪怪的值进去。输出契约也一样要定义清楚。比如结果必须包含“天气摘要”“通勤建议”“防护提示”三个固定段落并且每条建议后附上依据数据。这样下游技能或者前端渲染时解析成本极低不会每轮生成一个样子。2.3 技能间依赖关系的组织艺术技能不可能完全彼此独立总会有些依赖关系。有的技能需要另外的技能先执行比如“请假审批”这个技能它可能要依赖“员工信息查询”技能来确认剩余假期余额。这种依赖如果没有组织好Agent 就会把调用顺序搞乱。我采用的做法是“扁平的技能清单 技能内部的编排逻辑”而不是在技能之间画复杂的依赖图。具体来说当技能 A 需要技能 B 的结果时通常是技能 A 主动去调技能 B这种内部嵌套关系对 Agent 是隐藏的。这样做的好处是模型层面看到的仍然是一个平面技能列表选择压力小技能内部则像微服务一样自主拼装内部流程。这跟我们团队之前用过的插件化架构有相似之处。每个技能都是自包含的模块有自己的数据访问层和业务逻辑层唯一暴露给上层的是标准化接口。当上层要新增一个场景时不需要动底层技能只要组合或新增一个编排模块。3. 技能怎么写、怎么配才能真正落地3.1 技能定义的四大核心字段名称、描述、参数、返回一个可用的技能定义最少要有四个字段缺一个都可能在运行期出问题。我把这四件套挨个说透也顺便附上一个我之前在某个项目里用过的技能定义示例。首先是技能名称这个字段容易被当作摆设。我强烈建议名称用“动作对象场景”的结构比如“查询员工考勤记录并生成月度报告”不要用一个含糊的“考勤查询”了事。名称是模型首要搜索的依据它要是不能一眼看出干什么再好的描述也白搭。然后是技能描述这里要写清楚“什么时候用这个技能、什么时候不可以用”。很多人把描述写成一两句功能简介结果 Agent 在相近场景里就迷失了。我在描述里会刻意加入“适用条件”和“排除条件”避免和其他技能发生混淆。比如说一个“获取报表数据”的技能我会写它是“用于从报表中心拉取原始数据不负责数据分析如需分析请调用另一个技能”。接着是参数定义除了类型和必填性之外要给出取值范围和默认值的说明。尤其是有枚举值的情况下必须把有效值列全否则模型就可能凭经验乱填比如把“状态”传成“正常”而代码里实际只认“success”。我通常会把参数校验逻辑写在技能内部不依赖模型一定传对模型传错了我这边做一次容错校正能纠正就纠正不能纠正就明确报错。最后是返回结构。这里我不鼓励返回一段“看起来像人话”的自然语言最好是结构化的 JSON 或者表结构。为什么因为返回结果往往还要被其他技能或者渲染层继续消费。如果每次返回一个自由文本后续做格式化处理和逻辑判断都很难受。示例里我做的是一个“舆情热点检测”技能的定义这套写法已经在小范围项目里验证过{ skill_name: 检测指定平台热点话题并输出趋势摘要, skill_desc: 适用于需要分析某平台近期热点内容、总结传播趋势的场景。仅在用户明确要求检测平台话题或舆情趋势时使用不做通用信息检索。, params: [ {name: platform, type: string, required: true, description: 平台名称可选值社区论坛、短视频平台、资讯门户, default: 社区论坛}, {name: days, type: int, required: false, description: 回溯天数取值范围1-7天, default: 3}, {name: keywords, type: array, required: false, description: 限制检测范围的关键词列表为空则全量检测, default: []} ], returns: { data_type: list, item_structure: { topic: string, heat_index: float, trend: up/down/flat, summary: string } } }光看这个定义可能感受不到什么实际操作时它的价值就体现出来了Agent 拿到这个技能定义后会自动去对齐参数格式不会再把“days”搞成字符串返回处理也顺畅了。3.2 技能实现层的架构选型与代码组织定义层解决的是“模型怎么理解技能”实现层解决的是“技能怎么执行”。我项目里常用的技能实现结构分三层接收与校验层、执行层、适配层。接收与校验层负责解析模型传来的参数。这一层可以不写复杂逻辑但必须做参数格式校验和缺省值补齐。我以前偷懒跳过了这层结果上线后各种参数错误直接把业务逻辑层搅得很乱后来补上校验层错误率降低了将近一半。执行层是业务逻辑所在。这里我强调一个经验技能内部不要依赖外部的全局状态尽量把数据流显式地在内部步骤之间传递。技能可能是并发执行的多个任务之间一旦共享了可变状态排除 Bug 的难度会成倍增加。我见过一次线上故障两个技能并发写了同一个全局缓存结果数据互相覆盖排错了整整两天。适配层负责把执行结果加工成标准返回格式。这个层还承担“降级兜底”的任务如果执行层失败了适配层可以根据异常类型返回带错误码的说明有时候还能选一个备选方案去执行而不是直接把异常抛给 Agent。3.3 一个技能从开发到上线的全流程记录为了让你们有更体感的参考我把一个“会议纪要与待办提取”技能从零到上线的流程记录下来顺便说下每个环节的时间投入和踩坑点。第一步是需求拆解。这个技能要输入会议录音转写文本输出结构化会议纪要和待办事项。需求看起来简单但真正设计时发现至少有三个子问题说话人分离要不要做待办事项的负责人识别准确率怎么保证纪要和原始内容的映射怎么处理这一步花了大半天但值得因为后期所有设计都源于此。第二步是数据准备。我收集了几百条真实会议转写样本来做提示词调优。这里注意不能只用自己团队写的样例最好拿真实场景、不同口音、不同会议类型的数据来测否则很容易出现过拟合。我测试时发现模型在研发型会议上表现良好换到销售复盘会议就频频出幺蛾子就是因为样例里技术会议占太多了。第三步是编写技能逻辑。我用了一段标准函数结构执行链路是输入文本清洗、分段、摘要生成、待办抽取、结果合并。这里尤其要注意摘要和待办抽取两个步骤的衔接不能让它们各自为政否则摘要里提到的信息和待办清单对不上号。第四步是效果测试。我做了三轮测试分别覆盖标准会议、嘈杂环境录音转写、多人混说话场景。效果最差的是多人混说话的场景待办识别准确率掉到了 70% 以下。后来我在技能内部加了一个“说话人角色预判”辅助步骤准确率才拉回到 85% 以上。第五步是上线监控。技能上线不等于结束我建议至少观察两周的成功率和调用链日志。这个技能刚上线时成功率只有 91%后来从日志里看到是因为转写文本里有大量语气词和重复内容干扰提取效果我加了一层文本去噪处理成功率升到了 96%。4. 如何验证一个技能“合格”评估体系的搭建4.1 单技能评估准确率、调用成功率和兜底率技能到底合不合格不能靠感觉得靠数据。我一般用三个核心指标来衡量单个技能的质量它们分别从不同角度反映技能的可靠程度。第一个指标是准确率即技能返回的结果和人工标注的正确结果之间的匹配程度。这个指标衡量业务的正确性。比如一个技能是“提取合同关键条款”准确率看的就是提取结果能否和人工提取的条款内容一致。我不能接受准确率在 90% 以下的技能进入正式环境不是说这东西多难做而是如果基础准确率都上不去后面叠加了场景逻辑之后只会更糟。第二个指标是调用成功率指的是技能在真实环境中执行过程中不发生异常、能完整返回结果的比例。这个指标最影响用户体验。有时候准确率没问题但技能因为超时、第三方接口异常等原因频繁失败用户感知到的就是“这个 Agent 老出故障”。我习惯把调用成功率的目标定在 95% 以上。第三个指标是兜底率即当技能执行失败时能否通过内部的降级策略返回一个合理结果的比例。这是很多人忽略的点。一个合格技能应该有自己的“失败预案”而兜底率就是衡量预案有效性的数据。我曾经有过一个技能成功率不高但因为兜底设计做得好将近一半的失败情况都能返回备用结果整体用户体验没受太大影响。4.2 多技能协同评估冲突率和决策正确性单技能数据好不代表组合起来一定好。多技能协同评估我主要看两个指标技能冲突率和技能选择正确率。技能冲突率衡量的是两个或多个技能是否在相近场景下都会被错误触发。你在定义技能时如果描述写得太像就很容易出现模型一个任务先后调用了多个技能、或者同时触发了两个近似技能的情况。这时候不是单纯调描述就能解决而是要想办法从场景维度把边界划开说清楚哪个技能负责哪一段。技能选择正确率衡量的是给定一个用户任务系统能否选用正确的技能。这个指标我在每次迭代技能库之后都会跑一遍回归测试。方法是准备一个覆盖各场景的测试集里面包含几百条典型任务然后检查系统选择的技能是否符合预期。选择正确率低于 92%我是不会发版的。多说一句技能评估不是一次性工作。业务场景在变用户需求在变技能评估需要跟着变。我建议每两个星期做一次小规模回归每个月做一次全量回归。保持技能库和数据集的同步更新不然很可能做出一套“看起来挺好但跑不动”的东西。4.3 淘汰与迭代机制技能也会有生命周期技能不是越多越好也不是一旦创建就必须永久保留。我见过有的团队维护着上百个技能其中大量技能一两个月都没被调用过这种技能我一般建议直接下线。判断一个技能该不该淘汰我主要看两个数据调用频率和独立价值度。调用频率低说明命中场景少独立价值度低说明它的功能完全被其他技能覆盖。两者都不合格我就给技能打上废弃标记并从模型可调用的技能列表中移除。这不代表代码删除只是不让它干扰选择过程。同时我维护了一个技能迭代清单每次业务复盘时把使用率最高的几个技能拿出来重点看。技能迭代的目标一般是缩小输入干扰、提升极端场景的准确率、增加兜底路径。有一次我在迭代中发现某个技能的低效主要来自一段冗余的文本预处理逻辑那部分逻辑在早期版本有用后来数据源换了格式以后反而弄巧成拙删掉之后效率明显提升。这类问题只有在持续迭代中才能发现。5. 实战中的坑与排查心得从日志到上线监控5.1 日志设计看到链路里的每一步技能系统一旦复杂起来最怕的就是出问题之后无从下手。我强调的第一件事就是日志设计一定要把技能执行的链路记录下来。每个技能被调用时记录模型传进来的原始参数、参数解析后的标准化结果、每一步执行时间、中间结果以及最终返回。这听起来像基本操作但实际项目里很多日志散落在各处关联不上。我在技能执行层里增加了一个 trace_id 字段同一次技能调用全链路的日志都会包含这个字段排查问题时按 trace_id 拉日志就能看到一个完整时间线。我在真实项目里遇到过这样的情况Agent 反馈“技能执行失败”按旧日志设计只能看到最终错误信息根本不知道中间哪一步出了问题。加了链路日志之后才发现问题出在参数校验层把合法的日期格式误判成了非法值原因是校验正则写得太宽松。这种问题不靠链路日志排查难度很大。还有一个建议不要只记录正常流程的日志异常路径更要记录。我要求每个技能的 catch 块里必须输出完整异常信息包括堆栈、入参出参、当前上下文状态。这会让日志量大一些但排查效率会显著提升。5.2 典型失败模式与快速定位方法技能系统跑了这么久我总结出了几种典型的失败模式每种失败模式我都能一眼看出来。第一种是参数幻觉。模型编造了不存在的参数值比如传入了一个没在枚举范围内的状态值。这种错误第一落点就是参数校验层日志里会显示标准化失败。我一般会强化参数定义里的枚举说明并在校验层加一层相似值匹配比如把大小写错误或者常见拼写错误自动纠正掉。第二种是技能间错配。任务本该调用技能 A模型却选了技能 B。这种问题的根因通常是技能描述有重叠。我排查时会先对比两个技能的描述找出可能造成歧义的句子然后修订。修订之后跑一轮选择测试集看准确率有没有改善。第三种是内部执行超时。技能逻辑本身没问题但等待第三方依赖返回的时间太长导致整个调用链路超时。这种问题的排查方向是看超时配置和依赖接口的响应性能。我会把外部依赖调用都设置独立的超时时间并且在超时后走兜底逻辑不让主流程被拖死。5.3 上线之后我建议监控的三个核心指标技能上线之后监控这件事不能省。我最关注三个指标P95 延迟、错误率分布和用户最终满意度。P95 延迟决定了体感流畅度。有些技能在测试环境跑得挺快上线后一遇到并发就开始变慢。我会给每个技能设置一个性能基准线一旦 P95 延迟超过基准线立刻告警排查。尤其是包含多轮外部调用的技能任何一个外部接口变慢都会把整体延迟拖上去。错误率分布要看是不是集中在某个特定技能上。如果某个技能错误率明显高于其他技能那大概率是这个技能本身有缺陷而不是整体系统不稳定。我会单独拉这个技能的日志做分析。用户最终满意度才是最根本的衡量但它是间接指标。当用户觉得 Agent 回答得不行时往往不会直说“技能调用失败了”而是会说“你这答案不对”或者“根本不理解我在问什么”。我这里的做法是在用户对话里做关键词检测把包含明显负面反馈的会话自动标记出来回溯这些会话对应的技能调用记录找到问题技能的蛛丝马迹。6. 结尾说几句体己话做 agent-skills 这么长时间我最大的感受是技能库不是一套代码更像是一个不断生长的知识体系。代码只是承载真正的价值在于对场景的深度拆解、对边界条件的认真梳理、对失败机制的完备设计。很多人一上来就急着堆技能数量总感觉给模型的选择越多系统越强但实际上适度克制的技能库配合精心设计的描述与可靠的内部实现才是稳定和准确的最佳平衡。如果你正准备在公司内部搭一套技能体系我的建议很直接先挑两三个核心场景把技能做到足够深打磨到接近完美的程度再考虑扩展。一个用得顺手的技能远胜过二十个带着毛病的半成品技能。把基础打牢了后面很多事都会顺理成章。