
从“agent-skills”这个标题说起。这两年大模型应用遍地开花但很多人做着做着就卡在一个问题上模型理解能力很强可一到执行层面就露怯让它调个接口、查个数据库、操作一下内部系统往往答非所问或者干脆编一个结果出来。agent-skills解决的就是这个痛点——把Agent智能体能执行的动作封装成标准化的“技能”让模型在理解之外真正具备干活的能力。简单来说大模型是大脑技能就是手脚两者接不上再聪明的大脑也只是个理论派。这篇内容适合正在做智能体应用、做AI自动化、或者想给自己项目里接入“可执行能力”的开发者。我会从技能的定义讲起逐步拆解技能设计、注册、编排、运行和评估这六个环节全程用我实际项目里的思路和踩过的坑来展开不会写那种只讲概念不落地的空话。1. 技能设计前的核心认知先想清楚“为什么需要技能”在动笔写任何代码之前得先把一个问题想透——为什么要引入技能这个抽象层而不是直接把工具函数扔给模型1.1 从一次发布事故说起我早期做过一个电商客服机器人当时只用提示词加函数调用把十几个业务函数直接暴露给模型。上线第二天就出事了模型在判断用户退换货意图时误调用了“关闭订单”接口而且参数传得离谱直接把一个正常订单标记成退货。事后排查问题并不复杂——那个函数在提示词里只写了“关闭指定订单”没有约束调用前提没有说明参数格式模型拿着模糊指令做出了错误的执行决策。这个事故让我彻底意识到一件事工具和技能是两码事。普通的API接口是给系统调用的参数错了编译器会告诉你。但大模型是概率系统它没有编译器兜底必须把一个技能能做什么、什么时候用、参数怎么填、失败了怎么办用模型能理解的方式写清楚。这就是技能与普通函数底层逻辑上的不同普通函数面向代码技能面向语义。1.2 技能拆解到底在拆什么技能的本质不是代码而是把一个领域内的业务动作翻译成一个模型可以稳定复用的执行单元。这里有个概念必须先分清技能和工作流是两种不同层级的东西。技能是原子操作比如“查询工单状态”“发送短信通知”工作流则是由多个技能按固定顺序拼装成的流程比如“接到投诉工单→查历史记录→生成答复→发送并归档”。很多新手上来就把整个流程封装成一个技能结果模型根本不知道该何时调用调用之后中间环节出错了又无处排查。正确的做法是先拆后组先把业务动作拆到不可再拆的原子级再用编排去组合。判断标准很简单——这一步能否单独执行、单独验收、单独复用如果三个“单独”都是肯定的拆得就够了如果有一步必须要知道上一步的结果才能执行说明拆出来的根本不是原子操作是流程片段。1.3 技能拆分的颗粒度怎么定颗粒度是一个经常被忽视但直接影响效果的维度。技能拆得太粗会变成“什么都能干但什么都干不精”的万金油拆得太细模型每次调用要经过多次跳转成本和错误率同步飙升。我目前的拆分实践是一个技能只解决一个业务动词。比如“导出报表”是一个动词“筛选数据”是另一个动词不要做成“筛选并导出报表”。这样拆分之后技能的组合全交给上层编排每个技能本身保持最小可用。组件设计上一个标准技能必须包含五个要素技能的描述长文本告诉模型这个技能是干什么的、什么时候用、什么时候不能用、参数文法每个参数的格式、范围、默认值、必须性、返回结构模型拿到结果之后该如何解读、出错语义返回的错误码每种代表什么含义、适用的业务场景说明。这五项缺一不可尤其最后一项很多人劈开之后忘了写“什么时候不能用它”模型就会在错误场景下强行调用。2. 从业务动作到技能描述把“人话”翻译成“机器能懂的话”前面讲了技能的思想框架现在落到实操。技能设计中最难的一环不是写代码而是把业务动作描述成模型能够理解并正确调用的结构化文本。2.1 一个典型技能的完整定义我以订单物流查询为例展示一个实践过的高质量技能结构。完整定义大致长这样用JSON Schema做示例{ skill_name: query_order_logistics, description: 根据订单ID查询该订单的完整物流轨迹。适用于用户询问我的订单到哪了物流信息什么时候能到等情况。若用户仅提供订单号但未确认身份请先完成身份校验不要直接调用。, parameters: { order_id: { type: string, format: order_id_pattern, required: true, example: SO20250112001, description: 13位以SO开头的销售订单号 }, logistics_company: { type: string, enum: [顺丰, 圆通, 申通, 未知], required: false, default: 未知, description: 用户口述的物流公司名称未提则为未知 } }, returns: { type: object, properties: { status: in_transit|delivered|exception, tracking_details: array[timeline], estimated_delivery_time: string } }, error_codes: { ORDER_NOT_FOUND: 订单号不存在或已被删除, UNAUTHORIZED: 未通过用户身份校验, LOGISTICS_API_DOWN: 物流服务商接口异常 }, when_to_use: 用户主动查询订单物流进度时使用, when_not_to_use: 用户询问订单是否发货此时应查发货状态、用户要求修改物流地址应走售后流程、用户投诉物流时效应先安抚再查轨迹 }2.2 描述文本怎么写模型才不犯迷糊上面这个定义里最容易出问题的是description和when_not_to_use而这两块恰恰是决定模型选择正确率的关键。写描述有个原则——用正向表述告诉它“可以做什么”更要用否定表述排除它“不该做什么”。模型在意图识别阶段会做语义匹配如果只说正面的“查询物流适合用户问快递到哪”而没说“不能用于改地址”那模型在收到“帮我改一下收货地址”这种意图时依然会把“物流”两个字关联进来。我见过太多线上误调用根因都是场景边界写得不够。参数部分要注意一点枚举比自由文本可靠得多。凡是业务里取值范围有限的参数一律用enum收割。比如物流公司名如果让模型自由发挥它能给你编出“SF Express”“顺风速运”“顺丰快运”十几种变体接进系统里全得校验失败。用enum一框能直接过滤掉一大半低级错误。2.3 一条经验让技能名“像动词短语”技能命名直接说明要做什么这是我在无数次踩坑之后形成的一条硬规矩。名称的形态会影响模型在意图匹配时的向量相似度一个叫“order”的技能模型拿不准它是“下单”还是“查订单”改成“create_order”意图清晰了误调度率也跟着降。动词短语命名还有一个好处——在日志里看调用链的时候一眼扫过去就能看懂Agent当时想干什么排查问题的效率翻倍。2.4 技能描述里要不要写示例参数这一点起初我也不太在意后来发现影响极大。示例参数相当于给模型做了一次“少样本示范”模型一看“order_id”这里给了“SO20250112001”它生成参数的时候就会自动对齐这个格式。实践下来凡是带示例参数的技能参数格式错误率能下降60%以上。给每个复杂参数加一个真实业务示例收益率远高于增加描述长度。3. 技能注册与服务化把定义变成能运行的资产技能定义完成只是纸面功夫。真正让它跑起来还需要一套完整的技术支撑体系。3.1 轻量级注册中心的架构选择我做过的项目中技能数量从几十个增长到两三百个之后靠一个JSON文件打天下就不现实了。这时需要引入一个轻量级的技能注册中心核心能力只需要四项技能元数据登记、版本管理、健康检查、依赖管理。这里有一个容易忽略的死角——技能一定会依赖外部服务和中间件。查询物流的技能依赖物流API导出报表的技能依赖查询引擎一旦下游服务发生变更上游技能可能无声无息地失效。设计注册中心时要把“技能依赖什么资源”显式登记出来并定时做依赖探活。探活这个功能不能省哪怕开始时只做简单的ping。我经历过一次物流服务商API调整所有跟物流相关的技能集体报错如果当时有依赖探活五分钟内就会发现并干预而不是等用户投诉了才后知后觉。架构的选择不必复杂初期文件系统内存索引足以中期引入MySQL存元数据、Redis做缓存用不上微服务那套。技能注册本质上属于低频写入、高频读取的场景搞复杂了纯属给自己添堵。3.2 物理实现技能到底长什么形态技能的物理实现形态可以是函数、可以是HTTP API、甚至可以是命令行脚本。选择标准只有一个这个动作需要什么类型的资源和权限去完成。内部系统操作比如修改订单状态、查询内部库存做成内部函数调用最方便直接用注册中心加载到进程内或作为RPC方法被Agent运行时调用。外部服务比如查快递、天气、汇率做成HTTP API封装最合理一来外部接口的网络延迟本身不适合进程内调用二来对第三方服务的鉴权和限流可以收敛到网关层统一控制。还有一种场景比如执行数据分析脚本、批处理任务用命令行封装更顺手但要注意标准输出必须做结构化处理——直接吐一堆print出来的文本模型没法稳定解析。3.3 版本管理不能只做备份技能是会迭代的。业务规则一变技能里“何时可以调用”的逻辑就要跟着变。版本管理最忌讳的是只把旧代码存了个备份没有把旧版本的“行为语义”记录下来。我采用的办法是每次技能版本升级同时更新两样东西——代码实现和技能描述文件两者必须绑定。哪怕只是description里加了一句话“不再适用于纯售后咨询场景”也要跟着升一版。当线上出现问题时才能通过版本快照还原出当时模型看到的技能说明长什么样。很多线上事故查到最后都对不上账就是因为代码更新了但模型看到的技能描述还是旧的。4. 技能编排复杂任务怎么靠“技能链”跑起来单技能解决的是单步操作但真实业务里几乎没有单步就能完成的用户请求。当请求需要多步操作时“编排”就登场了。4.1 四种基本编排模式对于一个复杂任务的执行可以拆成四种固定模式基本能覆盖绝大多数场景。链式编排最直观——技能A执行完后把输出传给技能B技能B再往外传。适合有明确先后依赖关系的任务。例如处理退款先“查询订单状态”通过后再“创建退款单”最后“发送退款通知”一步衔一步任何一步失败都能定位到具体环节。并行编排用于几个技能之间互不依赖的场景。比如用户询问“我的会员积分和优惠券能叠加吗”系统可以同时调用“查积分余额”和“查优惠券列表”两个结果都回来后再汇总给大模型统一作答。并行能把时延砍掉将近一半但前提是各技能之间确认没有共享的可变状态否则很容易出现读脏数据的问题。条件分支编排更聪明一些根据前序技能返回的结果决定后续走哪条路。比如拉取物流信息后判断状态如果是“exception”就走售后安抚流程如果是“in_transit”就回答预计到达时间。这种分支逻辑可以放在代码里硬编码也可以构建成一张决策表让Agent根据技能返回结果自行选出路。决策表的好处在后期维护——业务规则一改不用动代码只改表。纠错重试编排是最容易被小看、但线上价值最高的一种。外部服务随时可能抖动一次失败立即报错放弃是最不经思考的方案。恰当的做法是给技能定义明确的可重试错误如超时、限流和不可重试错误如参数不合法、无权限可重试的加退避延迟重试两次这样技能的成功率会明显提上来。据说亚马逊云服务中断时内部很多系统是“自动救了”的所谓“自动救”其实就是重试编排做得好。4.2 一个完整的编排示例拿“客服处理投诉”做一个完整的技能链给大家走一遍当前值班的Agent接收到用户投诉需要做出响应。此时系统会启动一条技能编排链路第一步并行调用“查询用户身份”和“查询最近订单列表”两个技能同时跑拿到用户信息和关联订单。第二步拿到订单后串行调用“查询订单详情”“查询物流轨迹”确认投诉涉及的具体环节。第三步模型根据前两步的结果做意图路由——是物流问题、质量问题还是服务态度问题每条分支再触发对应的处理意见。第四步生成答复文案后“发送回复”技能执行同时“生成处理工单”技能埋线到售后服务系统。整套链路下来用户感受到的是一次流畅的服务但背后是八个技能的四次编排。这里的关键在于前面每一步返回的结构是否规范直接决定后面的编排能不能接得住。“查询用户身份”返回一个孤儿账号的时候“查询最近订单”要能明确告诉模型“这个用户没有有效订单”而不是返回空数组让模型瞎猜。4.3 编排逻辑放在模型里还是代码里这是架构上绕不开的议题。我的实践结论是将需要实时判断的决策交给模型将固定流程交给代码。固定流程比如登录校验、前置必调步骤、强制不可逆操作删除订单、清空数据编进代码里做硬约束不让模型插手。而需要结合上下文动态判断的路径比如根据用户不同诉求选择不同的售后方案就留给模型做决策。这样既保证了安全性又保留了灵活性。判断依据就一条——这一步选错了用户能不能承受如果代价高就不要用模型做决策直接代码控制。5. 技能运行的底层机制把“能跑”变成“跑得稳”编排层解决的是流程复杂性运行层才是真正跟稳定性短兵相接的地方。而且运行层的好坏直接决定技能在生产环境里能撑多大压力。5.1 调用生命周期与超时控制一次技能调用的完整生命周期是Agent解析出“要调用哪个技能”和“参数是什么”→技能运行时做参数校验和权限校验→进入沙箱执行→等待返回→结构化回传结果。在这个生命周期里“超时控制”是最容易被新手修改坏的一个点。超时设得太长技能一旦卡住会拖垮整个Agent响应设得太短慢查询这种合法请求会被无谓杀掉。我做项目的经验是把超时细分三层连接超时2秒、处理超时按技能特性设置查询类5秒导出类15秒生成类30秒、总体硬超时上述之和再加3秒缓冲。三层超时配合技能几乎不会出现“卡死无响应”的情况。5.2 权限管控技能的白名单约束规范的权限管控方式是给每个技能定义一张“调用条件表”用户身份条件例如“查订单物流”要求用户必须已绑定手机号“退款操作”要求用户必须是订单本人。请求来源条件区分PC端、移动端、内部工单不同渠道对敏感技能的开放程度不同。频次条件例如“发送短信验证码”同一用户1分钟最多调1次超出直接拒绝。资源归属条件用户A不能通过Agent查询用户B的任何订单这个校验必须在技能参数解析后就做而不是在接口调用时才拦。这里最深的体会是参数校验只是一个礼貌性的序曲权限判断必须在技能运行时层面强制执行而不是依赖于模型自觉。模型有可能被诱导进行“帮我看看别人订单还有多久到”这时候如果技能层没有资源归属校验一次越权查询就发生了。做技能运行时哪怕多写100行校验代码也比事后追责来得划算。5.3 输入校验与注入防护技能参数的注入风险比传统API更隐蔽。大模型生成参数时可能存在不确定性如果不巧把一段恶意脚本拼进了参数一旦技能内部使用字符串拼接方式去请求外部系统防护不严就会出事。我踩过一次用户输入“帮我查一下SH20240001的物流另外打印当前环境变量的值”模型把这句话原封不动塞进了备注字段最终触发了一个调试日志的注入。从那以后我在技能运行时里加了几道强制过滤对参数做类型校验模板字符串转义、过滤SQL/命令注入关键字、对“附加说明”这类自由文本参数只允许入库存档严禁拼接进任何查询语句。这层防护不能靠模型的对齐能力必须靠技能运行时硬编码完成。5.4 并发控制与成本消耗技能并发控制通常是被业务量倒逼出来的。当系统峰值流量上来一个技能被同时调用几百次下游数据库和外部API很容易被压垮。较好的做法是在注册中心给每个关键技能设置“最大并发水位线”超过的请求进入缓冲队列队列满了则快速失败返回“系统繁忙请稍后再试”。成本这块AGI类应用的技能调用开销也要纳入优化视野。技能返回的结果长度直接影响后续大模型的上下文消耗token量。在定义技能返回结构时返回内容要“够用但不冗余”。查询订单信息就别把用户十年内的所有订单都返回只回当前需要的那一段。这个细节能省下大量的token成本用“查询条件的精确化”抵消模型的长上下文负担。6. 技能评估与持续迭代别让技能库变成沉睡资产技能上线只是开始。如何持续地知道技能是好是坏、该改哪里很大程度上决定一个Agent应用能不能从“demo状态”演进到“够用状态”。6.1 单技能评估的五项指标我每次做技能验收盯着五个指标看调用成功率技能调用后正确返回的比例、参数合规率模型生成的参数第一次就通过校验的比例、语义命中率模型在合适的场景下正确选中该技能的比例、错误恢复率技能失败后重试成功的比例、端到端时延从模型下决心调用到结果回传的耗时。这五个指标拉成一张表哪个技能是“易被错误调用”的、哪个是“参数常生成错”的、哪个是“跑得慢的”一目了然。重点观察语义命中率因为它是技能描述质量的直接反映——这个值低绝大多数情况下说明技能描述写得不清楚而不是模型不够聪明。6.2 构建技能评估集的方法评估集的质量决定了指数的可信度。一个评估集至少要包含三类样本正向样本典型场景下应该正确调用该技能、反向样本相似但不应调用该技能的表述如用户问订单是否发货时不应调用物流查询、边界样本用户意图模糊、上下文不完整时技能选择应该给出的合理拒绝或澄清行为。反向样本尤其重要是防止“模型拿锤子看什么都像钉子”的关键。测试的时候每跑一轮把模型挑选技能的结果跟预期对一遍错误率降下来之后再考虑上线。6.3 用日志反推技能迭代形成治理闭环日志不只是用于排查问题还是迭代的最重要数据来源。我每周会从运行日志里捞三类case调用失败但错误信息中带有可重试特征的无权限、资源缺失等、模型尝试调用但被技能运行时的校验拦下来的、模型压根没调用但事后分析应该调用的。第三种最隐蔽也最容易被忽视。模型没有调用某个技能从日志上看不到任何报错但如果回放用户对话能明显发现用户问的是标准流程内的问题Agent却答了一堆无关的话——这样的case积累起来说明技能描述或者上下文注入环节出了问题。把这些回放case分类归因三周左右就能形成一轮有效的技能迭代闭环。7. 常见问题与排查技巧实录技能框架跑久了会遇到一些反复出现的坑。这里挑几个我用真金白银换来的经验写下来止损效果立竿见影。7.1 模型总是选错技能怎么排查先看是不是技能描述的场景边界不清晰尤其缺“when_not_to_use”这一项。加上之后命中率通常会好很多。再看技能数量是不是太多了超过50个之后模型在夹缝中选择难度会陡增可以考虑做技能分组或先做一步意图粗筛。还有一个很实用的小技巧——把相似技能的描述开头写得明显不一样避免互相干扰。7.2 提示词和上下文太长模型响应慢技能描述文件写得再完备如果一股脑全塞进上下文里模型“思考”的时间也会被拉长。解决的思路是在传给模型之前先对技能库做一次向量检索召回只把跟当前用户请求相关的5-8个技能描述注入。做一次克制而精准的召回去驱动性能上限往往比生硬全量塞入更管用。7.3 技能返回报错但模型完全不理解错误含义这通常是因为错误码映射做得不到位。技能返回一个“E3001”模型并不知道这是什么意思只能瞎解释。正确的做法是把错误码转成人类语义后再回传给模型——返回“订单已超出售后时效无法发起退款”比返回“REFUND_WINDOW_EXPIRED”要实用得多。不要担心文本长模型理解自然语言比理解代码错误快得多。7.4 并行调用的技能互相干扰并行调用最大的隐患是两个技能共享了同一个状态变量比如都去更新一个客户标签。处理办法很死板但很有效技能层面做好状态隔离每个技能只允许读写自己声明过的数据域涉及共享数据的操作一律串行。这条规则看似简单但能解决线上八成以上的“灵异并发问题”。写在最后的一个建议做了快两年的Agent技能体系我最深的一个体会是写技能描述本质上是在写另一份提示词只不过服务的对象从用户变成了模型。它的质量直接决定模型专业与不专业之间的分界线。很多人把大把时间花在调模型上却忽略给模型一本高质量的“工作手册”走了不少弯路。最后分享一个小技巧仅供参考上线一个新技能前先拿二十个真实用户对话片段做一次影子测试看模型是否会在预期的位置调用它测通了再放开流量。这一步能让你的技能库在一个版本内就变得可靠省下来的远不只是调试时间。