1. 为什么要把 Agent 的能力“技能化”一个没被说透的问题先聊个我自己的经历。去年我接到一个需求让内部客服 Agent 学会“查订单”“改地址”“算退款金额”这三件事。一开始很天真直接往 system prompt 里塞操作说明结果模型经常把“改地址”和“算退款”搞混该查物流的时候去算钱该改地址的时候把订单状态背了一遍。折腾两周后我意识到问题不在模型而在我的组织方式——我把“能力”写成了“一段话”而 Agent 运行时需要的其实是“一组可被精准唤起、稳定执行、能独立评估的技能模块”。这也是我想认真聊agent-skills的原因。它不是一个具体产品的名字而是一种设计思路把 Agent 能做的事拆成一个个离散的、有明确边界、可复用、可测试的“技能单元”然后像搭积木一样组装起来。如果你正在做 AI Agent、自动化工作流、或者任何让大模型去操作真实系统的项目这篇文章的内容大概率能用上——尤其是当你发现 Prompt 越来越长、模型行为越来越飘、新需求一来就改坏旧功能的时候。我在实践里把“技能”定义为三件事的组合一段给模型看的描述、一套入参出参规范、一段真正执行动作的代码或 API 调用。听起来简单但真正把这三件事做好涉及到描述怎么措辞、参数怎么设计、技能之间怎么避免互相干扰、上线之后怎么评估——这些才是agent-skills真正值钱的地方。1.1 先从一次失败的自动浏览任务说起为了让你直观感受“没技能化”的痛点我复盘一个真实案例。当时我想让 Agent 自动去某电商后台导出一份“近30天退货率超过15%的商品清单”。我最初的 Prompt 长这样你是运营助手。请打开后台找到商品数据模块选择退货率维度筛选近30天导出Excel。结果模型开始自由发挥它先调用了一个“打开网页”的工具然后试图自己识别页面上的按钮、猜测数据模块的位置、甚至尝试用 JavaScript 去点击页面元素。整个过程极其不稳定——页面稍有改版就失败网络慢一点就超时而且每次执行路径都不一样根本没法复现。后来我把这个任务拆成了三个技能query_product_stats接收日期范围、统计维度、筛选条件返回结构化数据。export_excel接收表格数据生成 Excel 文件并返回下载链接。generate_return_analysis接收商品数据自动产出退货率分析结论。每个技能背后是一段稳定的 API 调用Agent 要做的只是根据用户意图选择合适的技能、填对参数。效果立竿见影执行路径从“大模型自由发挥”变成“固定技能编排”哪怕中途某个技能失败也能单独重试不用整条链路从头再来。这个案例说明一个核心道理大模型适合做“决策”不适合做“过程”。你把过程写得再详细它也容易在执行中变形但你把它封装成“技能”模型只需要回答“现在调用哪个技能、参数是什么”剩下的交给确定性代码。1.2 技能、工具、工作流千万别混为一谈很多文章把 Skill、Tool、Workflow 混着用实际设计时含糊不得Tool工具最小粒度的可执行动作比如“查询订单”“发送邮件”。它不关心你要解决什么任务只负责把一件事做对。Skill技能面向某一类场景的能力封装内部可能调用多个 Tool并附带调用策略。比如“处理退货申请”这个技能内部会先调“查订单”判断是否在退货期再调“算退款金额”最后调“发送通知”。Workflow工作流面向一个完整业务流程的编排把多个 Skill 按固定顺序串起来常有状态流转和分支判断。我建议你用“技能”作为主要的复用单元原因有三第一它粒度适中比 Tool 更贴近业务表达便于产品经理参与定义第二它比 Workflow 更灵活同一个技能可以被不同工作流复用第三它是可独立评估的最小单位——你能单独测试“处理退款申请”这个技能的好坏而不必跑完整条流水线。后面所有内容都围绕“技能”这个层级展开。先把概念边界理清再往下走不会跑偏。2. 技能的四要素描述、参数、触发逻辑与执行体一个能被 Agent 稳定调用的技能在设计层面至少有四个组成部分一段高质量的描述、一套明确的参数规范、一个清晰的触发逻辑、以及一个可靠的执行体。这四个要素我逐个拆开讲。2.1 描述Description怎么写模型才真正“看得懂”描述是整个技能里最容易被低估的部分。大多数人写描述时习惯写“这个技能用来查订单”但模型真正需要的是“什么场景下用、什么时候不要用、有哪些注意点”。我自己写描述的经验可以压缩成三条第一条用“当……时使用该技能”的句式明确触发条件。例如description: 当用户想查询订单物流状态、包裹当前位置、或预计送达时间时使用该技能。 如果用户只是询问商品信息或售后政策不要使用该技能。第二条写明“不做什么”。负面约束比正面描述更能防止误调用。我见过一个查天气的技能因为描述里没写“不处理空气质量”结果用户问“今天PM2.5高不高”时模型也调用了天气技能——虽然也能返回温度但答非所问。加上一句“不处理空气质量、不提供穿衣建议”之后误调用率直接降了一半。第三条描述里不要堆砌执行细节。你不需要告诉模型技能内部调了几个 API、用了什么算法这些信息只会干扰模型的选择。描述的唯一使命是帮助模型判断“这个技能适不适合当前用户意图”。2.2 参数与调用协议从混沌到规范的演进技能的参数设计直接决定调用成功率。早期我犯过把参数写得过于“灵活”的错——比如给“查订单”技能只定义一个query参数让模型把订单号、手机号、日期全塞进去。结果模型经常塞一坨自然语言进去代码端还得做各种解析麻烦至极。后来我接口规范改为“强类型 必填项优先”parameters: type: object required: - order_id properties: order_id: type: string description: 订单号格式为 14 位数字字母组合 customer_phone: type: string description: 用户手机号用于辅助校验可空强类型有两个好处一是模型更容易理解每个字段的语义二是执行端可以立刻做参数校验不合法就直接报错重来不用等调用 HTTP 接口才发现问题。另一个容易忽略的细节是参数里的“格式示例”——给模型一个真实的格式样例比写十句正则规则都管用。2.3 触发逻辑显式注册还是自然语言唤起技能触发有两种主流方式显式注册和语义唤起。显式注册就像给技能一个“函数名”大模型从候选列表里挑语义唤起则是把技能描述混进上下文让模型根据语义自主决定。我现在更倾向于显式注册 语义评分结合的方案——先让模型从候选技能列表里做一次粗选再用描述做一次“这个技能是否真的适合当前问题”的二次确认。你别小看这个二次确认。有一次我只做了粗选模型错误地选取了“生成周报”技能去回答“帮我总结一下这个项目上周进展”——从语义上“总结”和“周报”确实接近但用户要的是简报不是结构化周报。加了二次确认让模型先回答“用户意图是否匹配该技能的核心触发条件”之后这类错误少了很多。3. 从零搭建一套 Agent 技能库目录、版本与评估机制纸上谈兵说完了下面聊落地。搭建技能库这件事不是写几个 YAML 文件丢进文件夹就行它涉及仓库结构、权限边界、评估机制三个层面。3.1 技能仓库的目录设计我踩过的组织方式我最早把技能文件按“功能模块”分目录比如order/、refund/、customer/结果发现一个问题一个技能往往横跨多个模块。比如“处理退货申请”既涉及订单又涉及退款放进哪个目录都别扭。后来我改为按“场景域”分目录 按“技能名”索引的双层结构skills/ ├── domains/ │ ├── order_after_sale/ │ │ ├── handle_return.yaml │ │ ├── calculate_refund.yaml │ │ └── query_logistics.yaml │ └── customer_service/ │ ├── check_policy.yaml │ └── escalate_complaint.yaml ├── shared/ │ ├── notify_user.yaml │ └── check_permission.yaml └── registry.jsondomains/放业务场景技能shared/放跨场景公共技能。registry.json是技能注册表记录每个技能的 ID、版本、依赖关系、启用状态。这样设计的好处是业务同学看domains/就能理解覆盖了哪些场景开发同学通过registry.json可以快速定位一个技能被哪些工作流引用公共技能单独维护避免每个场景都复制一份“通知用户”的实现。还有一点值得提技能文件我建议用 YAML JSON Schema 组合而不是直接用 Python/JavaScript 代码。YAML 描述能力边界和参数 Schema代码只实现“执行体”。这样非工程背景的人也能参与评审技能的触发条件和描述是否准确。3.2 给技能加“护栏”输入校验与权限边界技能一旦被 Agent 自动调用就相当于把某个操作权限交给了模型。不加护栏后果很严重。我见过不止一次因为参数校验缺失模型把“删除用户”的 ID 传成了别的用户 ID差点酿成事故。我的强制性护栏有下面这五条每个技能在注册时必须声明所需权限比如permissions: [order:read, user:write]调用时由权限中间件统一检查。所有参数在进入执行体之前做 JSON Schema 校验非法输入直接拒绝不让脏数据进入业务代码。涉及写操作修改、删除、发送消息的技能必须支持 dry-run 模式默认先预览影响范围。技能执行必须有超时控制和重试上限避免模型陷入无意义的循环调用。全量操作类技能如“清空列表”“批量删除”必须二次确认由用户明确说“确认执行”才真正运行。这五条里前两条是技术底线后三条是流程底线。我见过很多团队前两条做得不错但第三条几乎没有——他们觉得 Agent 直接执行操作就行直到一次误发营销短信给全体用户才意识到 dry-run 多重要。3.3 技能评估集不发版就上线是在赌运气评估是技能库建设里最容易被跳过的环节。很多团队测试技能只靠“聊两句看看效果”这完全不够。我给自己的技能库建了一套“评估集”机制核心是把历史上真实用户的请求脱敏后收集起来标注预期应该调用哪个技能、参数怎么填、结果是否可接受形成一份可回归的测试样本。评估集分三层第一层是标准样本每种技能至少 20 条典型请求覆盖常见说法和近义表达。第二层是边界样本故意用歧义、口语化、带噪声的表达测试技能的触发边界比如“我要退钱”到底该走“退款计算”还是“退货申请”。第三层是负样本明确不该触发该技能的表达。负样本的用途是防止技能被过度唤起。每次修改某个技能的描述、参数或执行逻辑都必须跑一遍整个评估集并对比“技能命中率”“参数准确率”“执行成功率”三个指标。命中率保证该触发的能触发准召率保证不该触发的别乱触发成功率保证执行端不会因为输入不合规而频繁失败。这套机制帮我挡住了很多次“拍脑袋改描述引起的回归问题”。4. 实战中的四个高频坑描述冲突、上下文膨胀、串行死锁、召回失灵理论讲完上一波动真格的。下面这四个坑是我在过去一年做agent-skills过程中反复遇到的每一个都花了好几天排查写出来给你避雷。4.1 坑一两条技能描述“打架”模型选了错误的执行路径现象是技能库里有“查订单”和“查售后单”两个技能用户说“帮我查一下我那个退货的单子到哪了”模型选了“查订单”结果查出来的订单状态是“已送达”用户根本不是在问这个。排查链路是这样的。我先抓了模型调用日志发现模型调用的是query_order而非query_after_sale——说明它没有识别“退货的单子”应该走售后单接口。然后我对比了两个技能的描述发现问题在于两个描述的边界重叠太多query_order的 description 里写了“支持查询订单状态和物流信息”query_after_sale的 description 里写了“支持查询售后单状态和退货物流”两者在“物流”这个语义上几乎一致。模型很难从描述里看出“退货物流”应归售后单。修法是把两个技能的描述边界完全切开。我重新写了query_order的描述description: 当用户想查询正常交易订单的状态、物流或完成时间时使用该技能。 不处理退货、退款、售后类单据查询。然后在query_after_sale的描述里加了明确的“迁移信号”description: 当用户提到退货、退款、售后、换货等关键词想要查询相关处理进度时使用该技能。 注意用户说“订单”时不一定是普通订单需结合上下文判断是否属于售后单据。关键是最后那句“注意”相当于主动提醒模型做一步语义判断。改完后这个场景的命中率从 57% 提到了 91%效果显著。4.2 坑二技能说明太长上下文被无关 token 吃满另一个常见问题是“技能描述越写越详细恨不得把接口文档都塞进去”。我见过有人把技能描述写到上千字还附带两三段示例对话。结果每次 Agent 运行光技能描述就占了大几千 token主任务还没开始上下文就被吃掉了三成。排查方式是看 token 消耗报告。某次我发现一个仅包含 6 个技能的 Agent每次对话消耗的 token 比预期高出 40%逐一统计后确认是某个查询类技能的描述太长。更麻烦的是长描述还影响模型的注意力——越长的描述关键触发条件越容易被淹没在细节里模型反而抓不住重点。我后来给技能描述定了硬指标常规技能描述不超过 300 字核心触发条件必须在前 50 字内出现任何操作步骤、接口细节、错误码说明一律移出描述放到技能内部的文档字段中模型默认不读取。这样既保证了触发准确率又把上下文占用压了下来。4.3 坑三技能互相调用出现循环等待技能之间支持互相调用是我很早就加的功能——比如“处理退货”会调用“查订单”和“算退款”。但有一次线上出现了超时风暴Agent 在处理一件退货时先是调了“查订单”确认订单状态然后调“算退款”计算金额算完后“处理退货”又尝试调用“查订单”做二次校验……两个技能互相依赖对方的返回结果各等 30 秒超时最后整条链路卡死。排查链路比较惊险。我先是在监控里看到所有相关技能的 P99 延迟飙升然后逐步加日志发现calc_refund在等待query_order返回而query_order又在等待某个权限校验接口而那个权限校验接口因为在等“上游技能释放锁”一直阻塞——本质上是技能调用形成了一个环形等待。修复方案有两个一是在技能内部禁止循环调用在注册表里维护依赖有向图构建时做环检测二是在调用层加递归深度限制比如最多嵌套 3 层超过就返回“需要人工介入”。我两个都做了。前者是从根上避免设计失误后者是兜底防止 Logging 临时改出的依赖链出问题。4.4 坑四同一技能在长短对话中的召回率差异这个坑比较隐蔽。测试单个技能时你直接发一句“帮我查一下订单”技能召回率很高但在完整对话中用户可能先聊了十分钟别的突然来一句“那现在呢”或“所以这个怎么办”此时如果上下文里没有明显的技能关键词模型就容易漏召回。我最初以为是上下文窗口问题后来仔细看日志发现长对话里模型的注意力被大量无关对话稀释了它做技能召回时过分依赖“最近几轮”而不是“整段对话的核心意图”。比如用户前面一直问退款政策中间聊了几句天气最后问“那我什么时候能收到钱”模型居然去调用了天气技能或闲聊处理器。解决办法是两层。一是在每轮对话结束时由 Agent 维护一个“当前意图摘要”把它注入下一轮决策的上下文中二是在召回阶段不是只看用户最后一句而是把最近五轮的“关键词快照”和当前摘要一起作为技能匹配的输入。改进后长对话召回率从 68% 提到了 87%。这算是个偏工程化的手段但对真实场景很有用。5. 技能升级的两种思路拆细与合并以及我的取舍标准技能库用得久了一定会遇到“技能数量太多”或“技能功能太杂”的困扰。这时候要决定是继续拆还是合并。没有统一答案但可以给一些判断标准。5.1 拆细一个操作一个技能什么时候是过度设计拆细的好处是每个技能职责单一、容易评估、修改风险小。坏处是技能数量飙升模型在数百个技能里做召回准确率必然下降。我目前的经验是如果某个技能在评估集上的命中率长期低于 80%优先考虑是描述问题还是边界问题而不是急着拆只有当用户意图确实存在多个完全不同的场景时才拆。举个例子“发送消息”这个技能最开始包含“发送邮件”“发送短信”“发送站内信”三种渠道。后来发现用户说“用短信发验证码”时模型经常误选成“邮件”因为描述里三种渠道是并列关系模型容易混淆。这时候我拆成send_email、send_sms、send_station_message三个独立技能并在send_sms的描述里写上“仅用于短信渠道不处理邮件和站内信”。拆完后验证码场景的准确率从 72% 提到 95%。判断拆不拆的标准很简单如果混淆频繁发生在某两个场景之间说明这两个场景的用户意图、参数结构已经足够不同值得拆如果混淆只是偶发的措辞问题先调整描述不要急着拆。5.2 合并聚类成领域的“老板键”什么时候是捷径合并的反向操作是把多个技能聚合成一个“领域技能”内部再做分流。适用场景是用户意图的表层表达变体极多但深层目的高度一致。比如“查天气”“查空气质量”“查紫外线指数”“查穿衣建议”本质都是“获取某地当前环境信息”拆成四个技能会让模型疲于分辨不如合并成一个query_environment技能内部用一个info_type参数做细分。我合并时的取舍标准有三条如果多个技能的参数结构高度相似仅在一个枚举字段上不同优先合并。如果多个技能的被调用场景经常同时出现比如用户问完天气顺带问穿搭合并更有利于上下文连贯。如果某个技能在负样本上的误触发率偏高合并反而是“收拢边界”的手段——把容易混淆的意图放进一个技能用参数区分描述反而更清晰。合并也有代价技能内部要承担一定的分流逻辑代码复杂度上升评估集必须覆盖所有子场景。我的原则是“能用合并解决的绝不拆必须拆的按业务语义拆而不是按功能实现拆”。6. 一套可复用的技能开发流程从需求文档到灰度发布最后分享一套我在实际项目中沉淀下来的开发流程。它不是 PPT 里的流程而是每条都踩过坑后固化的动作。6.1 步骤一用真实对话记录锚定技能边界我接到一个新的能力需求时不会立刻写代码或写描述。第一步永远是翻历史对话记录找出最近两周内所有与这个能力相关的用户表达至少收集 50 条。然后把这些表达按“触发该技能”“不该触发但像该技能”“边界模糊难定”三类分组。这个分组结果直接决定技能描述怎么写、负样本怎么准备。很多人忽略这步看到需求就拍脑袋写描述结果界是“拍”出来的不是从真实数据里“长”出来的。这步耗时可能两小时但能省下后面两周的返工。6.2 步骤二写描述时先写“不做什么”我的描述写法是先负后正第一步写“在下列情况不要使用本技能”第二步写“在下列情况使用本技能”。先写负面的好处是模型在歧义场景下更容易“排除错误项”而不是靠正面描述去“猜正确项”。这符合大模型对否定指令相对敏感的特性——你明确说“别做A”它的行为稳定性比只说“做B”更好。比如一个“生成绩效总结”的技能我会先写“不要在用户未提供任何量化数据时生成总结不要将周报内容直接改写为绩效总结不要主动生成个人评价”然后再写正面触发条件。这套反向描述帮我压掉了两个最头疼的误触发场景。6.3 步骤三小流量灰度与回归清单技能上线不是全量直接切。我的做法是先按 10% 流量灰度 3 天重点看两个指标——调用率是否落在预期区间、执行失败率是否低于阈值。然后逐步放大到 50%、100%。放大过程中只要任一指标出现跳变立刻回滚到上一版本并保留当时流量中的评估样本用于分析。同时每次技能改动都必须跑一遍已有的评估集没有评估集覆盖的技能不允许发版。这条规则听起来很死板但正是因为有了它我才能放心让 Agent 在无人盯守的情况下自动调用新增技能——因为每次发版前至少我知道它不会把“查天气”误调用成“订机票”。这套流程走下来单个技能从需求确认到全量上线通常三到五天。速度不算快但换来的是少在半夜爬起来处理误调用事故。对我来说值。
阅读完成 · 觉得有帮助?