☰
Agent技能体系搭建:从零设计可复用技能层的工程实践
2026/10/7 11:19:40 网站建设 项目流程

做Agent开发这一年多,我最大的感受是:模型能力的天花板早就不是推理水平了,而是它手里有没有趁手的技能。同一个开源模型,只给它对话能力,它就是个聊天机器人;给它配上检索、计算、自动化脚本这些技能,它就能变成能帮你干活的数字员工。今天想好好聊聊agent-skills这个话题,也就是Agent技能体系的搭建。这篇文章不是什么高深的理论,而是我自己从零到一把一个Agent项目跑起来,在技能设计、代码实现、问题排查过程中攒下来的一手经验。适合正在做AI Agent应用开发、或者准备把大模型接到实际业务流程里的朋友,可能你已经看过LangChain、LlamaIndex这类框架的文档,但对“技能到底该怎么设计”这件事还是一头雾水,那这篇文章应该能帮你避开不少坑。

1. Agent Skills到底是什么,为什么成了绕不开的关键词

1.1 “会聊天”和“会做事”之间,差了整整一个技能层

大模型刚火起来的时候,大家讨论的都是“它能不能写诗”“它能答对多少道题”。但真到了做项目的时候,你会发现客户根本不关心这些。客户关心的是:能不能自动查一下库存?能不能把这份PDF里的数据提取出来填到表格里?能不能每天凌晨跑一遍脚本把异常订单捞出来?

这些需求有一个共同点:光靠LLM的文本生成能力完成不了,它必须去操作外部系统。而Agent Skills,就是你给模型预设好的一系列可调用的操作能力——查数据库、调接口、执行脚本、读写文件,全都通过技能的方式暴露给模型。模型在对话过程中,根据用户的意图自主决定调用哪个技能、传什么参数,然后把结果整合进回答里。

可以这样理解:大模型本身是一个知识渊博但不会动手的专家,Agent Skills就是分配给它的那双手和工具箱。没有技能层,模型再聪明也只能“纸上谈兵”;有了技能层,它才能真的帮你把活干了。

1.2 Skill、Tool、Function Calling,这几个词到底怎么区分

刚开始接触这个领域时,我也被一堆术语搞得头晕:Function Calling、Tool Use、MCP、Agent Skills,好像都在说同一件事,又好像各有侧重。根据我的理解,它们其实是不同层面的东西。

Function Calling是模型厂商(如OpenAI、Anthropic)提供的一种模型能力,模型可以输出结构化的函数调用指令,而不是纯文本。Tool是应用层对可调用能力的统称,一个Tool可以是一个API函数、一段脚本、一个SQL查询器。而Agent Skills是我个人更愿意采用的组织方式——它不是单个函数,而是把函数、调用说明、参数约束、前置条件、错误处理、示例打包成一套完整的“技能单元”。

打个比方:Function Calling是模型会说“我要开灯”的能力,Tool是那盏灯的开关,Agent Skills则是一整套“通过自然语言控制房间灯光”的方案——包括开关在哪、什么情况开什么灯、灯坏了怎么报错、有没有备用方案。三层各有分工,缺一不可。

1.3 技能层解决的核心痛点:模型与真实世界的连接

我在项目中依赖Agent Skills最核心的原因,是它解决了三个痛点。

第一,模型永远是“无状态的”,它记不住上一次调用发生了什么,更不知道外部系统的实时状态。有了技能层,模型可以通过查询类技能获取最新数据,再基于真实数据做判断,而不是靠训练时的旧知识在编。

第二,模型的输出有随机性,直接让它生成SQL去查数据库,十条里有两条语法有问题。通过技能封装,把SQL提前写好,模型只需要填几个参数,错误率能降到接近零。

第三,业务逻辑要沉淀。开发一个订单管理系统,判断“这个订单要不要人工介入”的规则,你可以在系统里写死,但更好的方式是把判断逻辑做成技能,让模型结合上下文综合判断。业务规则后续要变,只需要调整技能内的逻辑,不需要改动模型本身。

2. 技能体系的分层设计:先搭框架再写代码

2.1 基础技能:把原子操作封装成积木块

在动手写第一个技能之前,我踩过一次挺深的坑:当时接了一个AI客服项目,我一股脑把后端十几个接口全写成技能丢给模型,结果模型经常在多个技能之间犹豫不决,响应速度也慢得离谱。后来我痛定思痛,开始给技能做分层设计。

首先是基础技能层,它对应的是“不可再拆分的原子操作”。比如查询订单状态、读取用户信息、计算两个日期差几天、给指定用户发送一条验证码,这些都是基础技能。设计原则是:一个技能只做一件事,且这件事的结果要能被明确描述。如果某个操作特别复杂,比如“处理退款”,那就不该是基础技能,因为它涉及资质校验、金额计算、库存回滚等多个步骤。

基础技能的最典型特征是“无状态、可复用”。它不关心这次调用是不是第二次,不关心是哪个场景在调用,输入固定的参数就返回固定的结果。就像乐高积木里的标准块,单看很朴素,但它是上层一切复杂结构的地基。

2.2 组合技能:用编排把积木搭成流水线

基础技能解决“单个动作”的问题,但实际业务需求往往是多步骤的。比如用户问“我那个退款什么时候到账”,正确流程是:先查订单状态,再查退款单状态,然后结合支付渠道的到账规则,最后生成一句自然语言回复。如果让模型自己做任务拆解,模型确实可以做到——但每次都要重走一遍思考链路,既慢又不稳定,偶尔还会漏步骤。

组合技能就是把这些稳定流程固定下来:内部预设好步骤顺序,外部暴露一个意图接口。模型看到用户的问题,只需要触发“查询退款进度”这个组合技能,技能内部自动依次调用基础技能,最后把结果汇总返回。用工程术语来说,就是从“自由编排”变成“模板编排”,适用高概率场景,能极大节省模型的推理时间。

但也有一个陷阱值得留意:组合技能不能过度设计。我见过有人把流程编排得非常深,技能里面套技能,套了三层。一旦底层某个技能报错,错误信息层层传递,排查起来非常痛苦。组合技能的层级建议控制在两层以内,除非有很强的复用需求,否则别为了“优雅”牺牲可维护性。

2.3 领域技能:把行业经验沉淀成专家包

比组合技能再高一层的,是领域技能。它通常是面向特定业务场景的“专家包”,把某个行业或某个垂直领域里经常用到的基础技能、组合技能、规则引擎、领域数据聚合在一起,形成一套开箱即用的能力集合。

举个例子,我做过一个电商客服的Agent项目,它的领域技能“售后处理包”就包含:订单查询、退款计算、物流追踪、话术模板、以及一系列业务规则(比如“签收超过7天不能无理由退款”)。外部接入方不用关心售后流程如何拆解,只需要引入这个领域技能包,模型就自动获得了一套完整的售后处理能力。

领域技能的粒度取决于产品定位。如果你做的是垂直行业Agent,领域技能就是你的护城河;如果你做的是通用助手,领域技能则需要谨慎定义,否则技能数量膨胀到最后,光是维护文档就能耗尽所有精力。我的经验是:领域技能宁可面向一个非常具体的场景做厚,也不要面向一类模糊的场景做宽。

2.4 技能注册表:帮你管好会越来越多的技能

当技能数量超过十几个之后,你一定会遇到“模型调错技能”或者“技能描述写得很像”的尴尬。我后来的做法是引入技能注册表(Skill Registry),用一个配置文件或数据库统一管理所有技能:技能ID、名称、描述、所属分层、输入参数Schema、访问权限、调用计数、平均耗时。

这个注册表的价值在实际运营中会被无限放大。你会突然发现,某些技能从来没被调用过,而某些技能调用量占比80%,那后面做性能优化和模型微调就都有了数据支撑。它还解决了权限控制的问题:基础技能可能只允许内部调用,领域技能需要过一层鉴权,这在注册表里可以统一配置。不要觉得这层设计是过度工程,等你被“技能太多无所适从”的现实狠狠教训过,就会明白提前管理的重要性。

3. 核心实现细节:决定技能好坏的3个关键点

3.1 技能描述:写给模型看的说明书,别写给人看

如果你真的亲手写过Agent应用,肯定有过这种体验:同一个技能,描述措辞改一版,模型调用准确率从50%直接跳到90%。技能描述不是文档,它本质上是一段“面向大模型的prompt”,需要精心设计。

我在写技能描述时遵循三条经验。第一,开头两句必须讲清楚“这个技能能帮用户解决什么问题”,而且要用用户的语言。比如“根据订单号查询订单的实时物流状态,返回已签收/运输中/待发货等状态信息”,而不是“订单查询接口”。第二,凡是用户意图可能与技能沾边的地方,都要写明边界。比如订单查询和物流查询是两个技能,就要在各自的描述里注明“如果用户问的是物流的速度和预计到达时间,请调用物流查询技能,而不是本技能”。第三,描述里可以嵌入“触发词”提示,例如“当用户提到物流、快递、签收、派送等词时,优先考虑此技能”,这能明显提升召回率。

还有一个小技巧是给模型提供一个调用示例(Few-shot),直接在技能描述的末尾附上一段示例对话或示例参数值。模型的上下文学习能力会参考示例的格式来生成调用参数,规范效果立竿见影。

3.2 参数设计:能少则少,能约束就别放养

很多开发者在设计技能参数时习惯性地把后端接口的所有入参一股脑暴露给模型,这是一个新手的经典错误。模型不是专业的API调用者,给它太多无关参数,它会迷茫,会增加无效推理和错传概率。

参数设计有一条黄金法则:模型侧暴露的参数越少越好,其余参数由技能内部自动补充。比如技能内部有一个request_id要传给后端,这个值明明可以自动生成,就没必要让模型操心。对于模型确实需要填的参数,务必在类型、格式、取值范围上下足功夫。字符串还是要枚举?是用标准日期格式还是纯数字?如果传错了,模型有没有能力自己纠正?这些都是需要在定义阶段回答的问题。

参数描述也同样重要。在OpenAI的Function Call规范里,参数可以带description字段,这个字段对模型的影响非常大。比如一个status参数,如果只写成“状态”,模型传“处理中”还是“processing”就很随机;但如果写成“订单状态,可选值包括:pending(待处理)/ processing(处理中)/ completed(已完成)/ failed(失败)”,模型的准确率一下就上来了。语言模型的底层逻辑是“看到什么就模仿什么”,你把约束写得越细,它就越不会发挥。

3.3 返回结构:一切以“模型好解析”为最高优先级

技能的执行结果要返回给模型,然后模型基于这个结果生成用户可读的回复。很多人在这个环节忽略了“返回结构的设计”,导致模型需要做大量的后处理,既慢又容易出错。

我的建议是:技能返回结果一律使用结构化JSON,并且保持字段命名的语义清晰、层级平坦。尽量避免嵌套过深的JSON,模型虽然处理JSON的能力不弱,但嵌套越深,出错概率越高。更要注意的是返回的“错误表示方式”:不要有的错误返回{"error": "xxx"},有的错误返回{"code": 500},有的错误直接抛异常。统一错误结构,比如一律返回{"success": false, "error": {...}},模型遇到错误时才能稳定地走同一条处理分支。

此外,返回的内容应该尽量“精加工”而非“生数据”。查询到物流轨迹,原始JSON可能包含轨迹更新时间和各个节点名称,最好在技能内部就完成排序、格式化,直接给模型一个流畅的文本段落加结构化数据。模型擅长理解和转述,但不擅长做数据清洗,技能要做模型的好帮手,而不是把脏活累活丢给它。

4. 从零搭建三个典型技能:完整实操记录

4.1 技能一:系统状态查询(读类型)

我们从一个最简单的读类型技能开始:查询部署服务的健康状态。这个技能会去调用一个内部运维API,拿到各服务的运行状态和最近5分钟的请求错误率,然后返回结构化结果。

from typing import Annotated from pydantic import BaseModel, Field class ServiceStatusInput(BaseModel): service_name: Annotated[str, Field( description="服务名称,可选值:api-gateway / user-service / order-service" )] = "api-gateway" def check_service_status(params: ServiceStatusInput) -> dict: """ 查询指定服务的实时运行状态,包括进程存活情况、最近5分钟的请求量与错误率。 当用户询问系统健康、服务可用性、服务异常、请求错误率时,调用本技能。 """ # 实际项目中这里会调用真实的运维API,这里做简化演示 result = fetch_ops_data(params.service_name) return { "success": True, "data": { "service": params.service_name, "alive": result.get("alive"), "error_rate": result.get("error_rate"), "qps": result.get("qps"), "last_check_time": result.get("timestamp") } }

这个技能的关键点有三个。第一,service_name参数使用枚举约束,模型就不会传一个不存在或格式不规范的名称。第二,描述里写了触发场景“系统健康、服务异常”,模型遇到这类问题时召回率明显提高。第三,返回值alive、error_rate都是模型能直接看懂的字段,不需要二次解析。

对于读类型技能,还有一个隐含要求是返回数据的时效性。模型本身没有实时信息,技能必须确保每次调用都返回最新数据,千万不能在技能层做数据缓存。哪怕只缓存5秒,遇到线上事故排查时,你给模型送一个过期状态,误导性非常大。

4.2 技能二:批量数据计算(计算类型)

第二个技能更像“给模型装上计算器”:读取一份销售明细文件的CSV,按指定维度做聚合统计后返回结果。这种技能很适合用来处理“帮我算一下这个月各渠道的销售额”这类需求。

import pandas as pd from typing import Annotated from pydantic import BaseModel, Field class SalesStatsInput(BaseModel): file_path: Annotated[str, Field(description="销售明细CSV文件的路径,格式为 /data/sales/2025/xx.csv")] group_by: Annotated[str, Field(description="聚合维度,可选值:channel(渠道) / category(类目) / region(区域)", default="channel")] def calc_sales_stats(params: SalesStatsInput) -> dict: """ 读取销售明细文件,按指定维度计算销售额与订单量。 当用户需要统计维度对比、销售额汇总、渠道排行榜时调用本技能。 """ try: df = pd.read_csv(params.file_path) result = df.groupby(params.group_by).agg( total_sales=("amount", "sum"), order_count=("order_id", "nunique") ).reset_index() return { "success": True, "data": result.to_dict(orient="records") } except FileNotFoundError: return { "success": False, "error": {"message": f"文件不存在: {params.file_path}"} }

这个技能体现了计算类型技能的两条重要经验。第一,错误处理要前置,文件可能不存在、列名可能对不上、数据可能为空,这些都要提前预判并返回结构化错误,不要光秃秃抛一个异常让模型自己猜。第二,group_by字段要有默认值,这样模型在“算一下销售额”这种模糊需求下也能成功调用,不会因为参数不齐全而卡壳。

可能有人会问,既然模型也会做简单的求和统计,为什么要专门封装这样一个技能?原因很简单:模型的数学能力在小样本上看起来没问题,一旦数据量变大、维度变多,它要么算错,要么偷偷省略数据,要么速度慢得让人崩溃。用代码做计算,精度和速度都是确定性有保障的,模型的价值在于理解需求、传达结果,而不是来做计算器。

4.3 技能三:生成工作日报(组合类型)

第三个技能展示的是组合技能的价值。这里有一个办公场景:每天早上,项目经理问Agent“今天有什么待办”,Agent需要汇总多个来源的信息,整理成日报。

def generate_daily_report(user_name: str) -> dict: """ 汇总用户当日的会议安排、重点任务、未读消息,生成一份结构化工作日报。 当用户提到日报、今日安排、今天要做什么、当日工作重点时调用本技能。 """ # 1. 调用基础技能:获取今日日程 events = fetch_calendar(user_name) # 2. 调用基础技能:获取任务列表 tasks = fetch_todo_list(user_name) # 3. 调用基础技能:获取未读消息摘要 messages = fetch_unread_summary(user_name) # 4. 内部整合 report = { "date": datetime.now().strftime("%Y-%m-%d"), "events_count": len(events), "task_count": len(tasks), "first_task": tasks[0] if tasks else None, "top_message": messages[0] if messages else None, } return {"success": True, "data": report}

组合技能在实现上的核心是“内部编排要固定,外部入口要极简”。对模型来说,它只需要知道“生成日报”这一个入口,至于日报怎么拼装、优先级怎么排,那是技能内部的事。这样做的好处是:模型每次调用生成的日报格式是一致的,不会这次先写日程下次先写消息。

生产环境里,这类组合技能往往还会再接上一个“输出策略层”,比如根据报告内容决定是否推送一条提醒、是否需要人工复核。这就不只是技能调用了,已经是Agent自主工作流的一部分。但原理不变:从稳定流程出发,把每一步做扎实。

4.4 技能上线前,有哪些必做的验证

技能写完不等于能用,我这里有一套成本不高的验证流程,推荐你直接拿去用。

第一轮是单元冒烟测试:直接调用技能函数,用几个典型的合法参数和非法参数各跑一遍,确认返回结构符合预期。非法参数测试尤其重要,因为模型是“不按套路出牌”的,它真的可能传一个空字符串或超长文本进来。第二轮是模型级联调:在真实的LLM对话环境里发起几类自然语言请求,观察模型是否在正确的时候调用技能、参数是否传对、返回结果模型是否能正确转述。第三轮是回归脚本:把历史用户的真实问题整理成测试集,每次技能改动都跑一遍,确保修复一个bug没有带出新的问题。

这轮流程的灵感来自传统软件开发里的单元测试和回归测试。做Agent项目很容易陷入“每次调试都靠人工聊几句”的野路子,效率极低,而且改一处坏一处。把验证固化下来,是Agent项目工程化必须迈过的一道门槛。

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

5.1 模型就是不调用技能,怎么回事

这是我在项目里被问过最多的问题。现象是:用户问了一个明显可以用技能解决的问题,但模型却自己凭空编了一个答案。排查路径一般分三步。

首先检查技能描述里是否缺少“触发暗示”。描述要和用户的语言习惯匹配,如果你在技术文档里把这个能力叫“SKU查询”,而用户问的是“这个有货吗”,模型就很难把两者关联起来。在描述里加上“当用户询问是否有货、库存情况、现货状态时,调用本技能”,问题往往会立刻消失。

其次检查参数Schema是否过严。模型可能识别出了技能,但看到必填参数有4个,而用户只提供了2个,它不确定剩下的怎么填,于是干脆放弃调用。这时候要么给非关键参数设置默认值,要么让技能支持缺省参数的预设值逻辑。多给模型一条可以走的路,它就不会绕路。

最后还要检查是不是有别的技能“截胡”。如果模型觉得另一个技能也能解决同样问题,而那个技能的描述更清晰,它就会选择后者。把所有技能打印出来,想象自己是一个LLM,看看哪一版描述更明确,就能定位问题。

5.2 参数总是传不对,问题出在哪

如果确认模型已经调用了技能,但传参经常出错,比如日期格式写成“明天”、字符串里带emoji、数值传了单位,那问题基本都出在参数定义上。排查方向有两个:约束是否写清楚了?示例是否给了?

模型对参数的理解完全依赖于Schema里的描述,你期望它传什么,就必须把它没见过、没被约束过的情况都写明白。日期参数明确告诉它“必须是YYYY-MM-DD格式的字符串”,数值参数告诉它“必须是不带单位的纯数字”。还有一个很实用的办法:把正确参数格式和错误格式各写一遍,模型对比示例后通常会自动校准。

在工程上,参数传错也不应该直接报错导致流程中止。更稳妥的做法是:技能内部做一个轻量级参数归一化,比如把“明天”自动换算成具体日期,把金额里的逗号和小数点统一格式。技能要像一个大厨——即使客人点菜时表述不严谨,你也可以根据常理把菜做对。

5.3 技能冲突与上下文膨胀的处理

技能数量多了以后,会遇到两类比较棘手的问题。一类是技能语义重叠,导致模型随机选用。解决办法是重新梳理技能边界,把重叠的部分合并或拆分,同时在描述里互相做“排除性说明”。比如“A技能处理的场景不包括物流信息,物流信息请调用B技能”。这看起来很笨,但实测效果非常好。

另一类是上下文膨胀。现在主流Agent框架都会把技能描述拼进系统提示词里,技能几十个以后,系统提示词可能超过令牌上限,或者虽然没超限但挤占了正常对话的上下文空间。我的应对经验是按需注入:根据对话历史先用一个轻量级分类器粗筛技能集合,只把最可能被用到的10个左右技能的描述注入系统提示词。这套做下来,响应延迟和调用准确率都有明显改善。

5.4 安全边界:技能不是越强越好

做技能设计时不能光想着“能干什么”,更要考虑“不应该干什么”。技能权限最小化是底线:查询类技能不能带写库权限,写操作类技能必须要过一层鉴权和审批。尤其是那些能执行脚本、能操作文件系统的技能,一旦被恶意prompt注入利用,后果不堪设想。

我的经验是给每个技能标注一个“风险等级”。低风险是纯读操作,可以直接调用;中风险是写操作但影响有限,比如发送一条通知,需要用户确认;高风险是删除、转账、修改配置这类不可逆操作,必须在技能内部生成一个“操作确认单”,让用户明确读了确认后再执行。把这个机制做进技能框架里,而不是靠模型自觉,是Agent项目上线前必须补上的安全短板。

6. 写在最后:这是我持续在踩但也一直在改进的方向

技能设计这件事,看起来是一堆工程细节,但本质上是在做“模型能力”与“复杂真实世界”之间的适配层。我越做越觉得,Agent开发的胜负手不在于模型选得多好,而在于你愿不愿意花时间在每一个参数约束、每一段技能描述、每一次错误处理上死磕。好的Agent给人的感觉是“它真的懂我需要什么”,而这背后往往不是一个超强的模型,而是一个把大量现实情况都预埋好的技能系统。

我自己现在还在持续迭代的方向,是把技能从“被模型调用”逐步升级到“能主动触发”。也就是说,模型不一定要等用户问,而是在监测到某些条件成立时自动调用技能。比如看到日历上有冲突的会议,自动去查参会人的空闲时间并提出调整方案。这个方向对技能设计的要求更高,但也让我越来越接近我理想中的Agent形态:不是问答机器人,而是一个靠技能系统驱动、真正能自主完成业务闭环的数字同事。对这些内容感兴趣的朋友,强烈建议从今天提到的分层设计、参数校验、按需注入这几个点入手,回去盘一盘你自己的技能库,大概率能发现不少可以优化的地方。

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

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

立即咨询