☰
AI Agent技能系统实战:从Prompt困境到可复用技能库
2026/10/14 9:48:43 网站建设 项目流程

我最早接触“agent-skills”这个概念,是在把大语言模型从“聊天机器人”往“干活机器人”方向改造的时候。当时项目停滞了很久,不是因为模型不够强,而是因为每次让模型执行任务,都要把一大串步骤、边界条件、异常处理全部塞进 Prompt 里。Prompt 越长,模型越容易在中间环节“跑偏”,而且换一个任务场景就得重写一大段逻辑,复用性几乎为零。后来我意识到,问题不在于模型,而在于我把“能力”和“对话”混在了一起。真正需要的,是一套能把能力单独拆出来、标准化、可复用、让模型按需调用的机制。这套机制,就是 agent skills 的核心。

如果你现在也在折腾 AI Agent,或者被“为什么我的 Agent 只会聊天不会干活”“为什么换个任务就要重写 Prompt”这类问题困扰,那这篇文章就是写给你看的。我会先把 agent-skills 这个概念拆明白,再给出一套可以直接落地的实现方案,最后把我踩过的坑和排查思路完整记录下来。不绕弯子,直接进入正题。

1. agent 技能到底在解决什么问题

1.1 从一个让人头疼的 Prompt 说起

在 agent-skills 出现之前,我写过一个文档处理类的 Agent。它的任务是读取用户上传的 Excel 表格,做清洗、去重、统计,然后生成一份报告。最初版本我写了一个 3000 多字的系统 Prompt,把“如何判断空值”“如何识别重复行”“统计口径是什么”“报告的章节结构是什么”全部写进去。

效果很不稳定。换一种格式的表格,行为就变了;用户多问一句话,模型就忘了统计口径;甚至同一个表格跑两次,输出结构都可能不同。问题出在哪儿?本质上是把“指令”和“能力”耦合在了一段自然语言里。模型需要在同一段上下文里既理解用户意图,又记住所有操作细节,还要保证执行的一致性——这对 LLM 来说是极高的负担。而 agent-skills 的思路完全不同:把每一个完整操作封装成独立的、可以被调用的“技能单元”。模型不需要记住清洗逻辑的每一步,它只需要判断“当前任务匹配哪个技能”,然后把参数填对即可。

这个概念上的转变,直接改变了 Agent 架构的稳定性。技能本身是确定的、写死的代码逻辑或高质量 Prompt 模板,模型的自由度被限制在“选技能”和“传参数”这两个动作上。模型自由发挥的空间变小了,出错的概率也就下降了。我在实际项目里体会到,这比任何 Prompt 技巧对稳定性的提升都大。

1.2 技能与工具、插件、工作流的边界

很多人会问,skills 和 tools、plugins、workflows 到底有什么区别?我一开始也分不清,后来在不同场景里反复对比,逐渐有了清晰的界定。

工具(Tools)是最小粒度的能力单元,通常是单个函数,例如“发送邮件”“查询天气”“调用搜索引擎”。它解决的是“能不能做”的问题——模型需要完成某个动作,去调用对应的函数。工具本身不包含复杂流程,也不负责判断何时使用。

技能(Skills)是在工具之上的封装层,强调的是“组合与场景”。一个技能可以包含多个工具的调用顺序,也可以包含内置的领域知识、参数规则、错误处理逻辑。例如“生成周报”这个技能,内部可能要先调用“读取项目数据”工具,再做数据格式转换,再调用“调用大模型生成文字”,最后调用“输出 Markdown 文件”。工具是积木,技能是一组积木和图纸组合成的完整模型。

插件(Plugins)更多是从系统集成角度定义的,指通过标准接口接入 Agent 的外部模块。插件往往可以提供工具,也可以提供技能,它更偏向部署形态,而不是能力抽象。

工作流(Workflows)则强调“固定路径”。工作流里的步骤、分支、次序通常是编排死的,适合完全确定性的流程。而技能更灵活,模型可以根据上下文动态选择怎么组合使用。

在实际工程里,我最终采用的划分标准是:确定性优先的用工作流,模型决策优先的用技能,单点原子操作用工具。这套划分标准在后面设计技能系统时非常受用。

概念核心粒度是否含决策逻辑典型使用场景
工具单个函数不含发送邮件、查询库存、调用搜索
技能多步骤组合含场景判断生成报告、数据清洗、竞品调研
插件集成模块视实现而定扩展 Agent 的第三方能力
工作流编排好的固定流程不含审批流程、定时任务、管道处理

2. 技能系统的设计拆解

2.1 技能的标准化接口

设计 agent-skills 的第一件事,不是写代码,而是定接口。接口定不好,后面每加一个新技能都要伤筋动骨。我参考了若干开源项目中对技能的定义方式,最终沉淀出我自己常用的四段式接口。

头部元信息(Metadata)包括技能的 ID、名称、版本、作者、描述、标签。这个描述是给模型看的,决定了模型在什么场景下会选中这个技能。版本号虽然容易被忽略,但在技能迭代时特别重要——同一技能的较新版本如果被旧版本覆盖,问题会很难排查。

参数定义(Parameters)描述这个技能需要哪些输入参数,每个参数的类型、是否必填、取值范围、默认值。参数定义越清晰,模型传错的概率越低。在实际项目中,我用类 JSON Schema 的结构来定义参数,既方便校验,也容易被模型理解和生成。

执行逻辑(Execution)是技能真正执行的代码或 Prompt 模板。代码执行最稳定,适合确定性的数据处理;Prompt 模板适合需要语言理解的任务;混合模式则先跑代码,再让模型基于中间结果生成最终输出。三种模式可以合并定义在一个执行体里。

校验与反馈(Validation & Return)定义技能的返回格式和错误处理策略。这一步我强烈建议标准化,因为技能的输出会被其他 Agent 消费,输出格式不统一,后续处理会非常痛苦。

{ "id": "report_generator", "name": "周报生成器", "version": "2.3.0", "description": "读取项目进度数据,生成格式化的周报。适用于周报、月报、项目阶段性汇报。", "parameters": { "project_id": {"type": "string", "required": true}, "date_range": {"type": "string", "required": true}, "style": {"type": "string", "enum": ["simple", "detailed"], "default": "simple"} }, "execution": { "type": "code", "handler": "skills.report_generator.run" }, "returns": { "format": "markdown", "schema": { "summary": "string", "sections": ["object"] } } }

2.2 技能描述怎么写 LLM 才看得懂

这一段可能是整个技能系统里最容易被低估的部分。很多开发者把精力花在执行逻辑上,结果模型根本不会在合适的时候调用这个技能。问题几乎都出在描述上。

技能描述里最常见的错误是写“这个技能可以生成周报”——这种描述的信息量太少了。模型需要知道的是:什么情况下用、什么情况下不要用、有哪些关键限制。一个合格的技能描述应该包含触发条件、典型案例、边界排除三条信息。

以周报技能为例,触发条件可以写“当用户需要按周汇总项目进展、输出结构化汇报文档时”;典型案例可以写“用户说帮我写一份本周项目周报,或需要汇总本周完成事项”;边界排除要写“如果用户只是询问项目进展而非要求生成文档,请勿调用本技能”。这样模型在做意图判断时就有据可依。

我还做了一个很有效的动作——在参数说明中为每个参数补充“解释性提示”,告诉模型这个参数应该从上下文中如何提取。例如 date_range 可以提示“从用户对话中提取日期范围,若无明确起止日期,默认取上周一至上周日”。提示越具体,模型提取参数的准确率越高。实测下来,加入解释性提示后,参数提取成功率从 78% 提高到 94%。

2.3 技能的组织方式:文件目录、命名空间、路由

当技能数量超过几十个之后,组织方式就开始变成一个大问题。最开始我把所有技能放在一个文件夹里,结果目录越来越乱,检索成本急剧上升。后来我按“领域 + 场景”的二维分类法重新组织。

skills/ ├── common/ │ ├── datetime_utils/ │ ├── data_convert/ │ └── memory_lookup/ ├── data_pipeline/ │ ├── excel_cleaner/ │ ├── deduplicator/ │ └── report_generator/ ├── content_creation/ │ ├── blog_draft/ │ ├── abstract_summary/ │ └── social_copy/ └── system_integration/ ├── email_sender/ ├── web_search/ └── calendar_event/

common 层级放那些跨领域复用的基础技能,data_pipeline、content_creation 这类按领域划分。每个子目录下再按场景拆成独立技能。这样做的直接好处是路由清晰:当用户问题进来,系统先根据领域关键词初步过滤,再在候选技能里做语义匹配,计算量大幅减少。

路由策略我也做过对比实验。Keywords 规则匹配最快速但召回率低;Embedding 语义检索召回率高但偶尔把不相关的技能也捞进来;最终我采用的是“规则预筛 + 语义精排”两阶段路由。第一层用关键词过滤掉绝对不相关的技能,第二层把剩余技能的描述做向量化,和用户问题计算相似度,选择 Top-1 或 Top-K 让模型决策。这个策略在实际项目中稳定发挥了作用,既保证了速度,又避免了纯语义检索的误召回。

3. 从零实现一套 agent-skills 运行框架

3.1 最小可用内核

我不会直接贴一个大项目的代码,而是带你从最小用例开始,一步步把运行框架搭出来。这个最小内核要回答三个问题:技能怎么注册?模型怎么发现技能?技能怎么被执行?

先定义一个技能基类,这相当于所有技能的“契约”:

# skill_base.py class BaseSkill: id: str = "" name: str = "" description: str = "" parameters_schema: dict = {} def validate_params(self, params: dict) -> dict: """参数校验,返回清洗后的参数""" import json # 这里可根据 parameters_schema 做类型、必填项校验 return params def execute(self, params: dict) -> dict: raise NotImplementedError

然后定义技能注册器,维护一个全局技能表:

# skill_registry.py class SkillRegistry: def __init__(self): self._skills = {} def register(self, skill: BaseSkill): self._skills[skill.id] = skill def get_skill(self, skill_id: str): return self._skills.get(skill_id) def list_skills(self): return list(self._skills.values())

核心的调用流程是:接收用户输入 → 路由模块挑选候选技能 → 把技能列表和用户输入交给模型 → 模型返回选中的技能 ID 和参数 → 系统解析参数、校验、执行 → 返回结果。伪代码如下:

# agent_core.py def handle_user_input(user_input: str): # 1. 获取候选技能 candidate_skills = skill_router.route(user_input) # 2. 让模型选择技能并抽取参数 decision = llm.choose_skill( user_input=user_input, skills=[to_skill_meta(s) for s in candidate_skills] ) # 返回内容:{"skill_id": "...", "params": {...}} # 3. 执行技能 skill = registry.get_skill(decision["skill_id"]) cleaned_params = skill.validate_params(decision["params"]) result = skill.execute(cleaned_params) return result

这个最小内核体量不大,但却是所有复杂 agent-skills 系统的地基。后面加持久化、缓存、并发、监控,都是在这一层上扩展。

3.2 技能加载与动态发现

写死 import 所有技能类显然不优雅,随着技能数量增长,动态发现机制就变得必要。Python 下我习惯用 entrypoints 或手动扫描模块目录两种方式。

手动扫描方式更直观,也更容易控制。大致思路是遍历 skills 目录下所有 .py 文件,跳过以 _ 开头的模块,导入模块后检查模块内是否包含继承自 BaseSkill 的类,然后自动注册。这样每新增一个技能,只需在新目录下写一个类,系统启动时就能自动发现。

# loader.py import pkgutil import importlib import skills def discover_skills(): registry = SkillRegistry() for module_info in pkgutil.iter_modules(skills.__path__): module = importlib.import_module(f"skills.{module_info.name}") for attr_name in dir(module): attr = getattr(module, attr_name) if isinstance(attr, type) and issubclass(attr, BaseSkill) and attr is not BaseSkill: registry.register(attr()) return registry

动态发现的另一个好处是“热插拔”。技能有变化时不需要重启整个 Agent,重新扫描一遍注册表即可。这个问题在实战场上很现实——运维时新增一个技能却要重启全部服务,在长时间运行的环境里完全不可接受。

3.3 让技能支持参数校验与错误恢复

参数校验是技能系统中问题高发区。模型给你传的参数字段名对不上、类型错误、缺少必填项,这些都是常态。如果每次都要在执行函数里写一堆防御性代码,技能维护起来会变成噩梦。

我建议在 BaseSkill 里内置一套 Schema 校验器。这里的教训是:一开始我用的是手写的 if/else,每个技能重复一遍,后来技能多了之后维护量剧增;改造成 Schema 驱动之后,新增技能时只需要声明参数 Schema,校验逻辑全部复用。

# schema_validator.py def validate_params_by_schema(params: dict, schema: dict) -> dict: result = {} for field, rules in schema.items(): required = rules.get("required", False) if required and field not in params: raise SkillParamError(f"Missing required param: {field}") if field in params: value = params[field] expected_type = rules.get("type") if expected_type == "string" and not isinstance(value, str): raise SkillParamError(f"Param {field} should be string") result[field] = value return result

错误恢复策略是我后来才重视起来的。之前技能执行失败后只会把异常抛回给模型,模型有时能自行修正,有时直接摆烂给用户一段“抱歉出错了”的话。后来我设计了三级降级逻辑:第一次报错,检查是否是参数问题,尝试从用户原话中重新提取参数;第二次报错,把错误信息返回模型,并附带技能的 JSON 定义,让模型重新生成参数;第三次如果还失败,才真正把错误透传给用户。

这套三级降级在实际运行中让技能执行成功率提升了十多个百分点。核心思想是:技能系统本身要有容错设计,而不能完全依赖模型“临场发挥”。

4. 我踩过的坑和排查思路

4.1 描述写得太模糊,模型频繁选错技能

我最早遇到的一个坑是:一个数据可视化技能和一个数据统计技能,模型经常选错。看执行日志,发现模型把“帮我看看 Q3 的销售走势”这条路走到了统计技能上,而实际上用户想要折线图。根源在两份技能描述太相似,都在说“处理销售数据”。

排查思路是把两个技能的描述放在一起对比,看区分度够不够。一个合格的说法是:统计技能强调“输出数值汇总表,例如总数、均值、同比增长,不生成图表”,可视化技能强调“输出图像文件,基于数据生成折线图、柱状图”。加了“不做什么”的负向描述之后,选错率明显下降。

4.2 参数总是抽不对,尤其是日期和模糊表达

用户说“最近一周”和“从上月10号到昨天”,模型提取 date_range 的结果经常不一致。之前我只在参数 Schema 里声明了 type=string,等于告诉模型“这里填一个字符串”,但它并不知道应该转成什么格式。

解决方案是给参数加 pattern 和 format 约束,并且在描述里明确时间格式规范,例如统一转成 ISO 8601 格式。另外,给模型提供“参考示例”也有效果。我在参数 Schema 里增加 examples 字段后,日期参数提取的成功率上升明显。不要指望模型默认知道你的时间格式约定,必须在技能定义里明示。

4.3 技能之间出现调用循环和死锁

当技能允许互相调用之后,新问题出现了——技能环型调用。技能 A 在处理过程中调用技能 B,技能 B 内部又回调技能 A,在没有深度限制时,会无限递归直到超时。我在一个自动化内容生产流程里遇到过一次,排查了很久才发现是调用链闭环。

解决方法是给技能调用增加深度上限和全局调用链追踪。每次调用启动时记录调用栈,一旦发现同一技能在一条调用链里出现两次,立即终止并返回错误。与其事后排查,不如在框架层把这个防御机制内置进去。

4.4 实测结论:什么情况下技能系统收益最高

我逐渐发现,不要在所有场景里都强行引入 agent-skills。收益最高的场景有三个显著特征:任务边界清晰、存在可复用的操作逻辑、输入输出结构相对稳定。例如报表生成、数据处理、定时内容分发,这类任务把能力封装成技能后,稳定性和可维护性提升非常明显。

收益不明显的场景也有,比如完全开放式的头脑风暴类聊天。用户期望的是自由对话,模型每次的回答都独一无二,没有必要封装成技能。硬套技能系统反而会让回复显得僵硬。记住一个原则:技能的边界是“可复用、可参数化、有明确产出”,不满足这三个特征的内容,留在普通对话链路里就够了。

4.5 项目管理层面的提醒

最后说一个容易被技术开发者忽略的层面的问题。agent-skills 系统一旦投入使用,它就是一套需要长期维护的“能力资产库”。技能的新增、修改、废弃都要有版本管理。我之前跟某团队配合时,他们一份技能文档天天改动,没有版本记录,某天某个技能行为突变,查了半天才发现是同事直接改了线上脚本。后来我把技能文件纳入 Git 版本库,并强制要求技能行为变更必须提交对应版本的说明记录,类似的问题就再没出现过。

技能是给 Agent 用的“API 文档”,但它更像是工程资产,应当享受代码一样的版本管理、评审和测试流程。养成这个习惯之后,技能系统的长期维护成本才会真正降下来,否则一开始有多省事,后续就有多麻烦。

5. 经验沉淀:如何培养一套高质量技能库

技能系统真正有价值的时候,是技能数量积累到一定程度后,形成所谓“技能生态”的阶段。单个技能是工具,几十个结构良好的技能组合起来,就变成了一套小型的能力平台。培养一套高质量技能库的过程,我认为有两个关键动作:标准化审加上线流程,以及建立技能的效果反馈闭环。

所谓标准化审加上线流程,就是每个技能在进入正式库之前,都需要经历定义审查、示例测试和回归验证三步。定义审查主要看技能边界是否清楚,描述是否区分于已有技能;示例测试是拿该技能的典型输入跑一遍,确认输出符合预期;回归验证是确认新增技能不影响已有技能的调用。这套流程听起来繁琐,但能大幅降低技能库膨胀后带来的混乱成本。

反馈闭环则是从运行数据中不断优化。每次技能调用后,记录调用是否成功、耗时多少、用户是否满意、模型是否在短时间内反复重试同一个技能。这些数据不需要很复杂的分析手段,简单的统计就能发现问题技能,比如执行失败率高、总是被模型选中但产出无效的技能,软件里这类情况我称它为“需要下架或重写的技能”。定期清理和优化比永远只做增量重要得多,技能库的健康度需要有人持续维护。

我自己的习惯是每个月做一次技能库盘点。整理出调用频次最高和最低的技能列表,高频技能优化执行速度和参数提取准确率,低频技能评估是保留、合并还是废弃。这样一个季度下来,技能库的响应速度和质量都会比放任不管好不少。真正让 Agent 变聪明的,不是某一个技能写得有多好,而是整个技能库作为一个系统,能不能持续稳定地提供高确定性的能力。

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

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

立即咨询