☰
Agent技能体系设计:告别杂乱工具函数,构建可组合能力单元
2026/10/13 8:21:12 网站建设 项目流程

别再做一堆没用的工具函数了,Agent技能体系应该这样设计。聊一聊我最近在梳理的 agent-skills 这个事,直接说几个核心观点:Agent的能力上限,其实不取决于模型本身有多聪明,而取决于你给它配了什么样的技能。一套设计良好的技能体系,能让一个普通模型做出惊艳的效果;反之,技能定义得一塌糊涂,再强的模型也会表现得像个只会说废话的呆子。

我在实际项目中踩过不少坑,一开始也是把各种工具函数堆在一起,结果Agent经常调用错函数、参数传错、上下文混乱,后来重新整理了技能体系,效果提升非常明显。这篇文章就把我在设计和落地 agent-skills 技能体系时的完整思路、操作方法、踩坑记录都分享出来,希望能给正在做Agent开发的你一些参考。

1. 整体设计思路拆解:为什么Agent需要一套“技能体系”

1.1 从“工具函数”到“技能体系”的思维转变

很多人做Agent应用时,第一反应是给模型准备一堆函数。比如要做一个文档助手,就写个read_file、write_file、search_keyword、summarize_text……然后一股脑塞给模型。这种做法在Demo阶段没问题,但一旦场景稍微复杂,问题就全冒出来了。

我做过一个模拟项目X,是个企业内部的知识库问答助手。最早版本挂了30多个工具函数,模型经常选错工具,有时候明明该调用搜索,它偏偏去读文件;有时候两个函数功能重叠,模型不知道该选哪个。调试过程极其痛苦,后来我才意识到:问题不在模型,而在“工具”这个概念太底层了。

工具是机械的、单一的,而技能应该是场景化的、体系化的。技能 = 工具 + 场景 + 约束条件 + 输入输出规范。它不是一个孤立的函数,而是一整套可以被模型理解、调用、协作的完整能力单元。

打个比方,工具像是厨房里的一把刀、一个锅、一块砧板,而技能则是一道菜的完整做法:什么时候用刀、什么时候开火、什么时候下料,这些决策本身已经内置在技能里。你给厨师的不是一堆散乱的厨具,而是一本写清楚“怎么做出一道菜”的菜谱,这样厨师才能稳定复现好吃的结果。

1.2 技能体系设计的三条核心原则

在重构 agent-skills 时,我给自己定了三条原则:

第一,技能即接口。每个技能必须有清晰、唯一、无歧义的接口定义,包括技能名称、功能描述、参数Schema、返回值规范。模型通过接口理解技能,接口定义的质量直接决定模型能否正确调用。

第二,技能可组合。单一技能解决单一问题,复杂场景通过多个技能的组合来完成。就像积木一样,小技能可以搭出大能力。比如“生成周报”这个技能,可以组合“获取本周工作记录”、“分析完成度”、“生成报告”三个子技能。

第三,技能可观测。每个技能的执行过程、调用参数、返回结果、耗时、错误信息都应有日志记录,这样才能在模型调用出错时快速定位问题。我见过太多团队在Agent出错时根本不知道是模型选错了技能,还是技能本身逻辑有bug,就是因为没有可观测性设计。

1.3 技能体系的分层架构

在设计 agent-skills 时,我把技能体系分成四层:

  • 底层是原子技能,对应单一、不可再拆分的操作,比如“读取文件”、“执行SQL查询”、“调用外部API”;
  • 第二层是场景技能,在原子技能基础上封装特定业务场景,比如“查找相似文档”、“提取合同关键条款”;
  • 第三层是编排技能,负责组合调度多个场景技能,比如“生成项目总结报告”会依次调用“获取项目数据”、“分析进度风险”、“生成报告文本”;
  • 顶层是策略层,定义Agent在什么业务场景下优先采用哪种技能组合,以及技能执行失败后的降级策略。

这个分层的价值在于:底层技能稳定通用,可以在不同项目中复用;场景技能贴近业务,是团队积累的核心资产;编排技能和策略层则让Agent具备了处理复杂任务的能力。我目前维护的 agent-skills 项目就是用这个架构,实践证明比平铺一堆工具函数要清晰得多。

2. 核心模块拆解:技能注册、技能描述、参数Schema与调度引擎

2.1 技能注册中心:一切技能的统一入口

技能注册中心是整个体系的“户口本”,所有技能上线前都要在这里登记。每个技能注册时至少包含以下字段:

字段说明是否必填
skill_id技能唯一标识必填
name技能名称,需简短且无歧义必填
description功能描述,说明技能用途及适用条件必填
parameters_schema参数JSON Schema必填
returns_schema返回值JSON Schema必填
tags技能标签,用于检索分类选填
version技能版本号必填
enabled是否启用必填

这里有个细节值得注意:name 和 description 是给大模型看的,而不是给人看的。很多开发者习惯用英文命名函数,或使用过于简短的描述,结果模型理解不到位。我见过一个项目把“查询用户订单”写成qry_ord,模型经常把它当作订单状态查询之外的用途,后来改成query_user_orders并配了详细的描述,误用率立刻降了一半。

我建议在注册阶段就建立一套规范,description的写法可以套用固定模板:“当用户需要[场景描述]时,使用此技能。[输入说明][输出说明][特殊注意事项]。”比如:

当用户需要查询订单物流信息时使用此技能。 输入:订单编号order_id(必填)、手机号phone(用于校验身份,选填)。 输出:物流轨迹列表,包含时间、地点、状态描述。 注意:订单编号不存在时,返回错误码404,不要尝试自行推断。

这种结构化的描述,模型命中率会明显高于“查询物流”这种笼统的写法。

2.2 技能描述的质量,直接决定模型调用的准确率

我在实际调试中发现一个规律:模型选技能靠的是语义匹配,描述写得越精准,匹配的准确率越高。这里分享几个实测有效的技巧:

第一,描述中要包含触发场景的关键词。比如“当用户询问天气/气温/降雨概率时”就比“天气查询功能”更能让模型找到对应技能。

第二,描述中要写清楚边界条件。比如“本技能只能查询已发货的订单,未发货订单请调用order_create技能”,这样能有效减少模型判断模糊的情况。

第三,描述中要区分主用途和附带能力。很多技能其实能做多件事,但模型理解核心用途就够了。我通常把主用途放在前面,附带能力如果跟其他技能有交叉,就干脆不提,避免给模型造成干扰。

举个例子,同样是文件处理技能,这样写就不合格:

处理文件。

而这样写效果就好很多:

当用户需要对本地文本文件进行读取、写入或内容追加时使用此技能。支持.txt和.md格式,输入为文件路径和操作类型,输出为操作结果或文件内容。注意:不支持二进制文件和图片文件。

第二种写法明确说了“什么时候用”、“支持什么”、“不支持什么”,模型几乎不会用错。

2.3 参数Schema:用规范化定义给模型“填空”

参数Schema是技能体系中最容易被忽视、但出错率最高的部分。模型在调用技能时,本质上是根据用户的自然语言输入,生成一个符合Schema的参数JSON。Schema定义得越清晰,模型“填空”的准确率就越高。

我有几个常用的Schema设计经验:

  • 参数名要直观,不要用缩写。比如user_phone就不要写成u_ph,模型很容易对缩写产生误解。
  • 每个参数都要写description,说明参数的含义、格式、取值范围。模型不是人,它不会自己“猜”参数是什么意思。
  • 必填参数和选填参数要严格区分。如果有一个参数大部分情况下存在默认值,就尽量设为选填,降低模型填错的概率。
  • 对于枚举类型的参数,把所有可选项和对应含义都写清楚。

这里给一个实际的参数Schema示例,是某个Agent项目里的“发送定时消息”技能:

{ "type": "object", "properties": { "recipient": { "type": "string", "description": "接收消息的联系人名称,必须是用户通讯录中已存在的联系人", "minLength": 1 }, "content": { "type": "string", "description": "消息正文内容,支持纯文本和链接,长度不超过2000字", "maxLength": 2000 }, "send_time": { "type": "string", "description": "消息发送时间,格式为YYYY-MM-DD HH:mm:ss,不填则立即发送", "format": "datetime" }, "priority": { "type": "string", "enum": ["high", "normal", "low"], "description": "消息优先级:high为立即通过强提醒发送,normal为普通通知,low为合并到摘要中发送", "default": "normal" } }, "required": ["recipient", "content"] }

注意priority这个字段,如果你只写“优先级”三个字,模型可能填一个“紧急”进去,Schema校验直接报错。但你把每个枚举值的含义和效果写清楚,模型就知道该填high而不是“紧急”了。

2.4 技能调度引擎:让Agent知道“先做什么、后做什么”

单体技能容易做,难的是让Agent在复杂场景中按正确的顺序调度多个技能。我的做法是给每个技能增加两个元信息字段:prerequisites(前置技能条件)和conflicts(冲突技能)。

比如“生成项目周报”技能,prerequisites里可以声明“需要先调用get_project_data获取项目数据”。当模型决定使用该技能时,调度引擎会自动检查前置技能是否已经执行,如果未执行,会先触发前置技能的调用,而不是等模型在对话中自行决策。

conflicts字段用来声明哪些技能不能同时使用。比如“修改文件”和“发送文件”在同一轮任务中同时出现,可能会引发文件内容不一致的问题。声明冲突后,调度引擎会提示模型做出选择或先征求用户确认。

这两个机制帮我解决了一个老大难问题:多步任务中的顺序控制。在没有这套机制之前,模型经常“想到哪做到哪”,有了明确的前置依赖声明,Agent的执行顺序明显稳定了。

3. 实操过程:从零构建一个技能体系并接入Agent

3.1 第一步:梳理业务场景,划定技能边界

我在构建 agent-skills 时,第一步不是写代码,而是花了两天时间梳理业务的全部使用场景。这个模拟项目是一个面向某公司内部的智能客服,需要支持问题解答、工单创建、知识库检索、人工客服转接等能力。

梳理场景时,我会把用户在对话中可能提出的各种请求列出来,然后逐条归类:哪些请求可以直接由一个原子技能满足,哪些需要编排组合,哪些其实不适合当前Agent处理(比如涉及隐私或权限的操作)。这个过程中自然就划定了技能的边界。

这里有个重要的实操心得:技能的粒度不是越细越好,也不是越粗越好,而是要匹配模型的理解能力和业务的需要。粒度过细,模型需要在一次任务中调用十几个技能,容易乱;粒度过粗,技能本身变成一个小业务系统,复用性又很差。我的经验是让每个技能的输入输出尽量控制在3-5个参数以内,这样模型最容易理解和生成。

3.2 第二步:定义技能清单与优先级

场景梳理完之后,我整理了一份技能清单,并给每个技能标注了优先级。这一步看似简单,其实很关键:优先级决定了Agent在能力重叠时首选哪个技能。

比如我的项目里有两个技能都和“用户订单”相关:“查询订单信息”和“修改订单信息”。虽然它们参数类似,但在实际客服场景中,70%以上的请求都是查询,所以我给查询技能的优先级设为P0,修改设为P1。调度引擎在处理模糊请求时,如果模型无法确定用户意图是修改,会默认按查询来处理,避免误操作。

我当时整理的技能清单大致长这样:

技能名称优先级类型说明
retrieve_knowledgeP0原子技能基于关键词检索知识库
answer_common_questionP0场景技能回答高频常见问题
create_ticketP0场景技能创建工单并通知对应人员
query_order_statusP0场景技能查询订单状态
modify_orderP1场景技能修改订单信息,需用户身份验证
transfer_humanP0编排技能转接人工客服并附上对话摘要

3.3 第三步:实现技能的注册与核心代码

注册中心的实现其实不复杂,用Python + FastAPI可以很快搭起来。核心逻辑就是一个字典存储技能定义,再加一些API用于注册、查询、更新技能元信息。

# 技能注册中心核心代码(简化版) class SkillRegistry: def __init__(self): self._skills = {} def register(self, skill_meta: dict, handler: callable): skill_id = skill_meta["skill_id"] if skill_id in self._skills: raise ValueError(f"技能 {skill_id} 已存在") self._skills[skill_id] = { "meta": skill_meta, "handler": handler } def get_handler(self, skill_id: str): return self._skills.get(skill_id, {}).get("handler") def list_skills(self): return [ {"skill_id": s["meta"]["skill_id"], "name": s["meta"]["name"], "description": s["meta"]["description"]} for s in self._skills.values() ]

技能实现本身就是一个普通函数,接收参数JSON,执行后返回JSON。我建议所有技能函数保持统一的签名:

def retrieve_knowledge(params: dict, context: dict) -> dict: """ 根据关键词在知识库中检索相关信息。 """ keyword = params.get("keyword", "") top_k = params.get("top_k", 5) results = knowledge_base.search(keyword, top_k=top_k) return {"results": results, "total": len(results)}

这里的context参数用于传递对话上下文、用户身份信息、权限标识等。技能函数不需要自己管理全局状态,统一通过context读写,避免多个技能之间产生隐式依赖。

3.4 第四步:设计系统提示词,教会模型使用技能

有了一套技能,还得让模型知道怎么用。很多人以为把技能列表塞进system prompt就行,其实远远不够。我踩过这个坑,直接塞列表的结果是模型经常自由发挥,编造不存在的技能名。

我的方案是在system prompt中明确告诉模型三件事:

第一,有哪些技能可用,每个技能一句最简描述; 第二,调用规则,必须严格遵循skill_id,参数必须符合Schema定义; 第三,能力边界,当技能无法满足用户请求时,不要编造结果,明确告知用户。

实际使用的提示词模板我简化一下:

你是某公司智能客服助手,你可以调用以下技能: - retrieve_knowledge:知识库检索,适用于具体问题查询 - create_ticket:创建工单,适用于需要后续跟进处理的情况 - query_order_status:订单状态查询,适用于用户询问订单进度 - transfer_human:转人工,适用于用户明确要求人工或本助手无法解决时 调用规则: 1. 每次调用前先评估用户意图是否匹配技能描述,匹配才调用 2. 参数必须按技能定义的JSON Schema填写,缺失的必填参数要向用户询问 3. 技能调用结果如果无法满足用户需求,要如实说明,不得编造信息 4. 一次对话中如需多步操作,按依赖顺序依次调用

这段提示词的写法其实借鉴了“规则引擎”的思想,明确、简洁、无歧义。我在多次测试中发现,模型对这类指令式的说明遵从度相当高,比“你可以自由调用以下工具”这种宽松表述效果好得多。

3.5 第五步:技能测试与调优闭环

技能接入后不能直接上线,需要先跑一轮系统的测试。我自己常用的测试方法是准备一批“用户问题样例集”,覆盖每个技能的典型场景、边界场景和干扰场景,然后自动运行Agent,记录每次技能调用的正确性。

测试时重点关注三个指标:

  • 命中率:用户问题对应的正确技能被调用的比例;
  • 参数准确率:调用技能时参数填写的正确比例;
  • 失败恢复率:技能执行失败后,Agent能否走降级路径或明确告知用户。

调优时优先处理命中率低的问题。如果正确技能没被调用,先检查技能描述是否足够清晰,再检查描述中的关键词跟用户问题中的表达方式是否匹配。有一个案例我印象很深:用户的表达是“我的订单什么时候到”,技能描述里写的是“查询物流轨迹”,模型一开始匹配不到,后来我把描述改成“当用户询问订单何时送达/物流进展/快递到哪了时使用”,命中率一下子就上去了。

这就是为什么我说技能描述是一等公民,它的质量直接决定Agent的可用性,而不是可有可无的注释。

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

4.1 技能调用失败:模型选择技能时“差之毫厘,谬以千里”

这是最常遇到的问题。现象是用户明明问的是A场景,模型却调用了B技能,或者直接不调用技能自己编答案。

我排查这类问题的思路是:先看模型在对话中的思考过程(如果模型平台支持输出推理过程),确认模型是理解错了技能描述,还是理解对了但选择了错误的skill_id,还是根本没意识到需要调用技能。

针对不同的原因,对应的解决办法也不同:

问题原因特征解决方式
技能描述与用户表达脱节正确技能未被调用,模型选了语义相近的其他技能重写技能描述,融入更多目标的表达说法
技能描述过于宽泛多个技能的触发条件重叠,模型无法区分明确边界条件,在描述中写死“不做”的场景
技能没有出现在提示词中模型“不知道有这个技能”检查注册状态和提示词中的技能列表,确保包含
参数Schema不清晰技能调用成功但参数必定填错重写参数description,补充枚举值和格式示例

还有一个高频场景:模型调用技能后,因为参数校验失败报错,然后它开始自己“修正”参数重试,结果越改越错。我建议在实现技能时对参数校验做两级设计:第一级是Schema硬校验,参数类型不对或缺少必填字段直接报错;第二级是业务软校验,比如查询某个不存在的订单ID,返回明确错误码。这样模型收到错误码后,能根据提示信息进行下一步,而不是盲目重试。

4.2 上下文污染:技能A的结果变成技能B的错误输入

这是做多技能编排时很容易踩的坑。在测试模拟项目X时,我遇到过一个问题:Agent先调用了知识检索技能获取了一堆文档片段,接着要调用工单创建技能,结果工单的内容字段里被填入了文档片段,整个工单正文完全不可读。

排查后发现问题出在上下文管理:我把所有技能的返回结果都原封不动地丢回了对话历史,模型在看到“当前有哪些信息可用”时,误把文档片段当作了工单内容。

解决办法是在技能返回值中增加metadata字段,标记数据类型和使用范围:

{ "data": {"content": "工单正文内容"}, "metadata": { "type": "ticket_content", "source": "user_input", "usage": "for_create_ticket" } }

同时在提示词里明确告诉模型:“不同技能的返回信息归属于不同用途,如果下一任务需要的信息来源不明,优先基于用户原话生成,而不是使用其他技能的输出。”这套规范加上之后,上下文污染的问题明显减少。

4.3 性能瓶颈:技能数量大了之后,上下文越塞越多、调用越来越慢

技能数量从十几个增长到几十个后,你会发现一个问题:技能描述和规则占用的上下文越来越多,留给对话和业务内容的空间越来越少。而且模型在几十个技能里做决策,耗时和出错率也会上升。

我的对策是分级“技能提示”:常用的核心技能放主提示词,低频长尾技能从注册中心动态加载,由模型在需要时主动查询技能列表再决定调用。

具体来说,我实现了一个search_skills技能:当主提示词中的技能列表无法匹配用户意图时,模型可以调用这个技能,基于关键词在注册中心检索对应技能。

这个设计有个附带好处:长尾技能不会被遗忘。如果你的所有技能都一股脑放在主提示词里,模型可能只记得前面的几个,后面的几乎不会被使用。而动态检索模式天然保证了技能被“按需唤起”。

4.4 安全问题:权限校验必须在技能层严格把关

技能体系还有一个常被忽略的点:安全。Agent可以调度技能,就意味着你开放了一个“可执行操作的入口”。如果没有权限校验,可能会出现越权操作,比如普通用户通过Agent修改了管理员才有的配置。

我的经验是三条铁律:

  • 技能注册时必须声明所需权限等级;
  • 技能执行前由调度引擎统一做权限校验,而不是在技能函数内部自行校验;
  • 高风险技能(如删除、修改、对外发送消息)必须在执行前让用户二次确认。

这里的第三点尤其重要。实际运营中,很多用户习惯性地说“帮我处理一下”,如果Agent真的自动执行了删除操作,出了问题很难挽回。我见过一个项目因为Agent自动发送了一封内容不完整的邮件,导致客户投诉。后来他们给“发送邮件”技能加上了二次确认机制,风险就完全可控了。

提示:二次确认不是让Agent问“你确定吗?”这样简单的重复,而是要把即将执行的操作内容重新概括给用户确认,比如“我将向某地址发送内容为X的消息,是否执行?”这种形式模型执行起来更可靠,用户也更容易感知风险。

5. 进阶方向:从“能用”到“好用”的几条经验

5.1 让技能具备自描述能力,让模型自己“发现自己需要什么”

传统的技能调用是模型从固定列表里选择,但这样受限严重。更进一步的思路是:把技能本身当成可检索的知识库,让模型先“查找该用什么技能”,再“调用该技能”。

我在实际项目中测试过这个模式,效果很理想。做法是给注册中心增加一个文本索引,技能描述和标签被向量化存储。模型在遇到用户问题后,先在索引中做相似度检索,返回最相关的3-5个技能候选,并附带每个技能的详细描述。这样既节省了上下文空间,又提高了技能发现的灵活性。

这个模式也有代价:一次检索多一次模型调用,延迟会增加几百毫秒。但如果你的技能库里有大量长尾技能,这个代价完全值得。

5.2 建立技能反馈闭环:每次调用都是一次学习机会

技能体系不是一次建完就结束的,它需要持续迭代。我在项目里建立了一个简单的调用日志表,记录每次调用的用户原始输入、模型选择技能、参数内容、执行结果、用户后续反馈(比如是否追问、是否投诉)。

每周做一次日志分析,找出那些“模型选错技能”或“用户对结果不满意”的案例,更新对应的技能描述或调整技能的优先级。坚持下来,技能体系会越用越顺手。

这里再分享一个小技巧:条件允许时,可以在测试阶段同时跑两套技能描述(A/B测试),对比同一批用户问题的命中率,选择效果更好的那版发布。我就是用这个办法把“查询订单状态”技能的描述改了四个版本,最终定下来的是包含“物流/配送/签收/在途”多个同义词表达的那版,命中率从73%提升到91%。

5.3 围绕“技能市场”思路沉淀团队资产

最后说一个团队协作层面的思考。很多团队的项目是短期、可复用的,但技能往往和个人经验绑定。我建议项目里把技能定义独立于代码仓库单独维护,形成团队内部的“技能市场”。每个技能定义、描述、参数Schema、测试样例、踩坑记录都可以沉淀下来,新项目直接复用现成技能,而不是重新发明轮子。

在维护 agent-skills 项目的过程中,我最深的体会是:Agent开发重的不是模型调参,而是把业务拆解成模型能理解的一等公民。技能描述写得好不好,参数Schema定义得规不规范,编排逻辑是否清晰,这些细节的累积决定了你的Agent是“能用”还是“好用”。如果你正在疲惫于跟模型的对话调来调去,不妨回头看看自己的技能体系,也许问题根本不在模型,而在于你给它的工具和规则还不够清晰。

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

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

立即咨询