资讯详情

agent-skills:构建可复用、可评估的AI Agent技能库

📅 2026/10/21 4:06:05 | 华诺云谱 👁 阅读
agent-skills:构建可复用、可评估的AI Agent技能库
先说个现象。我身边不少做 Agent 开发的团队前几个月还处于“一个需求写一个 Prompt”的原始阶段每次换个场景就要从头调上下文、试工具、改解析逻辑。直到有一个做客服机器人的朋友跟我吐槽说他们把所有工具函数全都塞进一个系统提示词里结果模型经常在调错工具和重复调用之间反复横跳Token 消耗直接翻了三倍。我跟他聊完才发现问题不在于模型能力而在于他们从来没有把 Agent 的“技能”当成独立的工程单元来管理——这就是我今天想聊的 agent-skills 这个项目标题背后的核心命题。如果你也在做 AI Agent、智能客服、自动化助手或者任何基于大模型的多步骤任务系统那么技能库的设计就是你绕不开的一环。agent-skills 的核心思路是把 Agent 能被调用的各项能力——包括工具函数、参数规范、触发条件、输出格式——抽象成一套可注册、可检索、可组合的“技能单元”。它解决的是三个非常现实的问题第一如何让 Agent 在合适的时机选中正确的技能第二如何让技能具备跨场景的复用性而不是每次都在 Prompt 里复制粘贴一大段第三如何让技能的调试和评估变得像写单元测试一样可预期。这篇文章我会从一个实际构建过技能库的从业者角度把整体设计思路、结构化的技能定义方式、实测过的注册与调度机制以及真实踩过的坑完整拆给你看。1. 为什么需要 agent-skillsAgent 开发的隐性成本1.1 先理解 Agent 和技能的关系很多人对大模型 Agent 有一个误解觉得只要给模型一个 API Key再写一段详细的 Prompt它就能自动完成任务。但在实际生产环境里这种“一把梭”的方式很快就会撞墙。原因在于Agent 的本质是一个“决策循环”它要观察当前环境状态决定下一步调用哪个工具、传入什么参数然后根据返回结果决定是否继续、是否终止。这个循环里最容易被忽视的就是“决策依据”。你的系统提示词写得再漂亮如果里面夹带着三四十个工具的描述模型在处理长上下文时很容易把工具的用途记混。我曾见过一个真实案例某团队让 Agent 同时管理“查天气”和“订机票”两个工具结果模型在用户问“明天北京适合穿什么”的时候居然先去查询航班价格。这不是模型笨而是你没有给它一个足够清晰的技能边界。agent-skills 的做法是把“技能”从提示词中剥离出来变成一种结构化的注册单元。每个技能有自己的名称、描述、输入参数、调用入口和回调逻辑。Agent 的核心循环只负责两件事根据用户意图在技能列表里做检索匹配然后执行命中的技能。这样提示词里只需要保留“什么时候该用哪个技能”的规则而不需要把工具的完整实现细节都塞进去。1.2 技能库能解决什么具体痛点先说维护成本。没有技能库的时候每新增一个能力就要修改系统提示词而提示词一变等于整个 Agent 的行为都可能跟着漂移。你可能只是加了一个“计算运费”的工具描述结果模型从此在回答“怎么退货”的时候也开始先算运费了。有了技能库新增能力只是往注册表里加一条记录完全不影响其他技能的触发逻辑。再说复用性。技能库本质上是一种“能力的封装”。比如你为客服场景写了一个“订单状态查询”技能如果未来要做工单系统这个技能只需要改一下数据源接口其余的参数结构、返回解析、错误处理全部可以原样保留。这种复用带来的开发效率提升在长期维护中非常明显。然后是评估问题。没有技能库时你只能靠人工去看对话记录判断这个 Agent 刚才的表现好不好。有了技能库你可以对每条技能做独立的成功率统计调用次数、命中率、参数错误率、超时率。这些数据可以直接暴露瓶颈——比如某个技能经常选错说明它的触发描述写得有歧义某个技能频繁超时说明你的工具接口需要优化。就我自己的经验把技能定义为独立单元之后团队里对 Agent 的讨论能从玄学变成工程这本身就是最大的收益。2. agent-skills 核心设计技能的结构化表达2.1 技能的元信息结构技能的本质是一个带元信息的函数但这层元信息决定了模型能不能正确使用它。我建议每个技能都至少包含下面这些字段name技能的唯一标识比如query_order_status。命名要动词开头并且描述性足够强避免用func1这种抽象名字。description面向模型的功能描述。这里有个关键技巧——要写“这个技能在什么场景下有用”而不是只写“这个技能做什么”。比如“当用户想查询自己订单的物流进度时使用”就比“查询订单物流”有效得多因为前者给了模型触发判断的依据。parametersJSON Schema 格式的参数定义。必须标明每个参数是否必填、类型、默认值、取值范围。模型靠这个来生成正确的参数值。handler实际的执行函数入口可以是本地函数、HTTP 接口调用也可以是另一个 Agent 任务的入口。tags轻量级的分类标签比如customer-service、order用于后续的检索筛选。你可能会问description 里加触发场景和系统提示词里写规则有什么区别区别在于作用范围。系统提示词是全局生效的所有技能、所有对话都要带着它跑而技能描述只在这个技能被检索命中时才参与计算。这样做的直接好处是模型在每一步决策时只需要比较当前用户输入和各技能的描述相似度而不需要反复阅读一大段全局规则。2.2 技能分层原子技能与复合技能我在实践中发现把所有技能都放在同一层级是一个很常见的错误。比如“处理退款”这个操作它实际上包含“查询订单”、“验证退款资格”、“执行退款”、“通知用户”四个步骤。如果把它定义成一个独立技能参数会变得异常复杂而且一旦其中某一步失败整个技能的状态很难回滚。更好的做法是把技能分成两层。原子技能是不能再拆分的最小操作单元比如“查询订单状态”、“扣减库存”复合技能则是一个编排层它内部会按顺序调用多个原子技能并负责处理它们之间的数据流转和异常情况。在这个设计里Agent 的主循环看到的是相对有限的复合技能入口而真正的复杂度被下沉到了编排逻辑中。这个思路很像写代码时区分函数和函数调用者。复合技能就像控制器层负责编排原子技能就像服务层只做单一职责的事情。我建议把绝大多数的业务规则判断放在复合技能里不要让 Agent 的模型推理来承担太多分支逻辑因为模型的稳定性远不如代码。2.3 技能与工具、提示词的关系很多人会把“技能”和“工具函数”混为一谈但实际上它们是有区别的。工具函数是执行动作的底层能力它不知道也不关心业务上下文而技能则是在工具之上包了一层“何时用、怎么用”的语义层。举个例子“转账”是一个工具函数它有 from、to、amount 三个参数但“给父母转账的技能”可能还会包含检查余额、确认赡养额度、发送通知提醒这些附带动作——这就是技能。再说和提示词的关系。我见过最差的架构是把所有技能的完整代码和描述全部写进系统提示词让模型“自由发挥”。这种做法的结果通常很惨烈不仅 Token 消耗大模型还会出现“幻觉调用”——它认为自己能执行某些技能实际上这个技能根本不存在。正确的关系应该是提示词里只需要写清决策规则和最高层级的技能列表而每个技能的细节存在技能注册表里由调度机制按需加载。这样既保留了模型在复杂场景下的弹性决策能力又不会让它陷入信息过载的困境。3. 手把手实现一套 agent-skills 技能库3.1 技能目录与注册表设计先规划目录结构。我的习惯是每个技能一个独立目录里面包含skill.yaml元信息、handler.py执行入口、schema.json参数定义。目录结构如下skills/ ├── order/ │ ├── query_status/ │ │ ├── skill.yaml │ │ ├── handler.py │ │ └── schema.json │ └── cancel_order/ │ ├── skill.yaml │ ├── handler.py │ └── schema.json ├── payment/ │ ├── create_refund/ │ │ ├── skill.yaml │ │ ├── handler.py │ │ └── schema.json │ └── verify_payment/ │ ├── skill.yaml │ ├── handler.py │ └── schema.json └── registry.jsonregistry.json是核心索引文件它记录所有已注册技能的入口路径和加载状态。系统启动时会扫描这个文件把每个技能的 description 和 parameters 提取出来构建成模型可用的检索索引。为什么要用独立的 YAML 文件而不用纯 Python 字典因为在团队协作中技能元信息的维护者可能不完全懂代码拆分文件可以让他们独立编辑描述和参数不用碰主程序逻辑。而且 YAML 天然适合表达层级结构写起来也比 JSON 简洁得多。3.2 技能注册与加载机制技能加载这块我建议做成一个懒加载机制。系统启动时只加载技能的元信息到内存里用于检索真正执行时再动态导入 handler。这样做的考虑是一个大型系统的技能可能有几百个如果全部加载完成再启动启动时间会变得很难看而且很多技能实际使用频率并不高。懒加载的核心代码如下import importlib import json from typing import Any class SkillRegistry: def __init__(self, registry_path: str): self.registry_path registry_path self._skills: dict[str, dict] {} self._executors: dict[str, Any] {} def load_metadata(self) - None: # 加载所有技能的元信息只取 name、description、parameters with open(self.registry_path, r, encodingutf-8) as f: index json.load(f) for item in index[skills]: if item[enabled]: self._skills[item[name]] { description: item[description], parameters: item[parameters], module_path: item[module_path], } def execute(self, skill_name: str, **kwargs) - Any: # 首次调用时动态导入 handler后续直接复用 if skill_name not in self._executors: mod importlib.import_module(self._skills[skill_name][module_path]) self._executors[skill_name] mod.execute return self._executors[skill_name](**kwargs)这个实现里有几个值得注意的地方。第一load_metadata只加载元信息不执行任何工具代码所以即使某个技能依赖的第三方库没装好也不会拖垮整个系统启动。第二execute里做了缓存同一个技能只会被 import 一次后续调用开销很小。第三enabled字段支持动态下线某个技能这在线上故障排查时特别有用出问题的技能可以直接摘掉不需要重新发版。3.3 技能方式的检索与调度实现有了技能库Agent 核心循环里要做的就是从技能表里挑出最合适的那一个。这个过程我建议分两层先用关键词做粗筛再用向量相似度做细排。粗筛可以用很轻量的方式比如基于 TF-IDF 或简单的词表匹配。这一步的目的是把候选技能从几百个缩小到三五个避免向量检索在大规模技能库上的性能问题。细排则用 embedding 模型把用户的问题和候选技能的 description 分别编码成向量算余弦相似度。下面是一个简化版的调度逻辑from sklearn.feature_extraction.text import TfidfVectorizer import numpy as np def retrieve_skill(query: str, registry: SkillRegistry, top_k: int 3) - list[str]: # 粗筛用 TF-IDF 从全部技能里挑出候选 all_desc {name: meta[description] for name, meta in registry._skills.items()} # 这里是简化演示实际工程会用预计算的索引结构 vectorizer TfidfVectorizer() corpus [query] list(all_desc.values()) tfidf vectorizer.fit_transform(corpus) scores tfidf[0].toarray()[0] tfidf[1:].toarray().T ranked np.argsort(scores)[::-1][:top_k] candidate_names list(all_desc.keys()) return [candidate_names[i] for i in ranked]细排部分我用的是主流的 embedding 模型通过计算用户 query 和候选技能 description 的向量相似度取最高分作为最终命中的技能。这里有一个关键细节候选技能的 description 必须越具象越好比如“当用户对某个订单发起取消请求时使用”要比“处理取消订单”效果好得多。原因在于embedding 模型对语义相关的匹配更敏感具象的句子能提供更好的语义锚点。调度机制的另一个重要模块是参数填充。模型决定调用某个技能后还需要根据用户输入生成参数。常见做法是把参数的 JSON Schema 描述交给模型让它提取对应的内容。这里的坑在于模型生成参数时经常会出现“多填”或“漏填”所以一定要在真正执行 handler 之前做一个参数校验层把不符合 schema 的内容拦截下来。3.4 一次完整的技能调用全流程结合上面的模块我用一个具体场景串起来让你看全流程长什么样。假设用户说“帮我查一下昨天买的耳机到哪了。”第一步Agent 收到消息请求调度模块做技能检索。粗筛阶段因为 query 里含有“查”“到哪了”候选中会出现query_order_status、query_logistics、query_product_info这几个。第二步细排。query_logistics的 description 是“当用户查询物流进度、包裹位置、到货时间时使用”和用户问题语义最接近命中。第三步参数提取。模型根据该技能的 schema生成参数order_id、delivery_scoperecent。由于用户没有提供订单号系统需要回问或者通过账号信息和时间范围去模糊匹配——这里就体现了技能设计中参数可选的兜底能力。第四步参数校验通过进入 handler 执行。handler 从内部 API 获取物流信息返回一个结构化的 JSON 结果。第五步Agent 拿到结果生成面向用户的自然语言回复“耳机从上海发出预计明天下午到目前已经在韵达转运中心。”整个流程中每一步都有独立的知识表示不会出现“模型既要做决策又要执行业务逻辑”这种耦合问题。这正是 agent-skills 方案最核心的价值。4. 踩坑记录与排查技巧实录4.1 技能命名与描述的第一个大坑我在刚搭建技能库时犯过一个很蠢的错误。当时我给所有技能按业务模块做了命名比如order_1、order_2还美其名曰“避免人工理解偏差”。结果模型检索时经常抽风把一个“查询订单”放在了“申请发票”的位置。后来我花了三天时间排查才发现问题出在描述上——order_1的 description 是“查询订单状态”而order_2的 description 是“申请发票”两者都和“订单”相关但是模型对数字编号的关联能力远远不如对语义描述的理解。这给我一个重要教训技能名字可以短但 description 一定不能用业务黑话要站在“模型该在什么场景下用这个技能”的角度去写。用一个公式来说好的 description 触发场景 操作对象 操作内容。比如“当用户想查询自己订单的物流信息时使用输入为订单号或订单日期范围”比单纯写“查询订单物流”要好用得多。4.2 技能冲突与优先级问题当技能库到了上百个规模的时候技能之间的边界模糊就成了主要矛盾。比如你有一个“查询天气”的技能还有一个“推荐户外活动”的技能用户问“明天下雨还能爬山吗”两个技能的匹配分数都会很高这时候就可能挑错。我后来引入了一组规则来处理优先级。一是建立技能级别的“互斥声明”明确标识哪些技能不能同时激活。二是在检索阶段引入 context 信息比如如果用户上一轮问了“杭州天气”当前问题的技能检索就会优先偏向户外类技能而不是纯天气类。三是给高业务风险的操作增加“人工确认”前置环节——比如退款、删除数据这类技能不会直接执行而是先输出一个确认请求给用户。这个优先级机制的设计思路本质上是在“模型的自由决策”和“系统的确定性约束”之间找平衡。千万别让 Agent 在涉及资金、隐私、数据删除的场景下全自动执行哪怕模型能力再强也不行。4.3 上下文过长与技能裁剪问题技能库做大了另一个副作用是检索时上下文膨胀。每次要把候选技能的完整参数 schema 传给模型如果候选技能有 5 个且每个参数都有一二十项生成请求的 Token 消耗会直线上升。我的解决办法是给技能做“裁剪视图”。每个技能配置两套 schema一套是完整版用于最终参数生成另一套是精简版只包含最关键的三五个参数用于首次检索时拼装 Prompt。模型只有在确认要调用某个具体技能之后才会看到完整版的 schema。这个方案在测试中能把 Token 消耗降低约四成而且对参数生成的准确率影响很小。4.4 技能评估与回归测试技能库不是写完就结束了它是一个需要持续迭代的系统。我在团队内部建立了一套回归测试机制每天定时把一批标注好的历史用户问题跑一遍统计每个技能的命中准确率、参数完整率、执行成功率。任何一个技能的指标出现下滑就会触发告警提醒相关开发者检查是不是 description 被改动了或者是底层 API 的响应格式变了。这里有一个容易被忽略的细节技能调用的失败不一定都是技术问题可能只是用户意图变了。所以我做评估时会把“技能选择是否合理”和“技能执行是否成功”分开统计。一个执行成功的调用如果技能选错了在业务上仍然是失败的。这个区分能帮你更精准地定位问题出在调度层还是执行层。再说一个实操上的小技巧。我在技能仓库里给每个 handler 都加了一个标准的日志输出格式统一记录入参、出参、耗时和异常信息。联调阶段你可能觉得这一步很麻烦但上线之后你能靠这个日志快速定位是参数提取的问题、还是第三方接口的问题这能省下大量排查时间。5. 从技能库到 Agent 工程化后续的方向技术方案演进到一定阶段后我回头看 agent-skills 这个项目标题体会最深的一点是技能库本质上给 Agent 的开发建立了一个“可测试单元”。没有这个单元Agent 的开发永远是 prompt engineering 的玄学你只能用“我觉得这句话应该这样写”来迭代。有了技能库你就可以像写单元测试一样去验证每一个能力边界。我目前的进一步规划是把技能的描述、schema 和 handler 的测试用例统一放到同一个仓库里每次改动技能必须连同测试用例一起更新测试通过才能合并。这一步推进之后Agent 开发的协作体验其实已经很接近传统的后端开发了——有明确的接口契约有统一的回归保障有清晰的运行监控。如果你正在做 Agent 开发我建议你从自己的项目里挑一个薄弱环节试着把它拆成两三个独立的技能来管理。你可能会发现Agent 的“能力”感比之前强很多因为它不再是一个什么都懂一点但什么都不精的“提示词大师”而是一个有着清晰工具箱和判断边界的工作助手。这正是 agent-skills 这类工程化项目最有魅力的地方。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑