最近在搞 AI Agent 的时候我几乎天天会撞见“agent-skills”这个词。一开始我以为它只是某个开源仓库的名字后来才发现这其实是现在做智能体落地绕不开的一个工程范式。简单说就是把原本散落在 Prompt 里的能力描述、Function Calling 里的工具定义、以及各种业务逻辑统一抽象成一个个独立、可复用、可迁移的“技能”。这东西解决的核心问题特别现实模型再聪明也不能替你“动手干活”而技能就是那双手。如果你正在做 Agent 应用或者准备把大模型接入真实业务这篇文章应该能帮你少踩很多坑。我会从概念讲起拆清楚它和 Function Calling、插件到底有什么区别再给出一套可以直接参考的技能定义结构然后带你把一个“网页内容提取与摘要”技能从零跑通。最后整理一些我在实际项目里踩过的坑供你排查时参考。1. 从“能聊天”到“会干活”Agent Skills 到底解决什么问题1.1 先聊聊 Agent 的能力瓶颈大模型本身的边界很明显它训练时看到的资料是静态的不知道今天的实时新闻它没有权限访问你的内部系统不能直接读数据库、发工单、改配置文件它也没有“手”没法替你把一份 PPT 改成 PDF 再放到指定目录。早期大家怎么解决写提示词。把每一步操作要求写进 Prompt让模型“想象”自己完成了操作或者把几个工具函数塞进上下文里希望它自己想起来用。临时做个小演示确实可行但一旦场景变多问题就来了几十个工具定义堆在上下文里模型经常选错不同业务线的工具命名还会冲突一套代码在 A 项目里能用换到 B 项目又得重新写一遍适配层。所以本质的瓶颈不是模型不够聪明而是能力没有标准化。我试过在同一个 Agent 里挂 20 个不同的工具最早的实现方式是从一个超级大的工具列表里硬选结果经常把“创建订单”和“查询订单”搞混更别提参数格式不统一时候的连环报错。agent-skills 的思路就是把这些乱七八糟的工具封装成标准化的“技能包”每个技能包自带描述、参数定义、执行逻辑和测试用例让 Agent 只关心“该用哪个”不需要关心“怎么实现”。1.2 技能化拆解一个技能 一个最小可复用的能力单元什么叫最小可复用的能力单元举个例子你经常需要“把一段 URL 里的正文抓出来去掉广告和导航生成摘要”。这个能力可以拆成一个技能名字叫webpage_fetch_and_summarize它有明确的输入URL、摘要长度、明确的输出结构化摘要、确定的执行方式先抓取页面再调用大模型做摘要。这样做的好处是它把“做什么”和“怎么做”分开了。Agent 只需要在决策阶段看到技能的名字和描述用户说“帮我看看这篇新闻讲什么”模型就会自动匹配到这个技能然后传一个 URL 参数过去。至于这个技能内部是调用爬虫库还是用正则清洗页面模型不关心我们也不希望它关心。我用一个生活类比给你解释技能就像是墙上的标准插座。插座本身不关心插上去的是电风扇还是充电器它只提供一个统一的接口而 Agent 不需要知道电流怎么走只要知道“这里有个插座能通电”就够了。技能化把复杂的实现细节封装在插头后面模型要做的只是判断哪台电器适合这个插座。1.3 和 Function Calling、Plugin 的差异很多读者会问这不是早就有了吗Function Calling 不也干这事儿吗还真不一样。我做过一个对照整理方便你一眼看清楚差异对比维度Function CallingPlugin / 插件Agent Skills定位模型 API 层面的函数调用协议平台级扩展生态独立、可移植的能力单元依赖关系强依赖某个模型的 tool 格式依赖宿主平台运行时与具体模型和框架解耦封装粒度通常一个函数对应一种能力按产品功能聚合能力按“可复用任务”聚合完整流程可测试性需要 mock 模型才能测依赖平台环境较难单测每个技能独立测试迁移成本换模型要重新适配换平台基本作废换一套 Runner 就能带走这里不是要踩 Function Calling它在单模型应用里依然是高效方案。但 agent-skills 更强调“平台无关的标准化”。你可以先按一种格式写技能然后在一套执行环境里跑甚至多个 Agent 共享同一套技能库。对中大型项目来说收益相当明显维护一套技能所有 Agent 都能用。2. 场景拆解什么时候应该上 Agent Skills2.1 典型场景信息检索、文档处理、自动化操作我实际用下来下面几类场景特别适合技能化。第一类是“信息检索与加工”。比如抓取网页、查天气、查股票、搜内部知识库接着再做过滤、摘要、翻译。这种任务有明确的数据源和处理链路适合封装成技能。我自己的项目里最常用的是一个“网页正文清洗”技能把周边噪声都去掉只留下标题和正文再交给模型做后续处理效果比直接让模型读原始 HTML 好得多。第二类是“办公文档处理”。像生成 Excel、把 Markdown 转成 Word、批量改 PPT、用模板批量化生成周报。这类任务的特点是操作步骤稳定参数也稳定只要给模型一个清晰的参数 schema它就能把业务数据传进来由技能去调用底层的文档库完成操作。之前我在做项目汇报时用脚本把所有 Python 脚本里的类名提取出来然后生成一张汇总表格整个过程就是一个标准的文档处理技能。第三类是“代码工程自动化”。比如创建 Git 分支、跑测试、做代码风格检查、自动生成 commit message。相比让模型直接“给你一段命令”技能化更安全它在受限环境里执行可以严格控制权限而且每一步都有日志出问题能快速定位。我见过不少团队把“运行 pytest 并整理失败用例”做成一个技能开发体验很好至少不会再发生模型随口编一个测试结果的情况。2.2 技能封装 vs 模型原生能力边界在哪里并不是所有事情都需要做成技能。我的判断标准很简单如果模型在对话里直接干得又快又准就不要封装如果这个任务需要操作外部系统、需要确定性逻辑、需要权限控制就考虑封装成技能。举个例子让模型把一段文字改成更正式的商务措辞它原生就能做不需要技能。但让它“读取 /reports/2025/ 目录下所有 Excel按月汇总后生成一张柱状图”这涉及文件遍历、表格解析、图表生成任何一个环节出错都会导致结果不可用就必须用技能把确定性逻辑固定下来。还有一类情况是“需要重复执行的复杂流程”。一次两次可以手写 Prompt十次百次就必须固化。比如你每天都要从企业微信里导出的 CSV 里清洗数据然后写入数据库这种重复动作如果不技能化早晚有一天会因为某一步格式变化而全链路崩溃。技能化之后你可以把清洗规则固化在代码里配上单元测试数据格式一旦变化测试直接报警。2.3 不适用场景过度抽象的坑我也见过为了“技能化”而技能化的情况。有朋友把“计划一次旅行”拆成了 8 个技能每个技能负责一个城市、一个景点推荐结果 Agent 在决定用哪个技能时非常纠结上下文也被大量技能描述占满反而比直接用 Prompt 效果更差。这里有个严重问题技能数量膨胀。技能库超过一定规模后模型从长列表里选技能的准确率会明显下降。这时候最需要做“技能路由”或“动态技能召回”而不是继续堆技能。另外如果任务本身高度依赖用户个性化要求且每次执行路径都不太一样强行抽象成技能反而限制灵活性。我通常只把“边界清晰、输入输出明确、流程稳定”的任务沉淀为技能其他的继续留在对话逻辑里自由发挥。3. 技能长什么样定义结构、编排与模型交互机制3.1 一份技能清单的基本组成我们做技能化的第一步是定出一套统一的技能描述格式。我建议至少包含这几块内容name唯一标识、description给模型看的说明、parameters入参定义、executor执行器、permissions权限声明、tests测试用例。下面是一份简化版的 YAML 示例name: webpage_fetch_and_summarize description: - 抓取一个网页的正文内容并生成指定长度的中文摘要。 当用户提到“网页、文章、博客、新闻”等需要获取线上内容时使用。 如果用户只要求解释概念不需要使用本技能。 parameters: type: object properties: url: type: string description: 需要抓取的网页地址必须是完整 URL包含协议头。 max_length: type: integer description: 摘要最大字数默认 200范围 50 到 500。 required: - url executor: type: python entry: skill.py runtime: python3.11 permissions: network: true filesystem: false tests: - input: url: https://example.com/post/1 max_length: 100 expect_keys: - summary你可能已经发现了这份清单本质上是在同时写两种东西一种是给“人”看的规范另一种是给“模型”看的指令。模型通过description判断该不该用这个技能通过parameters理解该传什么参数执行器则负责把参数变成真实操作。3.2 描述怎么写模型才“看得懂”写技能描述是有点玄学的但也有一些规律。我自己的经验是描述里必须包含三要素触发场景用户说什么、提什么话题时应该调用这个技能不触发场景哪些情况不应该调用防止误匹配参数语义每个参数的具体含义、格式要求、默认值。比如上面对比“网页”和“解释概念”就是在给模型划边界。实际调试中我发现模型经常因为描述太宽泛而乱用技能。你写“获取网页信息”模型可能会在用户问“今天周几”这种完全无关问题时也试图调用它。于是我把描述改成“当用户提供或讨论了一个网页地址需要获取该地址的内容时使用”误调用率立刻降了很多。另外如果多个技能功能相似必须在描述里明确差异。我遇到过一个典型问题同时有“生成日报”和“汇总周报”两个技能模型经常混用。后来我在描述里分别写了“日报按天输出粒度到小时”“周报按周汇总粒度到天”效果立刻改善。模型和你一样需要清晰的区别信息。3.3 技能编排模型自主串联 vs 固定工作流技能不一定要一次调用一个复杂任务往往需要多个技能协作。我见过两种主流方式。第一种是“模型自主编排”Agent 根据用户目标自行决定调用的技能顺序。优点是灵活适合开放任务。缺点是不可控模型有概率漏掉步骤或者循环调用。我通常会限制单次任务的技能调用轮数超过阈值就主动向用户确认避免死循环烧 token。第二种是“工作流引擎编排”在代码里先定义好 DAG有向无环图技能按照固定顺序执行。比如“先抓取网页 - 再清洗正文 - 再生成摘要 - 最后写入文档”这个顺序是确定的模型不参与排程只负责在每个节点填充必要参数。这种方式稳定、可解释性强适合生产级业务。对我自己的中小型项目我推荐先做第二种等业务确实需要复杂开放任务时再逐步放开模型自主编排。重点在于技能本身不应该关心编排方式它只提供“可执行能力”编排是 Agent 层的事。4. 实操从零搭建一个 Agent Skills 技能库以网页摘要为例4.1 技术选型与目录结构如果你要自己动手我不建议一上来就选重型框架。最简配置可以是Python 3.11 FastAPI 作为技能注册中心 一个轻量 Runner 负责执行技能目录里的脚本。每个技能独立目录互不影响。我的目录结构长这样skills/ ├── webpage_fetch_and_summarize/ │ ├── skill.yaml │ ├── skill.py │ └── tests/ │ ├── test_params.py │ └── fixtures/ │ └── sample_page.html └── registry.jsonregistry.json用于在启动时扫描技能目录生成技能索引。Runner 读取每个技能的skill.yaml校验参数后执行skill.py将输出统一转为 JSON 返回给上层 Agent。这样每个技能就是一个独立的 Python 工程可以单独测试、单独升级。4.2 实现一个“网页正文提取与摘要”技能我先说服你别用正则硬抓网页正文那是无底洞。网站改个 class 名你的代码就废了。建议直接用现成的解析库。下面是一个简化版skill.py实现核心逻辑做了三件事请求网页、抽取正文、生成摘要。import json from urllib.parse import urlparse import requests from bs4 import BeautifulSoup def extract_main_content(html: str) - str: soup BeautifulSoup(html, html.parser) for tag in soup([script, style, nav, footer, aside]): tag.decompose() article soup.find(article) or soup.find(main) or soup.body if article is None: return paragraphs article.find_all(p) text \n.join(p.get_text(stripTrue) for p in paragraphs) return text.strip() def summarize(text: str, max_length: int) - str: # 真实环境可以换成调用大模型接口这里用截断模拟 if len(text) max_length: return text return text[:max_length] ... def run(params: dict) - dict: url params[url] max_length int(params.get(max_length, 200)) # 基本校验避免非法 URL 引发 SSRF 或异常请求 parsed urlparse(url) if parsed.scheme not in (http, https) or not parsed.netloc: raise ValueError(url 必须是完整的 http/https 地址) response requests.get(url, timeout10, headers{User-Agent: Mozilla/5.0}) response.raise_for_status() content extract_main_content(response.text) if not content: return {summary: , warning: 未能提取到正文内容} summary summarize(content, max_length) return {summary: summary, chars: len(content)}这里有几个细节容易踩坑第一必须校验协议头和域名不然 Agent 可能传入file:///etc/passwd或内网地址搞出安全漏洞第二一定要设置请求超时否则某个网站无响应会卡住整个技能第三User-Agent 伪装成浏览器很多网站会拦截默认的 Python requests。上面都是我在实际调试中吃过亏的地方。skill.yaml里的executor指向这个skill.pyRunner 在调用时把run(params)的返回值序列化为 JSON 返回给 Agent。上层 Agent 拿到summary后可以再决定要不要继续追问或者直接组织语言回复用户。4.3 技能注册、测试与效果评估写完技能不能直接上线我至少要跑三类测试。第一类是参数解析测试。构造一份合法的入参和几份非法入参确认run能正确校验并抛出明确异常。非法入参包括缺url、url写成ftp://、max_length传负数等。第二类是真实网页回归测试。我会提前保存几个典型页面的 HTML 到 fixtures 里比如一篇博客文章、一个资讯门户首页、一个视频页面分别跑一遍确认输出摘要是否合理。尤其是资讯门户首页往往包含大量导航链接如果正文抽取逻辑不完善摘要会变成一堆导航文本这种问题必须回归测试兜住。第三类是模型选择效果评估。这一步最容易被忽略。我会准备 20 条用户问题一半是应当调用该技能的一半是无关的然后观察 Agent 在加载技能后是否正确触发。统计“命中率”和“误调用率”。如果误调用率高优先看描述是否写清楚了不触发场景如果命中率低则要考虑描述里的关键词是否匹配用户表达习惯。我建议在技能库里为每个技能增加一个简单的调用日志记录每次触发的用户输入、技能名称、参数、耗时、返回状态。这样做的好处是上线后能持续观察模型的调用行为发现描述和参数设计的问题。日志就是技能的“体检报告”。5. 常见问题与排查技巧实录5.1 模型不调用技能或者调用了错误的技能这是我在社区里被问得最多的问题。排查顺序我一般是这样的先看技能描述是不是太宽泛或太窄。如果宽泛模型会把无关请求也匹配上如果太窄该触发时不触发。我自己的习惯是把用户最容易说出口的表达方式写进描述。比如用户大概率会说“这篇文章讲了啥”描述里就写“当用户提到文章内容时”而不是只写“网页转载”。再看是不是技能之间描述重叠。两个技能如果描述相似模型很容易选错。解决办法就是在描述中增加“和 XX 技能的区别”把适用边界点出来。最后看参数 schema 是不是太严格了。如果required里有模型难以推断的字段它可能会放弃调用。比如你要用户填一个内部生成的task_id模型根本不知道那它就不会用这个技能。遇到这种情况要么把字段改成可选要么在参数描述里写清楚如何获取。5.2 技能执行报错但模型开始“编结果”这是最隐蔽也最危险的坑。技能里抛异常了但上层模型没有拿到异常信息还在自顾自地补充回答最后给你一个看起来合理但完全没执行过的结果。我的解决办法是技能执行器层面强制规定——如果run函数抛出异常Runner 必须把异常信息包装成结构化错误返回并在返回内容里带上status: error字段。上层 Agent 的 Prompt 里再补一句“当工具返回 statuserror 时不要猜测直接告知用户执行失败并附上错误原因。”通过这种方式把“错误信息”和“正常结果”严格分开。我踩过一次很深的教训是网页下载超时技能返回了空字符串结果模型把空字符串当成“用户没有提供信息”又编了一段分析。从那以后我的所有技能在遇到异常时会明确抛出SkillExecutionError而不是静默返回空值。5.3 技能描述太多上下文不够用技能数量一多把所有技能描述塞进上下文会占用大量 token而且模型在长列表里选技能容易“走神”。我的做法是两层方案。第一层技能路由。在 Agent 外层加一个轻量检索层先把用户问题和技能描述都向量化用相似度召回 Top 5 个技能再把这 5 个技能的描述放入模型上下文。这样上下文里的技能数量可控模型选择准确率高很多。第二层技能描述压缩。每条技能描述控制在 150 字以内只保留触发条件、不触发条件、关键参数。不要写长篇大论的解释模型不需要完整理解业务背景它只需要知道“什么情况用”和“怎么传参”。我实测过一个包含 80 个技能的库压缩描述 向量召回后模型选择准确率从 62% 提升到 86%上下文 token 占用也降了三分之一。这个优化非常值得做。5.4 技能版本管理与灰度发布最后一个坑是“技能更新导致旧行为全乱了”。技能也是代码随便改就容易翻车。我现在强制要求每个技能目录里带一个CHANGELOG.md每次改动都记录。上线前先在测试环境跑一遍回归用例再灰度到 10% 的流量观察调用成功率后逐步放量。如果你有多个 Agent 共用同一个技能库一定要考虑兼容性问题。比如 API 参数变了老的 Agent 还在按旧 schema 传参会直接报错。我给技能版本号起名遵循语义化版本规则主版本升级表示参数或行为不兼容需要同步修改所有调用方次版本升级表示向后兼容的新能力补丁版本用于修复内部 bug。现象可能原因排查路径模型完全不用技能描述缺失关键词、技能未被路由召回检查召回逻辑补充触发词模型总是用错技能多个技能描述重叠在描述中区分边界增加反例技能报错后模型继续编错误信息未传入上层执行器统一返回 statuserror上下文很快被技能描述塞满技能数量过多、描述太长压缩描述 向量召回 Top K技能更新后旧 Agent 调用失败参数不兼容、缺少版本管理语义化版本先灰度再全量如果你现在正在设计自己的 Agent 技能体系我的建议是别贪多先挑 3 到 5 个高频、稳定、确定性强的任务做成技能跑通“定义 - 实现 - 测试 - 部署 - 观察日志”的完整闭环。我见过太多团队一上来就搞几十个技能结果光路由和调试就耗掉了大半时间。技能库这东西数量不是荣誉好用才是硬道理。真等基础架子稳了再慢慢往里填新能力你会发现整个 Agent 的可控性和复用性都上了一个台阶。