
最近很多朋友都在折腾自己的智能体问得最多的一句话是“你的agent到底会什么技能”一开始我以为他们问的是模型能力后来才明白他们问的是agent能不能像人一样“会做事”。这背后就是今天想聊的主题agent-skills也就是智能体技能体系的构建。agent-skills不是某一个具体工具而是一整套设计思路把agent能完成的任务抽象成标准化的技能单元统一注册、统一调度、统一评估。它要解决的核心问题有三个第一让agent从只能“说”进化到能够“做”第二让能力可以沉淀、复用而不是每次都现写提示词第三让多个agent之间形成能力互借而不是各自造轮子。这篇文章主要适合正在做LLM应用开发、智能客服、办公自动化助手的团队也适合那些手头有demo但不知道怎么走向生产的个人开发者。1. 智能体技能体系的理解与设计1.1 为什么“技能”比“提示词”更适合智能体很多人第一次做agent就是往系统提示词里塞一大段话“你是企业助手你会查天气、会订会议室、会发送邮件……”这种方案demo跑得通但一上生产就露馅。原因很简单提示词是静态的而任务是多变的。你写进提示词里的功能描述再多模型也只能“知道”这些功能无法真正“执行”它们。它无法查数据库无法调用外部API也无法感知当前操作是否成功。技能抽象改变的是这个基本逻辑。一个技能不是一句话而是一个完整的能力单元。举个例子同样是“查询天气”提示词方案是告诉模型“你是天气助手可以查天气”模型会自己编一个天气出来技能方案则是注册一个weather.query技能底层挂载真实的气象API并定义好城市、日期等参数。模型要做的是识别意图、填充参数、触发执行而不是凭空生成结果。用装修来类比可能更直观。提示词方案像是给工人一张图纸图纸画得再细工人也得自己买材料、自己加工技能方案像是给工人一个模块化工具箱里面的柜体、台面、五金件都是预制好的工人只需要根据图纸选择合适的模块装上去。图纸当然重要但决定工程质量和交付速度的是工具箱里有什么模块、模块之间怎么衔接。这就解释了为什么“技能”比“提示词”更适合agent走向生产环境。技能具备三个关键特性可复用性、可测试性和可观测性。同一个“发送邮件”技能可以被客服agent用也可以被运营agent用技能单独测试通过后集成到任何一个agent里都相对可靠技能每次执行都会留下日志和结果出了问题能快速定位。这些特性在纯提示词方案里几乎做不到。1.2 技能库的整体架构设计聊技能就绕不开技能库也就是agent-skills里“skills”的载体。我在项目中把技能库拆成四个核心模块技能注册表、技能路由、技能执行器、技能上下文管理。这四个模块各司其职组合起来就是一套完整的技能生命周期管理。技能注册表是“技能的目录”保存所有已注册技能的定义信息包括技能ID、描述、参数结构、执行入口等。路由是“调度员”根据用户输入和会话状态从注册表里挑出最合适的技能。执行器是“真正的工人”负责运行技能对应的代码逻辑或API调用。上下文管理则是“共享内存”负责存放技能执行过程中产生的中间数据以及技能与主对话之间的信息交换。这个架构的关键决策在于技能的定义、路由和执行必须解耦。如果把技能描述直接写在agent的主逻辑里那每新增一个技能都要改主代码解耦之后新增技能只需要在注册表里加一条记录主流程完全不用动。我实测下来这种解耦对项目初期的开发速度提升非常明显尤其是当技能数量超过20个以后。架构上还有一个容易被忽略的点技能之间的依赖和隔离。早期我设计技能库时允许所有技能共享同一个执行环境结果一个技能的内存泄漏直接拖垮了整个agent。后来改成每个技能独立进程或独立函数沙箱成本略高但稳定性好得多。如果你只是做轻量级agent可以不用进程级隔离但至少要在代码层面保证技能之间的变量和状态不互相污染。2. 技能定义与实现的实操要点2.1 技能模板的字段设计技能定义是整个体系的地基。字段设计得不好后续路由、执行、维护都会吃苦头。我在实践中打磨出一套比较通用的技能模板核心字段包括id、name、description、parameters、executor、output_schema、permissions和version。先看一个实际例子这是我在项目中定义的一个会议预订技能{ id: meeting.book, name: 预订会议室, description: 根据日期、时间段和人数预订指定办公区的会议室。当用户表达需要开会、需要讨论空间时优先调用。, parameters: { type: object, properties: { date: {type: string, format: date, description: 会议日期}, start_time: {type: string, format: time, description: 开始时间}, end_time: {type: string, format: time, description: 结束时间}, capacity: {type: integer, minimum: 1, description: 参会人数}, office_area: {type: string, enum: [A区, B区, C区], description: 办公区} }, required: [date, start_time, end_time, capacity] }, executor: { type: http, url: https://internal-api.example.com/meeting/book, method: POST }, output_schema: { type: object, properties: { booked_room: {type: string}, status: {type: string} } }, permissions: [meeting.book], version: 1.2.0 }有几个字段需要特别强调。description是写给LLM看的不是给人看的。模型靠这段描述来做技能匹配所以必须说清楚“这个技能在什么场景下用”最好带一两个典型用户表述。比如上面这个description里的“需要开会、需要讨论空间”就是我从真实对话里抽出来的高频表达加上之后路由准确率明显提升。parameters建议严格使用JSON Schema规范。原因有两点第一LLM填充参数时需要明确的类型和取值范围schema越严格模型“编参数”的概率越低第二执行器可以用同一个schema做运行时校验防止脏数据打到下游系统。executor字段则要区分类型我见过三种方案HTTP调用、Python函数调用、命令行调用。HTTP调用最灵活适合微服务架构Python函数调用延迟最低适合单体应用命令行调用适合老系统集成但解析输出比较麻烦。2.2 参数校验与技能路由技能路由是agent-skills体系里最有意思、也最容易翻车的部分。路由模块负责回答一个核心问题“用户这句话到底想触发哪个技能”我试过两种主流方案基于LLM判断和基于向量相似度。基于LLM判断的思路是把当前用户输入和上下文一起发给模型让模型从注册表里选一个技能ID返回。优点是对语义复杂、需要推理的场景非常有效缺点是每个请求都会增加LLM调用次数延迟和成本都上去了。基于向量相似度的思路是把用户输入向量化跟每个技能的description向量算相似度取top1。这个方案快且便宜但无法处理“需要结合多轮上下文才能判断意图”的情况。我最终采用的是混合路由先用向量相似度做粗筛筛出前3个候选技能再把候选技能的description拼进一个轻量级prompt让LLM做精排。这样既控制了成本又保证了一定的语义理解能力。混合路由上线后技能命中率从单用向量方案的81%提升到了94%左右延迟只增加了大约200毫秒性价比很高。参数填充也值得单独说。即便路由选对了技能模型也可能填出非法参数。比如预订会议时把开始时间写成“下午3点”而schema要求的是“15:00”。我的处理方式是模型产出参数后不直接调用执行器而是先用JSON Schema做一次校验不合格就带着错误信息让模型重填一次。这个“校验-重填”循环最多跑两轮超时就返回技能调用失败避免无限循环。2.3 技能与工具、插件的边界很多初学者分不清技能skill、工具tool和插件plugin的区别但搞清楚这个概念对设计agent-skills至关重要。在我的理解里工具是最小的可执行单元比如“发送HTTP请求”“读取文件”“计算一个数学表达式”技能则是面向业务场景的、组合了工具甚至其他技能的能力封装比如“预订会议室”技能内部可能需要“查询会议室可用时间”和“创建会议”两个工具。插件则是一个更大的分发单位。按这个分层理解技能是插件里的核心资产插件是技能的打包分发形式。OpenAI Plugins生态和各类agent框架里的tool calling本质上都是在做这一层抽象只是粒度不同。设计技能时不要把粒度做得太细否则维护成本极高也不要做得太粗否则无法复用。我自己的经验是一个技能应当对应“用户可感知的一个完整任务片段”说完一句话就能交代出去执行后能给用户一个明确结果。边界清楚了代码结构也就清楚了。技能层只编排能力不直接碰底层细节工具层只做具体执行不关心业务含义。分层设计之后新增技能时基本只写编排逻辑复用已有的工具开发效率非常高。3. 一套可落地的技能库搭建流程3.1 场景分析与技能拆解前面讲了不少设计理念下面进入实操流程。我按自己落地agent-skills的方法把它总结成了五个步骤场景分析、技能拆解、技能实现、注册集成、评估迭代。第一步是场景分析就是收集足够多的真实用户对话梳理出用户到底想让agent做什么。我建议至少准备200条真实对话记录覆盖日常请求、异常请求和模糊表达。没有真实数据就靠业务方的口头描述来穷举效果会差很多因为“用户实际怎么说”和“产品经理以为用户怎么说”往往是两套话。第二步是技能拆解。拿一个“日程管理”场景举例。用户的主要诉求包括新建日程、查看日程、改时间、取消日程、提醒我。最保守的做法是把每个诉求做成一个技能于是有了schedule.create、schedule.list、schedule.update、schedule.delete。但拆完发现schedule.list在很多场景下都要先拿到当前用户身份而身份获取本身也是一个能力于是我把它拆成了独立的user.get_context技能供其他技能调用。这个“改时间”操作看似简单但实际包含“先查旧日程”“再确认不冲突”“最后更新”所以我让schedule.update在内部调用schedule.list和schedule.check_conflict两个子技能。拆解的判断标准只有一个这个步骤能不能被其他场景复用。能复用就拆出来不能就先留在当前技能内部。第三步到第五步是持续开发的过程。我个人强烈建议技能实现后马上写测试用例而不是等全部技能开发完再统一测。因为技能的输入输出是结构化的非常适合自动化测试。我每个技能至少写三个用例正常输入、边界输入、错误输入。这样后续改动技能逻辑时回归测试能兜住大部分低级问题。3.2 技能注册与加载机制技能注册听起来简单就是“把技能加进注册表”但注册机制的设计直接影响系统扩展性。我在项目里做了一套基于配置文件的技能加载器每个技能一个独立目录目录里包含schema.json定义文件和executor.py执行逻辑文件技能加载器启动时自动扫描目录把每一个schema.json解析后注册到内存注册表。import importlib.util import json from pathlib import Path class SkillRegistry: def __init__(self, skills_dir: str): self.skills_dir Path(skills_dir) self.skills {} self._load_skills() def _load_skills(self): for schema_path in self.skills_dir.glob(*/schema.json): with open(schema_path, r, encodingutf-8) as f: schema json.load(f) skill_id schema[id] executor_path schema_path.parent / executor.py self.skills[skill_id] { schema: schema, executor: self._load_executor(executor_path, skill_id) } print(f[registry] loaded skill: {skill_id}) def _load_executor(self, path: Path, skill_id: str): spec importlib.util.spec_from_file_location( fskill_{skill_id}, path ) module importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def get(self, skill_id: str): return self.skills.get(skill_id)这套机制的好处是“加新技能不用改框架代码”在目录里新增一个文件夹里面放好定义和执行文件重启服务就完成技能注册。配合Git版本管理技能的每次变更都有记录出问题可以直接回滚。注册表还需要支持动态更新也就是所谓热加载。我实现了针对单技能目录的监听文件变更后自动重新加载该技能。但要注意热加载在进程级共享状态时需要特别小心。比如一个技能执行器内部维护了数据库连接池热加载会覆盖连接池变量导致旧连接泄漏。我的建议是热加载只用于开发环境生产环境务必走重新部署流程避免各类诡异问题。3.3 技能执行链路与错误处理技能执行链路在我的系统里是这么走的用户输入先进入主agent对话逻辑主逻辑调用技能路由模块选出候选技能路由返回最优技能ID后agent把用户意图和已填参数包装成一个执行请求交给技能执行器。执行器先做参数校验再调用具体函数或外部API拿到执行结果后把结果结构化回传给agent最终由agent组织成用户可读的自然语言回复。关键点在于执行结果的成功与失败信息都要结构化返回。我定义了一个统一的结果协议{ status: success, biz_code: MEETING_BOOKED, data: { room: A区-302, time: 2025-06-18 14:00-15:00 }, trace_id: a4f2... }失败时status变成failedbiz_code说明失败类型message携带可读错误信息比如“会议室已被占用”。agent拿到失败结果时不是直接甩给用户而是根据错误类型决定是否重试、是否换一个替代技能、还是直接告诉用户原因。错误处理里最容易被忽视的是超时控制。早期我没有给技能执行设超时一个外部API如果卡死整个agent会话就挂起用户等来的只有超时。后来所有技能统一加上超时阈值默认8秒超过就终止执行并返回failed同时把错误记录到日志。实在需要长时间运行的任务我改成了异步任务机制技能先返回“任务已提交”后续通过回调或轮询方式通知执行结果。4. 常见问题与排查技巧实录4.1 技能调用不生效的排查思路技能不生效是大家遇到最多的问题症状通常是“模型明明选对了技能函数也执行了但输出结果还是不对”。排查时我有一套固定操作顺序。第一确认路由是否真的选对了技能。我会先查看agent的会话日志看路由模块返回的技能ID是不是预期的。如果路由选错优先优化技能的description把典型用户表达加进去。第二确认参数是否有缺失。很多执行失败是模型没填齐required字段导致的这时候要看回调里的具体错误信息。第三确认执行结果格式是否被主逻辑正确解析。我踩过一次坑执行器返回的data字段是JSON字符串而不是对象主agent拿到后直接拼进了回复导致用户看到一串转义过的JSON字符。这类问题统一定义好输出格式就能解决。如果以上都没问题还是表现不对那就要检查模型的上下文里是否带了太多无关信息。技能执行结果返回后主agent可能被系统提示词要求“只使用知识库回答”这个指令压过了技能执行结果模型就会忽略工具返回内容。解决方法是检查并调整系统提示词明确“工具执行结果是最高优先级事实来源”而不是把工具输出和普通文本混为一谈。4.2 上下文过长与Token占用技能体系引入后一个新烦恼是Token消耗明显上升。技能数量多了以后即使每个技能的description只有几百字全部塞进系统提示词也会挤占上下文窗口。我试过在上下文里放了40个技能定义单是技能部分就占了3000多Token对话轮次一多必然触发长度上限。解决思路有两个方向一是控制单个技能的description字数我强制要求不超过50个字把“详细说明”挪到技能定义文件内部不给LLM看二是做技能筛选只把可能当前会话会用到的技能定义放进上下文。路由模块先做一个粗筛把与当前会话相关的5到8个技能完整定义注入上下文其他技能只保留ID和一句话摘要。这样既保证了路由信息的完整性也控制了Token消耗。另外一个容易忽略的Token陷阱是历史对话记录。很多agent会把所有历史对话都完整保留技能执行的大量中间结果也都在历史里这样上下文很容易爆掉。我的做法是技能执行结果不回传原文只保留结果摘要多轮历史对话超过一定长度后用LLM压缩成结构化摘要再继续。实测Token消耗能减少大约一半。4.3 技能冲突与优先级设计当技能库规模到几十个时冲突问题就出现了。最典型的是两个技能描述高度相似比如schedule.update和schedule.delete用户说“帮我把明天下午的会议改掉”既可以被理解成更新也可以被理解成取消路由经常选错。解决这类问题我在路由环节引入了优先级机制。每个技能多了一个priority字段表示“当与其他技能候选冲突时谁优先”。例如schedule.update的优先级高于schedule.delete因为用户说“改掉”通常意味着要换时间而不是取消。优先级由人工标注并在测试集上验证这个环节需要业务侧配合。我在项目里拉上业务同事一起掰扯了十几个这类冲突case才把优先级列表调好。还有一种冲突是技能内部子技能调用导致的递归死循环。A技能调用B技能B技能又调用A技能如果不设深度上限服务就直接卡死。我在技能执行器里增加了一个调用链深度计数器默认上限为3层超过即抛异常并返回友好错误信息。设计原则是技能不要互相引用层级超过两层就应该考虑把公共部分抽成工具而不是技能套技能。5. 实战体会与下一步规划技能库搭起来半年最深的体会是agent-skills的本质不是做出一堆技能而是建立一套能力持续沉淀的机制。今天加了一个技能明天就能被其他agent复用今天踩了一个错误处理的坑明天就能在体系层面避免。这种复利效应是纯写提示词完全感受不到的。下一步我计划做两件事。第一把技能评估工作做得更细现在每个技能上线前虽然有测试用例但缺少与真实用户反馈的结合。准备做一套技能成功率追踪从线上日志里抽取技能调用样本定期人工复核路由和执行的正确率形成技能健康度报表。第二考虑让agent具备“自我学习新技能”的能力当用户请求没有命中任何已有技能时记录为新技能候选由开发人员审核后补充到技能库。这本质上是在给agent做能力积累的闭环虽然还比较早期但方向我觉得是对的。最后再分享一个小技巧别把技能库设计得过于复杂刚开始三五条技能就够用先跑通链路再逐步扩充。很多人一上来规划几十个技能结果光是参数打架和维护描述就消耗了大量精力反而做不成事。agent的能力建设是个长线过程稳一点比什么都重要。