Agent Skills实战:从技能定义到调度执行的完整指南
发布时间:2026/9/25 10:57:58来源:尧图网络
聊一个最近在AI应用开发里绕不开的东西agent-skills。说白了就是给大模型配一套可复用的技能模块让它不再只停留在“聊天”层面而是能真正动手干活——查资料、算数据、发消息、操作文件、拉取第三方接口甚至按一套完整流程把事情办完。这个方向最近热度很高很多人把它当成agent落地的最后一公里我自己也是从简单的function calling一路踩坑踩过来的。如果你正在做智能体产品或者想让自己写的大模型应用真正具备执行力这篇文章应该能帮你省不少时间。我会从技能体系的整体设计讲起再拆解技能定义、注册、调度、执行、评测这几个核心环节最后把我在实际项目中遇到的坑和排查思路一并整理出来。全文不会只讲概念尽量给到可以直接复现的代码和配置思路。1. Agent Skills整体思路拆解1.1 为什么是“技能”而不是“工具”或“插件”现在很多文章把工具调用、插件、技能混着说但它们解决的问题其实不太一样。工具tool是最小粒度的原子能力比如“发送HTTP请求”“读取文件”“执行Python代码”它的特点是单一、明确、无状态。插件plugin通常是一组工具的集合带有某种业务属性比如“飞书插件”里面可以包含发消息、建群、上传文件等多个工具。而技能skill更上一层它不仅包含工具还包含这个工具该怎么用的完整说明、适用场景、输入输出规范甚至包含一些固定的执行步骤。打个比方工具是一把扳手插件是一个工具箱技能则是“如何用这套工具箱换掉一个汽车轮胎”的完整操作手册。大模型本身不知道扳手怎么用最顺手它需要的是清晰、可被它“读懂”的操作指引。agent-skills要解决的恰恰就是“模型知道有哪些能力但不知道该在什么时候用、怎么用、用错了怎么恢复”这一系列问题。从实际效果来看把技能做到位之后模型的任务完成率和稳定性会有明显提升。早期我直接裸调function calling模型经常选错函数、传错参数后来把每个函数包了一层完整的技能描述并加上触发条件和反例说明误调率立刻降了一个量级。这也是我为什么建议所有做agent的人都认真对待技能设计而不是简单列一堆function让模型自己猜。1.2 一套技能系统需要考虑哪些核心环节一个可用的agent技能系统至少包含六个环节技能定义、技能注册、意图匹配、参数抽取、技能执行、结果回传。这六个环节不是各自孤立的它们共同构成一条执行链路。技能定义是基础需要把每个技能的能力边界、触发条件、参数Schema说清楚。技能注册解决的是“系统里到底有哪些技能可用”的问题一般用一个注册中心来管理支持技能的动态加载和版本更新。意图匹配是让模型或路由模块判断当前用户请求最应该调用哪个技能这一步直接决定了技能选择的准确率。参数抽取则是把用户请求里的关键信息映射成技能入参常见的做法是让大模型基于Json Schema生成结构化参数也可以配合正则或规则抽取做兜底。技能执行阶段需要处理超时、限流、重试、副作用确认等问题。结果回传则要考虑返回给模型的内容格式——既要保留关键执行结果又不能把原始返回一股脑塞给模型否则上下文很快就满了。我见过很多项目在意图匹配和参数抽取上做得很好却忽略了结果回传的上下文管理最后模型在做多轮决策时上下文里全是无用的日志效果自然崩。2. 技能定义的规范化设计与选型分析2.1 技能描述文件的三个关键字段技能定义是整套系统里最需要花心思的地方。我建议每个技能用一个独立的描述文件来承载而不是直接散落在代码里。目前比较通用的做法是借鉴Anthropic提出的SKILL.md格式它本质上是一个带前端元信息的Markdown文件里面包括技能名称、描述、适用场景、使用步骤和注意事项。统一用这种结构化的文件管理技能后续做技能的检索、评测、版本对比都会省力很多。技能描述文件里name、description、input_schema这三个字段是最核心的直接决定模型能不能正确理解和调用技能。name要简短且表意明确例如fetch_web_page、send_slack_message。description不要写成功能说明而要写成“给模型看的调用指南”明确写出什么时候该用、什么时候不该用、有哪些边界条件。比如你写一个“查询订单状态”的技能description不能只说“查询订单”而要写清楚“当用户询问订单的当前状态、物流进度、签收情况时使用该技能。如果用户询问的是订单退款流程或售后政策请改用售后服务技能不要调用本技能。”input_schema则要严格遵循JSON Schema规范。每个参数都要标注类型、是否必填、取值范围有条件的还可以给出示例值。这里有个容易忽视的细节模型对参数描述的理解直接影响抽取质量所以参数描述也要带上语义信息。比如status参数若写成“订单状态”模型可能不知道该传什么值如果写成“订单状态可选pending、shipped、completed默认pending”模型抽取的准确率会明显提高。2.2 从MCP到原生技能协议选型怎么看技能和外部系统之间的通信协议目前主要有两条技术路线一是走MCPModel Context Protocol这类标准化协议二是直接使用框架自带的原生技能机制。MCP的好处是生态化程度高一个MCP Server一旦部署好任何支持MCP的客户端都能直接复用。它特别适合企业内部的统一资源接入场景比如把公司内部的知识库、用户系统、订单系统都封装成统一的MCP服务由平台团队维护。这样各业务线的agent不需要重复造轮子接入成本很低。原生技能则更轻量适合那些不需要跨系统复用的场景。比如你只是在某个单机应用里让模型能算个Excel、做个图表完全没必要起一个MCP Server直接在代码里注册技能函数就行。我个人在实践中更倾向于“混合策略”通用性强、需要集中管控的能力用MCP暴露业务特定的轻量操作直接做成原生技能。这样既能控制维护成本又能保持技能调用的高效和灵活。不过在选型时有一点要注意MCP的标准化也会带来一定的约束如果某个技能有非常特殊的认证逻辑或非标准参数结构强行往MCP里套反而会变得别扭。2.3 技能版本管理与灰度发布技能是会迭代的。尤其当你用大模型来解析技能描述时描述的细微调整就可能让模型的行为发生漂移。所以技能不能只放在代码库里随便改最好有一套版本管理机制。我给项目里每个技能都加了version字段用语义化版本号管理。每次修改技能描述或参数Schema都要升一个版本并在变更日志里记录改动原因。这样做的好处是线上效果出现波动时你可以快速回滚到上一个技能版本而不是跟着模型行为一起莫名其妙。灰度发布的做法也不复杂。注册中心里同一个技能可以注册多个版本调度时按策略分流比如先让5%的流量走新版本技能观察任务完成率和用户反馈确认没有明显退化后再逐步放量。现在一些Agent编排框架内置了版本控制能力比如Dify、Coze这类平台已经支持技能/插件的版本发布流程。如果你是自己搭框架也建议至少预留versions这个字段。3. 核心实操从零搭建一套Agent技能执行框架3.1 整体架构分层设计我搭建技能框架时通常分四层来处理接入层、策略层、执行层、资源层。接入层负责跟用户的对话系统对接接收用户的原始请求策略层承担意图判断和技能路由决定调用哪个技能执行层负责技能的实际调用包括参数校验、超时重试、错误处理资源层则是技能依赖的具体系统比如数据库、第三方API、文件存储等。分层的好处是每层的职责清晰出了问题也容易定位。比如用户反馈“技能没生效”你可以快速判断是路由层没有匹配到技能还是执行层调用失败还是资源层返回了异常数据而不需要从一堆缠在一起的代码里排查。这四层实现时并不一定要拆成四个独立服务很多场景下它们可以在同一个进程里。我自己的项目就是将策略层和执行层放在同一个Python服务中接入层通过FastAPI对外暴露接口资源层以客户端SDK的方式注入。整体结构不复杂但边界很清晰。3.2 技能注册中心与路由调度的实现方案注册中心的核心功能是维护一份所有可用技能的索引。每个技能注册时至少要包含技能对象、技能描述、参数Schema、优先级这几个字段。我平时会用一个装饰器来简化注册过程大概长这样# registry.py from typing import Dict, Callable, Any from dataclasses import dataclass, field dataclass class Skill: name: str description: str input_schema: dict priority: int 5 tags: list field(default_factorylist) class SkillRegistry: def __init__(self): self._skills: Dict[str, Skill] {} self._handlers: Dict[str, Callable] {} def register(self, skill: Skill): def decorator(func): self._skills[skill.name] skill self._handlers[skill.name] func return func return decorator def list_skills(self): return [ {name: s.name, description: s.description, input_schema: s.input_schema} for s in self._skills.values() ] def get_handler(self, name: str) - Callable | None: return self._handlers.get(name) registry SkillRegistry()使用时每个技能文件里通过装饰器完成注册。以“查询天气”技能为例# skill_weather.py from registry import registry, Skill registry.register(Skill( nameget_weather, description当用户询问某个城市的当前天气、温度、降水情况时使用该技能。如果用户询问历史气候或未来长期预报不要使用本技能。, input_schema{ type: object, properties: { city: {type: string, description: 城市名称如北京、上海}, unit: {type: string, enum: [celsius, fahrenheit], default: celsius} }, required: [city] }, priority5 )) def get_weather(city: str, unit: str celsius): # 实际对接天气API的逻辑 return {city: city, temperature: 25, unit: unit}路由调度的实现方案有很多种我实践下来最稳的是“分层路由”先用轻量的关键词或分类模型做第一轮粗筛缩小候选技能范围然后把候选技能的description拼进prompt让大模型做最终选择。这样做比直接让大模型从几十个技能里选要稳定得多因为技能数量一多模型的选择准确率会骤降。粗筛阶段我会给每个技能预先打标签比如“查询类”、“写入类”、“计算类”。用户请求进来后先做一次文本分类找出相关类别下的技能。然后进入精排阶段把候选技能的name、description、input_schema喂给模型让模型返回最合适的技能名和参数。这里有一个可以考虑的升级方向如果技能数量更多就把技能描述和输入样例向量化存入向量库用语义检索的方式缩小候选范围。3.3 技能执行链路与结果回传技能执行链路中最容易出问题的环节是参数校验和结果回传。参数校验不能只靠模型生成的结构化参数“自觉”在代码里一定要再校验一遍必填项、枚举值、类型。校验不通过时有两种处理方式轻微错误由模型自行根据错误信息修正重试严重错误则直接返回用户“该技能暂时无法使用”的兜底文案。结果回传我总结了一个经验无论技能返回的数据有多丰富传递给模型的内容一定要做“提炼”。我通常会把技能返回结果整理成固定格式比如状态、关键摘要、数据长度、错误信息。这样模型无需消化大量原始数据就能基于摘要做下一步判断。如果技能返回的是一个超长列表我会在前端做截断只传前10条同时标注总条数让模型知道还有更多数据可以查询。回传格式的参考结构{ status: success, message: 查询成功, result: { temperature: 25, humidity: 60 }, truncated: false, elapsed_ms: 23 }4. 实测中的踩坑记录与排查思路4.1 技能“看起来注册了就是调不到”这个是我最早踩的坑也是最容易让新人困惑的问题。技能已经写好了注册中心也能看到但实际请求就是不触发。排查下来八成是description写得太宽泛或者跟其他技能的description互相重叠导致模型在意图判断环节选了另一个技能。解决办法有两个方向。第一个是给技能desc加“反例提示”明确写清楚“什么情况不要调用”。第二个是设置技能优先级路由模块在多个技能匹配度接近时优先选择优先级高的技能。注意这个priority字段对模型选择是起不到直接作用的它主要用于我们自建路由的仲裁逻辑如果你完全依赖模型做技能选择还是要靠description写得足够精准来区分。4.2 模型把技能参数传错了怎么办模型根据用户请求生成参数时偶尔会出现“想当然”的情况。比如用户说“看看北京天气”它把“北京”两个字直接传给city参数这没问题但用户说“帮我处理一下刚刚那个文件”模型就不知道“刚刚那个文件”指的是什么了可能传一个空值或者臆想出来的文件路径。针对这类情况我在技能框架里加了一个“参数预检”环节。先把模型生成的参数和用户原始请求做一次一致性校验如果发现参数缺失、为空、或者跟上下文明显对不上就触发一轮“追问”而不是直接执行。追问模板类似“你提到要处理刚才那个文件我还未拿到具体的文件名称请告诉我文件名或文件路径。”这个追问机制看似简单但能把很多无效执行拦截在门外避免技能执行到一半才发现参数不对浪费时间和资源。另一个值得一提的经验是参数Schema里能不用free-form string就不用尽量给枚举值。比如状态、类型、类别这些字段写成枚举后模型的输出基本不会跑偏。必要的时候可以在代码里加一个fuzzy匹配函数模型返回了“beijing”或者“Shanghai6”这类值系统能自动修正为规范值。4.3 多技能并行竞争与上下文污染当两个技能在功能上有重叠时模型的行为很容易在两个技能之间摇摆。比如我同时有“发送邮件”和“发送消息”两个技能用户说“发消息跟小王说一声”模型有时候选邮件有时候选IM完全看语气和之前的对话上下文。这种问题靠改单个技能的description是解决不彻底的需要从技能目录结构上去规避。我会把有功能重叠的技能归为同一个“技能族”在注册中心里做互斥处理一个请求最多只能命中该族内的一个技能并由路由模块按照预设规则比如更具体的优先、更高优先级的优先统一仲裁。上下文污染是另一个隐蔽问题。技能执行后返回的结果如果包含大量无用信息比如日志、调试输出、原始报文这些信息会被拼进对话上下文影响模型后面的判断。我在结果回传环节做了两件事一是结构化返回内容只保留必要字段二是对超长内容做摘要或截断。实测下来上下文长度控制好之后多轮任务的成功率能提升不少。4.4 技能执行超时与重试策略设计外部依赖总是会出问题的尤其是技能调用了第三方API。网络抖动、限流、网关超时都可能导致技能执行失败。我的策略是“快速失败有限重试”。单个技能的执行超时时间设置在5秒左右超过就返回timeout错误允许自动重试1次如果第二次仍然失败就不再重试而是把错误信息返回给模型让模型决定是换一种方式完成任务还是直接告知用户当前能力不可用。这里需要特别留意“副作用操作”的重试安全。比如发送邮件、创建订单这类操作如果第一次调用其实已经成功但响应超时了此时盲目重试就可能重复下单、重复发送。对于这类技能我会强制要求接口实现“幂等键”idempotency key也就是每次请求带上一个唯一的操作标识服务端看到相同标识就返回上次的结果不重复执行。这一点在对接支付、订单、消息类接口时非常重要。5. 评测方法与效果调优5.1 用最简回归集卡住技能质量技能系统改完之后怎么知道改好了还是改坏了不能只靠直觉要有一组固定的评测用例。我建了一个小型回归集大概30条左右覆盖每个技能的正常调用、边界调用、错误调用三类场景。比如对“查询天气”技能我会放这样几条用例“北京今天多少度”应该是正常调用期望技能命中get_weathercity北京。“明天会下雨吗”是模糊调用期望命中get_weather如果框架设计了追问也可以期望触发追问。“推荐一本悬疑小说”则不应命中任何技能或者应触发兜底问答。每次改完技能定义或路由策略就批量跑一遍回归集对比技能命中准确率和参数正确率。这样能避免“改了A技能导致B技能误选”这类回归问题。评测指标我主要看三个技能选择准确率、参数填充率、任务完成率。技能选择准确率指的是命中的技能是否符合标注参数填充率衡量模型是否正确填入了必填参数任务完成率则是端到端看技能执行有没有真正解决用户问题。这三个指标各有侧重需要组合在一起看单看某一个容易产生误判。5.2 把失败案例变成改进素材评测集是固定的但真实用户的请求千奇百怪。所以光有回归集还不够要把线上失败案例回流形成迭代闭环。我在框架里加了一个日志采集模块每次技能调度、执行、失败都会记录结构化日志。每周会花一点时间过一遍失败样本挑出共性最强的那一批针对性地优化技能描述、补充反例、调整路由策略。这个环节经常能发现一些意想不到的问题。比如有用户说“帮我看看这个Excel”我们的表格技能本来只注册了读取、汇总功能模型有时候会直接尝试去修改原文件因为没有相关技能它就乱猜或者编造一个结果。针对这种情况我们在技能描述里明确加了“如需编辑文件请先告知用户当前仅支持读取和汇总”模型后续再遇到类似请求时行为就正常多了。如果条件允许可以更进一步给技能系统建立一个“行为轨迹回放”机制。记录用户原始请求、技能选择、参数生成、执行结果、模型最终回复这五层数据。当某个线上case效果不佳时按时间线回放能非常直观地看到问题出在哪个环节是技能选错了参数抽错了还是模型回复阶段出错了这样调优就从“猜测”变成了“定位”。写在最后的实操心得做agent-skills时间长了我最大的体会是技能系统本质上不是技术架构问题而是“定义问题”。模型本身足够聪明但我们得用它能理解的方式把合适的能力放到它手边并告诉它什么时候用、怎么用、用错了怎么办。与其把精力都花在训练复杂的调度模型上不如先把每个技能的description和参数Schema打磨到位这往往是最低成本、最高回报的优化手段。另外一个建议是技能的数量不要盲目扩充。很多团队恨不得把几十个能力全部塞给agent结果模型在选择时反倒变得犹豫不决。我现在的做法是“按需加载”——只会把当前场景真正高频、必要的技能放进注册中心其余技能做成可插拔扩展等用户在对话中表露出特定需求时再动态临时加载。这样既保持了系统的轻量也维持了模型技能选择的稳定性。如果你正在规划自己的agent技能体系可以先从一个高频场景的3到5个技能开始把定义、注册、调度、执行、回传这条链路彻底跑通再慢慢扩展。这套框架走顺之后你会发现agent的能力边界其实比想象中宽得多。
网站建设高端定制企业官网