☰
Agent Skills 实战:从技能设计到落地踩坑全解析
2026/10/7 4:05:59 网站建设 项目流程

别急着去看那些眼花缭乱的 Agent 框架和所谓“智能体编排平台”,先想一个问题:你手里那个看起来很聪明的 Agent,真正让它从“能聊天”变成“能干活”的,到底是什么?

答案大概率是 skills——技能。这也是 agent-skills 这个主题近段时间反复被拿出来讨论的原因。模型本身只是大脑,而技能是手和脚。一个 Agent 能不能落地到真实的业务场景里,取决于你给它装了什么样的技能、技能定义得够不够清晰、执行链路有没有兜底。今天这篇就围绕 agent-skills 这个方向,从设计思路到实现细节,再到我踩过的坑,一次性聊透。

1. Agent 的核心不是模型,是技能

1.1 为什么“能对话”不等于“能干活”

大语言模型最擅长的事情是生成文字。你说“帮我查一下上海明天的天气”,模型可以给你写一段像模像样的回答,比如“上海明天多云,气温 12 到 18 摄氏度,东南风三级”——但它并不知道真正的天气。它只是在预测“这段文字出现在答案里的概率比较高”。

要让 Agent 真正去查天气、查库存、调接口、改数据,就必须让它具备调用外部工具和执行具体操作的能力。而这一层能力,行业里通行的做法就是把它抽象成“技能(skills)”。

技能的本质是什么?我的理解是这样:技能是“意图到动作的映射”。模型在对话或者推理的过程中,识别出用户意图,然后从技能清单里选一个匹配的技能,填入参数,触发执行,再把执行结果带回给模型,由模型组织成自然语言回复给用户。

所以,评价一个 Agent 靠不靠谱,不取决于它用的是什么模型,而是取决于它的技能体系完不完整、技能调用准不准、执行稳不稳。

1.2 agent-skills 到底指什么

从工程上看,agent-skills 可以拆成两个层面:

第一层是技能本身。也就是一个可以被 Agent 调用的函数、工具、API 或操作流程,它有一个名字、一段描述、一套参数定义,以及一段可执行的逻辑。

第二层是技能的管理与调度。包括技能怎么注册、怎么被发现、怎么被模型选中、参数怎么校验、错误怎么处理、执行完的结果怎么反馈。

如果你的 Agent 只挂了三个技能,那也不需要什么复杂体系。但当技能数量增长到几十个、上百个,技能之间的命名冲突、描述模糊、参数歧义、上下文干扰这些问题就会全面冒出来。这个主题想解决的就是这一整个生命周期的问题。

1.3 适合哪些人关注

  • 正在做 AI 应用落地,觉得模型“不够聪明”但其实问题出在工具层的人;
  • 想把 Agent 接入实际业务流程,但不知道怎么组织和设计技能的人;
  • 已经跑通了一个 demo,但技能一多就乱,想让 Agent 更稳定的开发者。

2. 技能体系整体设计思路

2.1 技能分层:别把所有东西平铺在一张表里

我见过不少团队的第一版技能列表,就是把所有功能平铺在 JSON 文件里。比如:

{ "skills": [ "查天气", "发邮件", "查库存", "创建订单", "查快递" ] }

这看起来简单,但马上会碰壁。技能数量超过十个以后,模型在做工具选择时经常选错或者犹豫不定,因为平铺的清单里缺少“领域感”。打个比方,这就像你打开手机通讯录,所有联系人按首字母铺排,想找一个经常联系的人反而要翻半天。

更靠谱的做法是做技能分层。我在实际项目里会把技能分成三层:

  1. 基础技能:不可再拆的原子操作。比如“查天气”“查汇率”“发 HTTP 请求”“查询数据库”。
  2. 复合技能:由多个基础技能按固定流程组合而成。比如“下单”可能由“查库存”“创建订单”“发送通知”三个基础技能组成,但对外暴露成单一技能。
  3. 流程技能:涉及多轮交互、条件判断、人工确认的技能。比如“报销审批”这种流程,Agent 不能一次性执行完,需要中间暂停、等人确认再去下一步。

分层的好处有两个:一是模型在选技能时,候选列表更短更聚焦;二是复合技能可以被复用,不用每个场景都从头编排一遍。

2.2 技能注册:让 Agent 知道“你有什么”

技能不是写在代码里就结束了,Agent 要能“看到”每个技能的存在,并理解它的用途。这个过程叫技能的注册与发现。

常见的注册方式有三种:

  • 函数级注册:把每个技能封装成函数,用装饰器或注解声明技能名和描述,启动时自动扫描。
  • 配置级注册:把技能定义写在 YAML 或 JSON 配置里,运行时动态加载。
  • 目录级注册:技能拆成独立模块或微服务,通过接口注册到技能中心,Agent 运行时动态拉取技能清单。

我推荐的方式是配置级注册 + 函数实现分离。理由很简单:技能名和描述是交给模型看的内容,改动频率高;函数实现是代码逻辑,改动需要测试。把它们分开,可以让“调技能文案”和“改技能逻辑”互不干扰。

2.3 技能描述:写不好描述,再好的技能也没用

这是被很多人忽视但极其重要的一个环节。模型是通过技能描述来理解“这个技能是干嘛的”的,描述写得模糊,模型就会乱用,甚至无视这个技能。

好的技能描述应该包含四要素:

  1. 功能定义:这个技能能做什么,一句话讲清楚。
  2. 适用条件:在什么场景下才应该使用这个技能。
  3. 不适用条件:什么情况下不要用,避免误调。
  4. 参数说明:每个参数的含义、类型、取值范围、是否必填。

我举一个实际例子。假设你写一个“股票查询”技能,糟糕的写法是:

查询股票信息。

模型看完这句话,不知道“股票信息”具体指什么,不知道是查价格、查市盈率还是查公司公告。更好的写法是:

根据股票代码查询指定股票的最新交易价格。使用场景:当用户询问某只股票今天的价格、涨跌幅、成交量时。不适用场景:用户询问股票的历史K线图、公司财报数据时,请使用其他技能。参数:stock_code(string,必填,6位数字股票代码)。

写清楚之后,模型选错技能的概率会明显下降。别嫌这一步啰嗦,技能描述是模型理解你系统的最主要入口,省掉的每一句话最终都会变成用户看到的错误回答。

3. 手写一个技能模块的完整实操

3.1 技能定义文件怎么写

内容安全是底线,这里直接展示一个我从实际项目里简化出来的技能定义示例,结构可以直接抄。

{ "skill_name": "query_weather", "display_name": "天气查询", "description": "根据城市名称查询未来5天的天气预报。当用户提到天气、下雨、温度、降水等词语时使用。", "input_schema": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,如北京、上海、广州", "required": true }, "days": { "type": "integer", "description": "查询天数,取值范围 1-5,默认 1", "required": false } } }, "output_schema": { "type": "object", "properties": { "city": { "type": "string" }, "date": { "type": "string" }, "temperature_max": { "type": "number" }, "temperature_min": { "type": "number" }, "condition": { "type": "string" } } } }

可以看到,每个技能都包含:技能名、签名、输入参数 Schema、输出 Schema。其中输入 Schema 是最重要的,它会被模型用来决定怎么填参数。

注意一个细节:days 参数虽然选填,但我在定义里专门写了取值范围。这样做是为了防止模型填出一个 999 这种一眼就不合理的数字。模型虽然聪明,但你不把边界写清楚,它就敢自由发挥。

3.2 技能执行器:真正的业务逻辑藏在哪

技能定义是给模型看的,技能执行器是真正干活的。还是用天气查询来举例,一个最简单的执行器长这样:

from typing import Dict, Any def query_weather_executor(params: Dict[str, Any]) -> Dict[str, Any]: city = params.get("city") days = params.get("days", 1) # 这里真实项目会调用外部天气 API,比如和风天气、OpenWeatherMap # 我们这里用 mock 数据演示 result = { "city": city, "date": "2025-01-15", "temperature_max": 18, "temperature_min": 12, "condition": "多云" } return result

执行器的核心原则只有一个:输入必须是参数化的,不能硬编码。很多入门选手把用户 query 直接塞进执行器,比如:

def query_weather_executor(text: str): # 从 text 里面自己解析城市

这样写的问题在于,Agent 框架本身已经帮你了参数提取这一步,不需要你在执行器里再做一次解析。执行器要做的是“收参数、调服务、返回结果”,职责越单一越不容易出错。

3.3 技能编排:允许 Agent 自主组合,但要加安全边界

单技能的调用没什么难度,难点在于复杂任务要多个技能配合。比如用户说“帮我订一张明天下午从上海到北京的高铁票,然后提醒我后天早上开会”。

这个任务涉及“查高铁班次”“提交订单”“创建日历提醒”三个技能,而且是串行关系:先查班次,再订票,最后才能加提醒。Agent 不是一个一个独立调用,而是需要把三步编排起来。

我对编排的建议是:让模型自主编排,但要在代码层面加约束。

具体来说,框架层面要支持一个“执行计划”的概念。模型先生成一个计划,列出技能调用顺序、每个技能的输入依赖,然后由执行引擎按计划执行。如果某个技能失败,根据失败类型决定是终止、重试还是跳过。

同时要特别注意安全边界。比如涉及付款、删除、修改数据的技能,应该设置人工确认节点。不要让模型在没有确认的情况下直接执行破坏性操作。我在项目里就是这样设计的:所有技能分三类——只读类直接执行,更改类需要用户确认,高风险类需要双层确认。这层护栏比什么都重要。

3.4 技能执行环境:沙箱与超时控制

技能的执行环境也是个不容忽视的问题。如果 Agent 能执行代码或者调用系统命令,那安全的做法是在沙箱里跑,比如 Docker 容器、Firecracker 微虚拟机,或者云平台的 Serverless 环境。

这样做的目的不是说你的技能会被人恶意攻击,而是防止“意外伤害”。模型在执行任务时可能会生成本身就没预料到的参数组合,如果这个组合恰好触发了一个破坏性操作,那后果由谁承担?沙箱能显著降低这种风险。

另外,每个技能都应该有超时设置。我默认给所有技能设置 30 秒超时,超过直接报错返回,避免 Agent 卡在某个接口等半天,浪费用户的耐心。

4. 参考落地方案与工具选型

4.1 三大主流实现路径

笔记类或者说技能构建这件事,现在没有统一的行业标准。市面上主流路径有三条:

  1. 大模型平台原生 Function Calling:比如 OpenAI 的 function calling、通义千问的工具调用模式。人工把技能定义成 JSON Schema,然后随对话请求一起发给模型,模型返回一个结构化调用指令,你本地执行后把结果回传。
  2. AI 框架集成:比如 LangChain 的 Tools、LlamaIndex 的 Query Engine Tools、或各类国产 Agent 框架的插件机制。框架帮你做了技能注册、上下文管理、路由分发,你只需要写执行函数。
  3. 自研技能中心:把技能做成独立服务,通过统一接口注册到中心,Agent 运行时动态拉取技能列表并执行远程调用。适合技能数量大、需要跨团队协作的场景。

如果是个人项目或者快速验证原型,可以选用第 2 条路径,成本最低。如果是企业级应用,技能数量多、访问控制严格,我建议直接走第 3 条路径自研,虽然前期工作量大,但后期扩展性远优于前两者。

4.2 给技能加上“记忆”会变得更好用

纯技能调用有个问题:每个调用都是上下文无关的。同样的技能,昨天查过上海天气,今天再查一次,Agent 不知道昨天查了什么。如果要让 Agent 记住历史操作,需要在技能层之外加一个记忆模块。

我的做法是给每个技能的执行结果做持久化,并把它挂到一个短期记忆缓存中。当用户在后续对话中提到比如“昨天你查那个城市的天气怎么样”,Agent 能从记忆里找到对应的历史结果,而不需要重新调用技能。

但这里有个度的问题:记忆不是越多越好。上下文窗口是有限的,记忆太多会挤占模型处理当前问题的空间。所以记忆也要有淘汰策略,最常用的做法是只保留最近 N 条执行记录,并按相关性筛选后再注入上下文。

4.3 框架和自研怎么选,给你一条判断标准

我在不同项目里两种方案都试过,我的判断标准很简单:

  • 技能数量少于 10 个,交互逻辑不复杂,是一个聊天助手类的应用 → 直接用框架。
  • 技能数量超过 10 个,或者涉及复杂的权限管理、多团队协作、需要跨系统调用 → 自己写技能中心,哪怕一开始只是简单地用接口版注册表。

框架的问题在于:它替你做了很多事,也让很多事变得不可控。LangChain 这类框架层理了模型、RAG、Agent、Memory,多一层抽象就多一层黑盒,出了问题排查起来非常痛苦。而且技能多了以后,框架的统一调度策略不一定适合你的业务场景。

自研技能中心的好处是可以完全按自己的需求来。技能怎么注册、怎么描述、怎么选择、怎么执行、失败怎么处理,每一步都可以自定义。缺点是要写的东西多,光技能生命周期管理就要花不少时间。

没有绝对正确的方案,只有适不适合当前阶段。我的建议是:先用框架快速验证,发现不够用了再逐步替换成自研,不要一上来就陷入自研的黑洞。

5. 常见问题与排查技巧实录

5.1 模型不调用技能,老是自己在“编”

表现:用户问“上海天气怎么样”,Agent 没有走技能,直接回复了一段看起来像天气播报的话。这段内容其实是模型根据训练数据“猜”出来的,并不是实时数据,有时候是假的。

排查思路:

  1. 先看技能是否注册成功。最简单的方式是打印一次模型请求的 payload,看技能列表是否包含进去了。
  2. 检查技能描述是否清晰。描述写得太泛,模型不会认为它属于当前场景。
  3. 看温度参数设置。把 temperature 调低到 0 到 0.3,可以让模型更倾向于调用工具而不是自由发挥。
  4. 检查模型版本。不同模型的工具调用能力差距很大,一些参数较小或能力较弱的模型,在复杂指令下容易忽略工具调用。

我遇到的最常见原因是第二点,描述写得不够具体,模型无法把用户问题映射到技能上。

5.2 模型调用技能但参数填错

表现:模型确实调用技能了,但参数填得离谱。比如城市填了“Shanghai City, 100083”、日期填了“明天”。

排查思路:

  1. 检查技能的 input_schema 是否写清楚了参数类型和示例。模型在不确定参数格式时,会根据自己的理解发挥。
  2. 在技能执行器里加参数校验逻辑。不要信任模型给的参数,执行器必须先校验再执行。
  3. 做一个“参数不合法时返回错误信息”的机制,让模型看到错误后再重新填参数。这个技能层级的反馈循环,能显著提升重试时的正确率。

一个辅助技巧:在 Schema 描述的末尾加一句示例,比如“city 参数的示例值:北京”。模型对示例非常敏感,给一个正确的示例,它填错的概率会降低一半以上。

5.3 技能太多导致上下文爆炸,还没轮到他说话

表现:Agent 对话不流畅,响应时间越来越慢,或者技能调用出现“张冠李戴”。原因是技能列表全量塞进上下文,模型每次要判断几十个技能哪个合适,判断成本非常高。

解决方案:

  1. 技能分组,根据用户首次输入的关键词先做一次意图粗筛,只把粗筛后的技能子集注入上下文。
  2. 用向量检索的方式动态选技能,提前把技能描述转成向量,每次用户提问时只取 Top K 的相关技能。
  3. 对于流程类场景,可以状态机化。当前需要哪些技能是确定的,不再让模型从所有技能里自由选择,而是限定在当前状态允许调用的技能范围内。

我比较推荐第三种方式,特别是业务流程固定的应用。它大幅缩小了模型的选择面,也就大幅降低了调用出错率,代价是需要你提前梳理流程。

5.4 技能执行报错,Agent 不会自己恢复

表现:技能执行抛出异常,Agent 直接说“对不起我暂时无法处理”,用户体验很差。

原因:Agent 框架默认情况下拿到报错信息后不知道怎么处理,只会把报错当作最终结果呈现给用户。

解决方案:在技能执行器里对异常做分类,并返回结构化的错误信息。我常用的分类简单直接:

  • 参数错误 -> 返回“参数不合法:xxx”,让模型重新提取参数。
  • 外部服务错误 -> 返回“服务超时,请重试”,让模型隔几秒后重试一次。
  • 业务规则错误 -> 返回“业务规则不允许:xxx”,让模型终止流程并告知用户原因。

实现时,给执行器加一层异常捕获,统一包装成以下形式返回:

{ "status": "error", "error_type": "invalid_params", "error_message": "城市名称不能包含数字", "retryable": false }

模型拿到这个错误信息后,能做到比“直接报错”好得多的反馈:参数错误就重填参数,外部服务超时就重试,业务规则不允许就结束并解释给用户。

5.5 排查实操的一张速查清单

症状优先检查点常用解法
模型不调用技能技能描述、温度参数、模型能力优化描述、调低温度、换更强模型
调用但参数乱填input_schema 清晰度、示例缺失补参数示例、加执行器校验
技能调用零散、无组织技能分层缺失、没有编排做技能分组、按状态机限制调用范围
执行报错无法恢复异常信息不结构化按错误类型分类返回、允许重试
上下文爆炸响应慢技能全量注入向量筛选、意图粗筛、状态机限定

6. 经验总结与个人体会

看了这么多,最后分享几个我在做 agent-skills 这个方向时比较深的体会。

第一,技能的抽象设计要在动手写代码之前想清楚。技能不是越细越好,也不是越粗越好。粒度太细,模型编排任务的负担很重,一个简单任务要调十几次技能;粒度太粗,技能复用性变差,场景一变就要写新技能。我个人的判断标准是:一个技能应该对应一个不可再拆的领域动作或一个完整独立的业务流程,介于两者之间的粒度通常都是不合适的。

第二,技能描述值得花时间打磨。你可能觉得写描述是件很“软”的事,不如写代码实在。但一个技能描述的好坏,直接决定了模型十几二十次调用的准确率。我给团队的要求是,技能描述至少经过三轮 review 才能上线——第一轮让产品负责人看描述是否准确,第二轮让工程师看 Schema 是否合理,第三轮让测试用各种刁钻话术去试同一个技能会不会被误导。

第三,这个方向的迭代节奏特别快。我在项目做了一半的时候发现,光是技能定义格式,社区里就已经有几种不同的方案在竞争。与其等标准落地,不如先用一套自己的规范把业务跑起来。只要保留好抽象层,未来即使格式变化,影响面也能控制在配置层,不会震到业务代码。

agent-skills 不是一个能一蹴而就搞定的话题,它跟 Agent 本身一样,是在一轮一轮的试错里迭代出来的。希望这篇里的设计和踩坑经验,能给你省掉几周的弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询