最近我在调整团队里 Agent 的技能体系把之前散落在 prompt 里的“口头指令”、硬编码的工具调用统一收敛成了一层独立的 agent-skills 中间层。这个过程踩了不少坑也摸出了一些比较稳定的设计套路。今天就把这套东西从设计思路、实现细节到踩坑实录完整整理出来给正在做 Agent 应用、或者在纠结怎么组织智能体能力的团队一个参考。先说结论如果你的 Agent 还在靠一段又长又杂的系统提示词硬撑或者模型经常“听不懂”该调用哪个能力那么独立设计一套 agent-skills 层会很值得。它能解决三个核心问题——让模型知道有什么技能可用、什么时候该用什么技能、用完怎么反馈。下面展开聊。1. 先搞清楚Agent 的“技能”到底指什么1.1 从“功能函数”到“技能描述”的转变很多团队最初做 Agent 时思路很直接给模型接几个函数让 function calling 去调。代码倒是跑得通但模型经常在调用边缘反复横跳——要么该调的不调要么调的时候参数填得乱七八糟。问题出在我们给模型暴露的是一个“函数接口”而不是一个“技能”。函数接口关心的是类型、参数、返回值技能关心的则是这个能力解决什么问题、在什么场景下触发、有哪些边界和约束。同样是“查询天气”一个裸函数get_weather(city: str, date: str)让模型自己猜怎么填和一个完整的技能描述“当用户查询某地未来几天的天气情况时调用本技能城市传中文名日期默认为今天”效果完全不同。我当时重构 agent-skills 层时给每个技能配了三样东西一段面向模型的能力说明、一个结构化的参数 Schema、一份面向系统调用方的执行代码。前两者是让模型“看得懂”最后一个是让系统“跑得动”。缺了第一块模型只能瞎猜缺了第三块再好的描述也只是纸上谈兵。1.2 为什么传统 function calling 不够用传统的 function calling 本质上是“一个函数 一份 JSON Schema”模型在推理时会从候选列表里挑一个函数并生成参数。但在真实场景里问题往往更复杂技能数量多上百个函数塞给模型光是寻址就够模型喝一壶的选择一多准确率直线下降。技能有前置条件比如“创建报表”前可能需要“选择数据源”“配置维度”这些状态依赖在裸函数里是表达不出来的。技能有失败反馈函数调用失败时模型需要拿到结构化错误才能决定下一步是换参数重试还是换技能裸函数抛个异常就没了下文。agent-skills 的做法是把“技能”当成一个带有完整上下文的独立单元每个技能不仅描述自己能做什么还描述不能做什么、失败时怎么反馈、调用前后有什么状态变化。这样一来模型面对的不再是一堆平铺的函数列表而是一本清晰的“能力手册”。2. 技能体系的设计思路与核心原则2.1 技能最小化一粒一粒地拆而不是一坨一坨地糊设计技能库时最容易犯的错就是“贪大求全”。我见过有团队把“处理订单”做成一个技能里面包含了从下单、支付、查物流到开发票的全部逻辑。结果就是模型一碰到订单相关的活儿就调这个巨无霸参数二三十个经常填错。按我的经验技能拆分的标准应该是一个技能只做一件不可再分的事。下单就只做下单支付就只做支付。数据血缘上每个技能有明确的输入、输出和副作用。判断拆得够不够细可以问自己一个问题如果这个技能被复用到一个完全不相干的任务里是不是只需要很少的适配成本比如“查今天天气”和“查下周天气”应该共用同一个“查天气”的技能只是参数不同而不是两个独立的技能。2.2 可发现性让模型一眼就懂什么时候该用我技能描述不是写给程序员看的文档是写给模型看的“使用说明书”。同样的逻辑技术文档写清楚参数类型技能描述要写清楚触发场景。我后来固定了一套描述模板技能名称动词 宾语如“查询实时天气信息”功能概述一两句话说明这个技能到底做什么适用场景具体到什么样的用户请求下应该考虑使用本技能不适用的场景明确说哪些情况不要用避免模型误调用参数说明逐个参数解释含义、取值范围、默认值、示例值返回结果说明成功返回什么失败返回什么特别注意“不适用的场景”这一栏这是很多设计里缺失的。模型不知道某个技能不该用才会发生“用查询天气的技能去查股票”这种离谱调用。你明确写“本技能仅处理天气信息不涉及任何投资建议”模型就能少犯很多低级错误。2.3 可组合性让技能之间能互相协作单个技能是原子任务但真实业务往往是多个技能串联。比如“帮我看看北京明天适不适合跑步”就需要地理编码技能算出经纬度再调天气技能拿预报最后用“运动建议”技能结合空气质量给出判断。在设计技能体系时要刻意保持技能的“输入输出规范化”让一个技能的输出可以直接作为另一个技能的输入。我常用 JSON 作为技能间流转的统一数据格式每个技能返回结果里带一个data字段和一个meta字段前者是核心数据后者是状态信息。这样模型在规划多跳任务时心里有数——前一个技能的data能喂给后一个技能的哪个参数。3. 实操一套可以照抄的 agent-skills 实现方案3.1 技能定义一份 YAML 让模型和系统都满意我在项目里用yaml文件定义每个技能好处是可读性好做版本管理方便还能直接生成模型上下文。下面是一个非常典型的技能定义name: query_weather description: 查询指定城市在指定日期的实时天气和未来预报 triggers: - 用户询问某地天气 - 用户询问某地是否适合出行/运动 not_triggers: - 查询股票、汇率等金融信息 - 查询历史天气数据暂未支持 parameters: city: type: string description: 城市中文名如“北京”“上海” required: true example: 北京 date: type: string description: 查询日期格式 YYYY-MM-DD默认今天 required: false default: today example: 2025-06-15 returns: success: description: 返回天气状况、温度区间、风力等级、建议 schema: ... # JSON Schema 省略 failure: description: 返回错误码 error_code 和简短描述 error_message把这份 YAML 渲染成自然语言描述拼进 system prompt再配合底层 function schema 做调用模型就能很清晰地理解这个技能。3.2 技能注册与发现做一个轻量级“技能注册中心”当技能数量到几十个时一股脑全塞进上下文是浪费 tokens 的也容易干扰模型判断。我建议做两层检索第一层是粗筛把技能按领域天气、交通、财经、办公……打标签用户请求进来先做一次意图分类只加载相关领域的技能。第二层是精排在相关领域内用 embedding 做语义匹配把最相关的三五个技能挑出来再拼进 prompt。这个思路很像搜索引擎的召回 排序。我实际测试下来几十个技能的场景里全量放进 prompt 的准确率可能只有七成而分层检索后能稳定在九成以上token 开销还低了。需要说明一下这种“粗筛 精排”是业界在技能选择上比较常见的做法具体实现细节你可以按自己的场景调整。3.3 技能执行与反馈回路技能执行的核心不只是把代码跑起来更关键的是把执行结果“翻译”成模型能理解的结构化反馈。比如{ ok: true, data: { city: 北京, date: 2025-06-15, temperature: 25~32, condition: 多云转晴, humidity: 45% }, meta: { latency_ms: 320, source: cn_weather_service } }如果调用失败我的习惯是返回一个带error_code和retry_advice的结构。这个retry_advice是给模型看的“下一步建议”比如“参数 city 不能为空请向用户确认城市名后再试”。这比干巴巴地抛异常对模型友好得多。技能执行这一层我建议加一个超时控制和幂等设计。外部 API 可能超时所以单个技能执行要设硬超时比如 10 秒写操作类技能要做幂等避免 Agent 重试时产生重复订单或重复消息这个坑我是真实踩过。4. 我踩过的坑那些让 Agent 翻车的技能设计细节4.1 描述“太抽象”模型识别不了调用时机最开始我写的技能描述文绉绉的“获取天气数据”“执行算术运算”。模型根本不知道什么时候该触发。后来改成“当用户询问未来几天天气情况尤其是外出安排、运动计划时使用”效果立竿见影。核心原则是描述的触发条件要贴近用户的自然表达宁可啰嗦一点也别让模型靠猜。4.2 参数设计“太深”模型填不对还爱硬填有些技能的参数是嵌套对象比如{location: {lat: 39.9, lng: 116.3}}。模型其实很难从一串英文名里正确推理经纬度取值。与其让模型去“算”不如增加一个辅助技能来做转换或直接让技能内部去完成 mapping。举个实际的场景用户说“我要查广州的天气”你非要模型传一个city_code257模型大概率会编一个。正确做法是让参数保持为“广州”技能内部去查城市代码表。把复杂的转换逻辑放到技能内部而不是丢给模型准确率会提升很多。4.3 技能粒度过大牵一发动全身在技能设计里我一开始把“生成周报”做成了一个技能内部自动汇总数据、分析趋势、写标题。听起来挺酷但后来产品要支持“只给标题不写正文”的场景只能被迫拆技能。拆完反而好用了get_weekly_data、analyze_trend、generate_summary三件套既支持全流程自动跑也支持模型根据用户需求跳过某个环节。4.4 失败反馈不完整Agent 一条道走到黑还有一个隐蔽的坑技能执行失败后Agent 会尝试重试如果你没有告诉它该怎么改它就会用同样的错误参数反复调用直到撞上重试上限。给失败反馈加上retry_advice后Agent 就能拿到“有效指导”。比如话说错了明确提示“用户请求中不包含城市名向用户询问完整城市名后再重试”模型就会先追问用户而不是盲目瞎试。别小看这一处它把多次调用失败的成功率从五成拉到了九成。4.5 别忽略了权限与安全边界这是个很实际的工程问题。技能不是无限被调用的像“删除数据”“发送消息”这类写操作必须有权限校验。我的设计是给技能打上permission_required标签执行前先做校验校验不通过直接返回“未授权”的提示同时告诉模型不要尝试绕过。这既是工程安全需要也是在给模型兜底别让它承担自己不该承担的责任。5. 技能运行时的观测与持续调优5.1 给技能调用做可观测性设计一套技能体系上线不重要上线后能不能持续优化才重要。我在日志系统里为每次技能调用记录几个关键字段skill_name哪个技能被调用了input_args模型填的参数output成功/失败的结构化结果latency_ms耗时token_cost上下文消耗trace_id关联到具体的会话链路这几份数据积少成多就能算出模型在哪些技能上老是走弯路集中精力修。5.2 指标体系三个最有用的效果指标我建议盯三个指标调用准确率模型该调这个技能的场景里有多少真正调对了。参数命中率调用技能时模型给出的参数有多少是合法且可用的。任务完成率从用户原始请求到最终得到满足的闭环比例。不用做得很复杂就这三项已经能帮你找出大部分问题。如果调用准确率低优先查技能描述如果参数命中率低优先查参数设计如果任务完成率低就要看整个多技能协作链路哪里断了。5.3 迭代节奏先补描述再调参数最后改代码我个人的修 bug 顺序是固定的先看是不是技能描述不清晰这是最常见的问题再看参数设计是否合理是不是要求模型去做了它不擅长的事最后才去看执行代码本身的 bug。按这个顺序排优先级能少走弯路。另外建议做好 A/B 对比。同一个技能把描述方案 A 和方案 B 分别开放给部分流量跑几天看调用准确率。不用等用户反馈数据自己会说话。6. 一些更进阶的玩法技能体系一旦稳定可以往上叠加很多能力。我最近在尝试的是“动态技能启停”根据会话上下文决定哪些技能对当前模型可见。比如用户在聊天气就不需要把写代码的技能暴露给他。这既省 token又降低误调用率。还有一个方向是把技能做成可插拔的“插件市场”。团队里每个人都能上传自己做好的技能 YAML 代码统一审核后就能被所有 Agent 共用。这就把技能体系从“一两个工程师维护的代码库”变成了“整个团队共建的能力生态”。我个人的体会是agent-skills 这套东西没有太多高深的算法本质上是把“模型怎么理解能力”这件事想清楚了然后用工程手段把它固化下来。你不需要一次性把技能库搭得很大找一个最常用、最容易出错的场景先拆出一个技能写好描述跑通观测再慢慢扩。这个方式稳扎稳打最后出来的技能库会比一上来就追求大而全的方案可靠得多。
阅读完成 · 觉得有帮助?