1. 为什么说 Agent 的能力瓶颈,早就不在模型本身了
1.1 你以为的 Agent 和实际的 Agent 差距在哪
今年 agent-skills 这个关键词在智能体开发圈子里越来越热,但说实话,很多人对它的理解还停留在"给大模型挂几个工具函数"的层面。我去年连续做了几个智能体项目,从客服助手到数据分析助手都有涉及,折腾了大半年后得出一个非常明确的结论:智能体能不能稳定干活,拼的根本不是模型有多聪明,而是它脚下那层技能体系结不结实。
大多数团队起步的方式都一样——注册一堆 function calling,把接口填进去,模型自己决定调用哪个。Demo 阶段跑得很欢,一进生产环境就露馅。我见过最典型的场景:一个客服 Agent,用户问"我的退款为什么还没到账",模型正确识别了意图,也调用了"退款查询"接口,但参数里把"退款单号"填成了"订单号",下游服务直接报错。模型也不知道怎么办,就生硬地回复"系统异常,请稍后再试"。用户当然不满意。
这类问题反复出现几次之后,我开始意识到一个事:模型本质上只是一个"会说话的调度器",真正干活的是技能。如果技能本身没有设计好,没有把参数抽取规则、边界条件、异常兜底写清楚,模型再聪明也会在细节里翻车。agent-skills 的价值,就是把"模型能调函数"升级成"模型真正具备解决一类问题的能力"。
1.2 技能与工具调用的本质区别
很多人问过我:工具调用和技能体系到底差在哪?我一般用一个比喻回答:工具是给 Agent 一把螺丝刀,技能是教会 Agent 怎么在一次完整的维修流程里正确选择螺丝刀、拧多大力、力矩不对怎么办、拧花了怎么处理。
工具调用是一锤子买卖。它暴露给模型的只有一个函数签名和一句简短描述,模型负责决定"是否调用"和"传什么参数"。至于参数错了怎么办、接口超时怎么办、返回结果不符合预期怎么办,全靠模型临场发挥。这种设计在任务单一、接口稳定的场景下够用,但一旦业务复杂起来,问题就成堆出现。
技能体系则是一个闭环,它至少包含了五个部分:意图识别边界、输入输出契约、可执行逻辑、回退策略、验证用例。我用一个表格说明区别:
| 维度 | 工具调用(Function Calling) | 技能体系(Agent Skills) |
|---|---|---|
| 边界描述 | 一句话描述"是什么" | 明确写清"什么时候用、什么时候千万不用" |
| 输入输出 | 简单参数列表 | 严格 Schema、枚举值、参数归一化规则 |
| 异常处理 | 依赖模型临场发挥 | 预设多层回退链和兜底话术 |
| 验证机制 | 基本没有 | 每个技能挂测试用例,可回归 |
| 可组装性 | 单点能力 | 可被编排进复杂任务流 |
这个区别在真实业务里的意义非常大。简单工具调用是"出了问题再说",技能体系是"设计阶段就把坑填平"。
1.3 一个真实的"没有技能体系"事故场景
讲一个让我彻底转向技能体系的事故。之前做一个电商客服助手,后端已经接好了工单查询接口和退款状态接口。一切看起来很美好,结果上线第一周就出了一个让人头大的 case。
有个用户问:"我的订单已经退款成功了,为什么钱还没到账?" 这个问题其实包含两个关键信息:退款状态、资金到账状态。但 Agent 只调用了工单查询接口,返回了一个"处理中,请耐心等待"的通用答复。实际情况是:退款审核已经通过,但打款环节因为用户绑定的银行卡过期被拦截了。用户看到"处理中"还以为自己没问题,实际上卡在了一个需要自己操作的环节,多等了三天。
事后我们复盘,根因很简单:工单查询这个函数的描述里只写了"查询工单处理进度",没写"不适用于退款到账状态查询"。模型根本不知道这个边界,只能靠猜。这类问题靠调 prompt 只能缓解,治不了本。后来我把技能层整个重构了一遍,每个技能都强制包含适用场景、不适用场景、参数抽取规则和权限校验逻辑,误用率才真正降下来。
那次重构让我彻底不再迷信模型能力,开始认真对待 agent-skills 这套工程体系。
2. 把技能拆开看:一个生产级 Skills 的最小标准结构
把技能当作一个工程单元来设计,它应该有标准结构。我目前用下来的最小可用结构长这样:
{ "name": "refund_status_query", "description": "查询退款申请的当前处理状态。适用于用户询问退款进度、预计到账时间、退款失败原因等场景。不适用于订单物流查询、工单处理进度查询、支付渠道技术故障排查。", "input_schema": { "type": "object", "properties": { "refund_id": { "type": "string", "description": "退款单号,形如 R20250101XXX" }, "user_id": { "type": "string", "description": "用户ID或登录手机号,用于权限校验" } }, "required": ["refund_id", "user_id"] }, "output_schema": { "type": "object", "properties": { "status": { "type": "string", "enum": ["processing", "approved", "rejected", "waiting_user_action"] }, "estimated_time": { "type": "string" }, "reason": { "type": "string" } }, "required": ["status"] }, "execution": { "type": "code", "entry": "skills/refund_status/run.py", "timeout_ms": 5000 }, "fallback": [ "参数缺失时尝试通过订单号反查退款单号", "接口调用失败时返回'退款状态暂未更新',不编造结果", "权限校验失败时引导用户重新登录并绑定有效支付方式" ], "tests": [ "R20250101 + 已审核 → 返回 approved", "R20250102 + 已退款 → 返回 refunded", "R20250103 + 风控拦截 → 返回 rejected 并给出原因" ] }2.1 技能描述:给 Agent 的"说明书"
技能描述是 Agent 判断"是否调用这个技能"的第一依据,但大多数人把它当成了一个可有可无的字段。我见过大量的技能描述只写一句话,比如"查询退款状态"。这句话对 Agent 来说信息量几乎为零,它不知道什么时候该用、什么时候不该用。
我现在写描述会遵循一个三段式结构:职责说明、适用场景、不适用场景。拿退款查询这个技能举例,"职责说明"是查询退款申请的处理状态;"适用场景"是用户问退款进度、到账时间、退款失败原因;"不适用场景"要更详细——订单物流、工单进度、支付渠道故障都不归它管。
有一个很反直觉的经验:不适用场景比适用场景更能防止 Agent 误调用。因为误调用大多发生在边界模糊的场景里,你把边界画得越清楚,模型就越不容易跑偏。实测下来,描述里加了"不适用"部分之后,误用率能下降将近一半。
2.2 输入输出契约:决定了一个技能能不能被安全调用
输入输出契约是技能与 Agent 之间的接口协议,它的作用是让参数抽取和结果理解都变得可预期。
输入 Schema 要尽量严格。每个字段都要写 description,枚举值列全,必要的时候给示例。为什么要这么做?因为 Agent 的参数抽取本质上是一个推理过程,它需要从用户的自然语言里识别实体。如果字段含义模糊,比如"refund_id"没写清楚格式,模型很可能把订单号当退款单号传进去。
输出 Schema 同样重要。我强烈建议所有技能都返回结构化 JSON,不要让下游模型在一个长文本里自己找信息。结构化输出意味着状态机明确、枚举可控,Agent 拿到结果以后可以直接走下一步逻辑,不用再做一次"阅读理解"。
还有一个容易被忽略的点:参数归一化。用户可能说"退款单号是 R20250101XXX",也可能说"我那个退款订单帮我查一下"。前一种情况 Agent 可以提取退款单号,后一种情况它只能拿到订单号。我在技能执行层里会加一层"参数归一化"逻辑——收到订单号时,先反查退款单号,查不到再返回参数缺失。把这类判断放在执行代码里,而不是丢给 Agent 去猜。
2.3 可执行逻辑与后备回退策略
技能的"手"是执行代码,但代码只解决正常路径,真正的可靠性来自回退策略。我在技能设计规范里要求每个技能至少准备三层回退:
- 第一层:参数修正。参数抽取不全时,用其他字段反查补全。
- 第二层:接口异常兜底。下游服务超时或报错时,返回一个可接受的结果,绝不编造。
- 第三层:权限与场景兜底。权限校验失败时,引导用户完成验证,而不是直接报错。
回退策略的价值在于:它把异常处理的逻辑从"模型临场发挥"变成了"设计阶段预设好的路径"。模型不需要知道底层为什么失败,它只需要按照 fallback 里预设的路径走,输出保证是可控的。
2.4 验证用例:技能质量的最小可运行闭环
最后一个我愿意花成本去做的部分,是给每个技能挂一组测试用例。技能没有测试就不算交付,因为技能的本质是代码,代码没有回归测试,迟早出问题。
每个技能至少配 5-10 条用例,覆盖正常路径、边界情况、典型拒绝场景、异常返回。比如退款查询技能,正常用例是"已审核→返回 approved",边界用例是"退款单号不存在",拒绝用例是"用户问物流→技能应不被调用"。这个设计的一个附加好处是,这些用例可以当 few-shot 示例塞进提示词里,让 Agent 在真实调用之前就知道"正确的输入长什么样"。
3. 技能库长大以后:组织结构与检索命中率怎么保
3.1 为什么技能一多,Agent 就开始乱挑
技能数量少的时候,一切都很美好。模型面前摆着 10 个技能,它很容易选出对的。但当技能数量超过 20 个,情况开始失控。我遇到过一个项目,技能库堆到 80 多个,结果 Agent 出现了一个很滑稽的行为:用户问"查订单",它调了"查物流";用户问"退款进度",它调了"售后单查询"。每次看日志都觉得很无语。
根本原因很简单:技能库本身成了一个没有索引的"杂物间"。所有技能的信息是平等地展现在模型面前的,模型要在几十个描述里快速找到最匹配的那个,本来就很容易受各种因素干扰——描述的长短、位置、措辞、与其他技能的相似程度,都会影响选择结果。指望模型"每次都选对",基本不现实。
3.2 分类、命名与索引:面向检索设计你的技能库
既然靠模型全量理解不现实,就必须从组织结构上做文章。我在多个项目里验证下来,一套可靠的组织方式分三层:
| 层级 | 作用 | 示例 |
|---|---|---|
| 域(Domain) | 按业务领域粗分 | 客服域、财务域、运营域、系统管理域 |
| 类(Category) | 域内的功能组 | 客服域内再分:订单类、退款类、售后类、优惠券类 |
| 技能(Skill) | 具体可执行技能 | refund_status_query、order_cancel_apply |
命名规则我也有硬性要求:动词开头 + 业务对象。比如 query_order、cancel_refund_apply、update_shipping_address。这样命名有两个好处:一是描述时天然带上了动作和对象,Agent 更容易理解;二是同类技能在检索时会自然聚在一起,方便做路由。
别名表也是必须的。用户口语里"退款""退货""refund""退钱"说的是同一个意思,但模型可能理解不到位。我会给关键技能维护一个别名列表,作为技能描述的前缀补充,显著提升意图匹配命中率。
3.3 技能描述怎么写 Agent 才能看懂
技能描述既不是给人类看的产品文档,也不是给搜索引擎看的关键词堆砌,它是给 LLM 的"意图匹配文本"。我建议使用一个固定公式:
职责一句话 + 触发条件列举 + 不触发条件列举 + 典型参数示例
举个例子,"查询退款状态"这个技能我见过最糟糕的描述是:"查询退款状态。" 三个字。模型拿到这个描述和一个叫"查询订单"的技能同时摆在面前时,完全不知道两个有什么区别。我调整后的版本是:
"查询退款申请的当前处理状态,包括退款审核进度、打款状态、预计到账时间。当用户明确提到退款、退货退钱、退款失败时使用。如果用户只是询问物流位置或工单处理进度,不要调用本技能。典型退款单号格式:R20250101XXX。"
这样一改,技能的辨识度立刻上来了,类似的描述我认为是 agent-skills 落地中最容易产生 ROI 的改动,成本几乎为零,效果立竿见影。
3.4 一个轻量的技能路由方案
当技能数量超过 50 个,只靠"把所有描述塞进上下文"的做法就行不通了,因为 token 成本扛不住,模型的选择准确率也会往下掉。这时候我建议引入一个轻量的路由层,走三层过滤:
第一层是规则过滤。按领域关键词把候选技能缩小到几个以内。比如用户提到了"退款",就直接过滤掉物流、优惠券等领域的技能。这一层用正则或者简单的匹配就能做。第二层是向量召回。把用户 query 和技能描述都做 embedding,用向量相似度召回 top-k。第三层是模型裁决。把召回的几个技能描述交给 LLM,让它选出一个最适合的,同时抽取参数。最后让 LLM 输出一个固定的 JSON 结构:
{ "skill": "refund_status_query", "confidence": 0.98, "params": { "refund_id": "R20250101XXX", "user_id": "13800138000" } }这套方案落地成本不高,但效果非常明显。我在一个技能接近 100 的项目里实测过,路由命中率从 70% 出头提升到 93% 左右。核心原因在于:模型不需要在 100 个技能里大海捞针,而是在 3-5 个候选里做选择题,难度完全不是一个量级。
4. 上线前的最后一关:技能评估体系的搭建
4.1 评估集怎么造:别拿边角料用例骗自己
很多团队的技能测试用例质量都很差,我见过最多的做法是拿五六个正常案例跑一遍,能通过就认为"技能 OK"。这种做法只能证明技能在理想路径上是通的,完全不能说明它在真实场景里可靠。
我造评估集时会刻意分配比例:主路径约占 30%,参数变体约 25%,边界情况约 20%,拒绝场景约 15%,异常返回约 10%。拿退款查询技能举例,主路径是"退款单号正确+有权限";参数变体是"用户只给了订单号";边界情况是"退款单号不存在""退款状态未知";拒绝场景是"用户问的是物流";异常返回是"下游接口超时"。
拒绝场景的用例尤其重要。一个技能不仅要能在该出手时出手,还要能在不该出手时管住自己。一个经常被误调用的技能,哪怕单次成功率再高,整体体验也是灾难性的。
4.2 评估维度:成功率、成本、稳定性、回退率
只看成功率是不够的,这是我在项目里反复踩过坑才明白的。我目前评估一个技能至少看六个维度:
| 指标 | 含义 | 参考阈值 | 为什么重要 |
|---|---|---|---|
| 调用成功率 | 技能执行后返回符合预期的比例 | ≥95% | 最基础的能力指标 |
| 参数抽取准确率 | Agent 给技能传参的准确程度 | ≥90% | 参数错是失败的第一大原因 |
| 误用率 | 不该调用却调用的比例 | ≤5% | 误用是体验崩坏的元凶 |
| 单次平均 Token | 完成一次调用消耗的 Token 数 | 尽量低 | 直接影响成本 |
| 平均响应时长 | 从用户提问到技能返回的时间 | ≤3 秒 | 体验红线 |
| 回退率 | 触发 fallback 路径的次数占比 | ≤10% | 回退率高说明技能触发条件设计不佳 |
回退率是我后来专门加的指标。以前我总是把 fallback 当作"保底机制",觉得有就行,从来没统计过触发次数。后来加了日志统计才发现,有些技能的回退率高得离谱——意味着 Agent 经常在参数不全或场景不清的情况下强行调用它,每次都依赖兜底。与其靠回退硬扛,不如回头优化触发条件和参数抽取规则。
4.3 回归问题的处理:新增技能不能牺牲老技能
技能库是一个增量系统,你今天加了新技能,明天就可能把老技能的场景抢走。我遇到过一个特别典型的回归问题:新增了一个"售后进度一键查询"技能之后,原来"退款状态查询"技能的调用量在一周内暴跌了 60%,用户问退款问题,Agent 全跑到了新技能上。但新技能其实是查售后工单的,结果就是返回值永远差一层意思。
从那以后,我定了一条铁律:任何新技能上架,必须在完整的评估集上做回归。评估集不只是一个技能一份用例,而是所有技能加起来组成一个全局回归集,任何变更都要全局跑一遍。
对于场景冲突,我还会给技能之间设优先级规则。规则就一条:更具体的技能优先于更通用的技能。举例来说,"退款状态查询"比"订单综合查询"更具体,当两者同时命中时,优先选择退款状态查询。这个优先级规则统一写在路由层里,而不是让模型自己随机发挥。
5. 落地踩坑总结:几件没人告诉我的事
5.1 技能不是代码攒得越多越好
我见过最有意思的一个反面案例:一个团队花三个月给 Agent 挂了两百多个技能,看起来功能非常全,结果各项指标全面下滑——召回准确率下降、响应时间变长、Token 成本飙升。后来他们静下心把技能砍到 50 多个,把留下每个技能的描述都重写了一遍,效果反而大幅回升。
原因在于,技能库不是仓库,它是检索空间。空间里的"物品"越多,找到目标物品的难度就越大。而且技能多了之后,必然会有大量同类技能语义重叠,模型每做一次选择都是赌运气。我的经验是:少而精永远是首选策略,新增一个技能之前,先问自己——这个需求是不是已有的某个技能改改就能覆盖?
5.2 自然语言 prompt 技能 vs 代码技能的选型
技能的实现方式可以分为两类:一类是纯代码执行,一类是用自然语言 prompt 引导模型完成。很多人不知道两者怎么选,我的判据很直接:
- 确定性任务用代码技能。比如算折扣、查库存、开权限,这类任务结果可预期,必须用代码保证准确性。
- 开放性任务用 prompt 技能。比如安抚用户情绪、生成内容摘要、判断用户意图,这些任务的输出没有唯一正确答案,适合交给模型发挥。
- 混合型任务先用代码取数,再用模型组织表达。典型场景是数据分析助手:代码负责查表和计算结果,prompt 负责把结果翻译成人话。
用错了选型会非常痛苦。我见过有人用一个超长 prompt 去让模型做复杂四则运算,结果经常算错;也见过有人用代码硬写一个开放性话术模板,结果语气僵得像机器人。选型的本质是:把确定性的交给代码,把开放性的交给模型,不混用,不整花活。
5.3 我的几条实操经验
最后再分享几条我从多次失败里攒下来的实操经验。
第一,每个技能必须带 owner 和变更记录。技能库三个月没人维护就会腐化,有了 owner 和记录,至少有人敢改、知道怎么改。第二,上线的第一周重点盯"误用率",不是盯成功率。成功率高是一个低质量信息,因为真正伤害体验的往往是很多次本不该发生的调用。第三,任何技能描述的改动,都必须把全量回归集重新跑一遍。一句话的修改可能让一个技能从"偶尔跑偏"变成"全局混乱"。第四,技能日志单独存表。记录 Agent 选中了哪个技能、它的置信度是多少、传了什么参数、最终结果如何。这套日志是技能库持续迭代的最好燃料。
我目前做智能体方案时,已经把 agent-skills 当作第一优先级来设计。模型选型可以后续调整,提示词可以慢慢优化,但技能层一旦设计歪了,后面所有环节都在将就。如果你也在做 Agent 类项目,建议把这条链路好好打磨一遍——这比换更强的模型、调更复杂的 prompt 带来的收益大得多。