资讯动态

Agent Skills实战:从零构建AI代理技能体系与编排方法

发布时间:2026/10/22 4:50:37 来源:尧图企业网站定制
这两年做AI应用最头疼的问题不是模型不够聪明而是模型空有一身本事却缺一套趁手的工具。我自己在几个真实的业务项目里反复碰壁之后才慢慢摸到一条比较靠谱的路不要指望一个巨型System Prompt搞定所有事情而是给代理Agent配一套组织良好的技能Agent Skills。这篇文章就把我从零开始构建技能体系的完整过程包括设计思路、目录结构、代码模板、测试方法和排坑记录一次性讲清楚。适合正在做Agent应用开发的工程师也适合刚接触AI编程、想给助手加自定义能力的朋友。先说一个必须承认的事实模型本身的推理能力进步很快但它在很多具体任务上依然表现得很挣扎。让它写一段总结没问题让它从某个URL里提取正文并转成干净的Markdown它就容易胡说八道或者输出一大堆没用的导航文本。这时候你需要的不是换一个更强的模型而是给它一个明确的、可复用的技能——把不确定性极高的任务变成确定性极高的流程。这套思路的本质就是把Agent当成一个会看说明书、会调用工具的实习生。你给它一套写好的技能手册它按需翻阅、按步骤执行。下面我按自己实际落地的路径把这个体系一点点拆开讲。1. 先搞清楚Agent Skills到底是什么网上关于Agent Skills的说法很多但真正动手做的时候你会发现很多人把它和插件、函数调用、工具混为一谈。我自己的理解是Skill是一个“带了说明书的能力单元”它至少包含三个部分——描述文件、实现脚本、运行说明。Agent在运行过程中根据用户请求的意图动态决定是否加载某个Skill然后按照说明书去执行。如果你用过早年间的命令行工具会发现这思路非常熟每个命令都有自己的--help你不需要记住所有参数需要的时候翻一下就行。Agent Skills就是把这个逻辑搬到了大模型时代。它不是预先把所有能力塞进模型上下文而是按需挂载模型看到SKILL.md里的描述之后才知道该调用哪个脚本、按什么流程做、最后输出什么格式。1.1 为什么单个Agent会“卡壳”我刚开始做Agent的时候也走过弯路把所有指令、规则、示例都塞进一个巨大的系统提示词里。结果上下文很快被撑满模型开始注意力涣散任务一多就互相干扰。有一次我在同一个Promot里既让它处理数据清洗又让它生成图表结果它把数据清洗规则用在了生成标题上整个输出完全没法看。这个现象的本质是模型的能力上限由上下文长度决定但注意力的分配质量不由上下文长度决定。指令越多模型越容易在关键时刻抓错重点。Agent Skills把大段操作指南从上下文中拆出去只在需要的那一步读取对应的技能说明这样做从源头上解决了指令互相污染的问题。一句话单线程的Prompt是给简单任务用的Agent Skills是给组合任务用的。1.2 Skill与Tool的分工逻辑很多朋友会问我有Function Calling了为什么还要Skill这个问题问得很关键。Tool是原子操作比如搜索天气发送邮件执行SQL。它接收一组参数返回一个结果通常由模型根据函数定义自动决定是否调用。Skill则是复合能力它内部可以包含多个Tool、多段脚本、甚至几轮自我检查流程。一个周报生成Skill可能要调用数据查询Tool、文本总结模型、Markdown渲染脚本最后还要按公司格式模板输出。更直白的区分方式Tool是零件Skill是装配好的部件。零件放在那里模型知道怎么用部件则需要额外的说明书告诉模型在什么场景下组装、按什么顺序使用、输出结果要符合什么约束。Agent Skills的核心价值就在这里——把一组零件打包成一个开箱即用的整体大大降低模型自由发挥的空间。1.3 一套技能体系到底包含什么一个完整可用的Agent技能体系至少要涵盖三层第一层是技能库也就是一堆SKILL.md文件加上配套脚本按目录组织好放在Agent可以访问的位置。第二层是注册表不管你是用JSON清单、数据库记录还是简单的目录扫描Agent需要能知道当前环境下有哪些技能可选。第三层是运行环境脚本运行时的依赖怎么管理、权限怎么控制、日志怎么收集这些决定了技能能不能在生产环境稳定跑起来。我自己在实践中的经验是初期别追求庞大先保证有3到5个技能跑通全流程再逐步扩充。技能体系设计得好不好在规模小的时候看不出差距等技能上到几十上百个的时候你会发现描述质量、命名规范和版本管理才是真正的胜负手。2. 构建技能体系前先想清楚这四件事很多教程一上来就教你写代码但实际踩坑之后我发现代码反而是最简单的一环真正难的是设计。技能体系就像搭积木积木本身的尺寸标准如果不一致后面怎么搭都会塌。2.1 技能粒度多大算一个技能这个是我被问过最多的问题也是最难回答的问题。技能太大Agent一加载就要读取大量说明推理负担重效果还不好技能太小Agent频繁切换技能、跨技能传递数据又容易在中间环节丢失信息。我个人的判断标准是如果一个技能说明需要超过500行才能讲清楚说明它拆小了如果一个技能的一句话描述说不清它能干什么说明它拆大了。做网页正文转Markdown这个技能时我一开始把抓取、清洗、下载图片、生成摘要全放在一个技能里结果SKILL.md写了快两百行Agent加载后经常忽略后面的步骤。后来拆成内容清洗和图片处理两个独立技能各自配合起来反而稳定很多。另一个实用经验技能应该对应一个模型需要学习才能正确完成的任务。如果一段操作不需要模型理解、直接写死脚本就能完成那它应该是一个Tool或普通脚本而不是一个Skill。2.2 边界清晰一个技能只做一件事这条听起来像废话但执行起来非常反直觉。我见过很多同事写技能时喜欢在说明书里加各种顺便另外如果用户需要也可以之类的扩展职责。结果就是模型在调用时犹豫不决不知道当前这个技能到底主用在哪甚至把一个报表生成技能用在了文档翻译上。给技能划定边界写SKILL.md时就要把所有职责限制在第一屏里。描述区只写清楚这个技能解决什么问题、不解决什么问题、在什么触发条件下会被调用。你的不做什么写得越明确模型的误用概率越低。比如我的文件整理技能开头就会写清楚本技能仅用于文件批量重命名和归档不负责文件内容修改。2.3 描述即接口让Agent理解你的技能这是整套体系里最容易被低估的地方。很多人把精力花在脚本实现上SKILL.md草草写几句这个脚本能把HTML转成Markdown就完事。结果Agent根本不会主动调用或者调用了却传错参数。我需要反复强调SKILL.md的description字段就是接口接口写不好再强壮的程序也发挥不出来。写description有一个很实用的技巧第一句话直接写触发场景而不是功能概述。在用户提供一个URL并要求提取正文内容时使用比网页内容提取工具好用得多。前者让模型在做意图匹配的瞬间就能锁定技能后者则让它还要再想一下这个功能到底适不适合当前场景。描述中还要明确说明输入参数类型和输出格式最好直接给一个小例子。因为目前的Agent系统多数不支持复杂的函数签名校验技能之间传递数据全靠模型阅读理解。一个小例子能帮模型极大降低格式猜测成本。2.4 目录与命名规范技能多了之后目录命名就是检索效率的关键。我见过有人用中文拼音、功能缩写、项目代号做目录名的到最后连自己都分不清。推荐直接用技能名-主语言-版本号的规则比如web_cleaner_python_v2。在SKILL.md中用完整可读的名称作为name字段目录名与name字段保持一致。如果团队协作我建议再维护一个技能索引文件记录技能名、版本、负责人、依赖关系、更新日期。这不是形式主义当你面临某个技能脚本突然不工作了的排查时索引能帮你快速定位上一次改动是谁做的、动了哪些文件。3. 动手实操搭建第一个技能拿一个真实场景练手做一个网页正文提取与清洗技能。这个技能在我日常工作中使用率极高——抓竞品资料、整理资讯、喂给RAG系统都要用到。我会把从文件结构到注册调用的完整过程放出来。3.1 技能目录的最小结构一个最小可用的技能目录大概长这样skills/ └── web_content_cleaner/ ├── SKILL.md ├── scripts/ │ └── clean_page.py └── assets/ └── stopwords.txtSKILL.md是给模型看的说明书scripts目录放可执行脚本assets放脚本依赖的静态资源。我的习惯是任何非代码的资源文件都放assets避免和脚本文件混在一起。这样打包、迁移技能时也能快速定位哪些是执行文件、哪些是数据文件。如果你用的是某个AI编程框架或自定义Agent平台技能目录的读取方式可能有自己的约定。不过一般都会兼容这种描述文件脚本的模式差异只在于注册方式。先按这个结构把技能写明白再适配到具体平台是成本最低的路径。3.2 编写SKILL.md的正确姿势一份好用的SKILL.md最好控制在60到100行左右。太短说明信息不足模型猜不到执行细节太长模型加载慢还会干扰关键信息提取。它的核心结构非常固定YAML头部、触发描述、使用方法、注意事项、示例调用。--- name: web_content_cleaner description: 当用户提供一个网页URL并要求提取正文、去除广告/导航等无用信息、将内容转换为Markdown格式时使用。不适用于PDF和图片内容。 --- # 网页正文提取与清洗 ## 使用方法 1. 使用脚本 scripts/clean_page.py 处理HTML内容。 2. 输入参数通过STDIN传递JSON格式为 {url: ...} 或 {html: ...}。 3. 脚本输出JSON格式为 {title: ..., content_markdown: ...}。 4. 若需要下载图片另行调用 image_downloader 技能。 ## 注意事项 - 脚本不处理动态渲染页面必要时先用浏览器抓取渲染后的HTML再传入。 - 网页编码混乱时优先检查HTML中的charset声明。 - 输出内容保留原文段落层级不要擅自改写正文。这段文档的写法是刻意为之的。开头就把触发条件和不适用范围交代清楚减少模型误用使用方法用编号步骤写模型执行时更容易按顺序走示例调用和注意事项放在后半部分保证主体步骤不被干扰。我强烈建议你按照这个模板先套一套等熟悉了再按需调整。3.3 脚本层的输入输出协议脚本是实现层面最需要标准化的一环。无论你用什么语言我建议统一采用STDIN收JSON、STDOUT出JSON、STDERR记日志的方式。这样模型侧不需要管理临时文件路径也不容易踩编码坑。# scripts/clean_page.py import json import sys from html.parser import HTMLParser class TextExtractor(HTMLParser): def __init__(self): super().__init__() self.skip_tags {script, style, nav, footer, aside} self.in_skip False self.parts [] def handle_starttag(self, tag, attrs): if tag in self.skip_tags: self.in_skip True if tag in (p, div, br, li, h1, h2, h3, h4): self.parts.append(\n) def handle_endtag(self, tag): if tag in self.skip_tags: self.in_skip False def handle_data(self, data): if not self.in_skip: self.parts.append(data) def clean_html(html): parser TextExtractor() parser.feed(html) lines [line.strip() for line in .join(parser.parts).splitlines()] lines [line for line in lines if line] return \n\n.join(lines) if __name__ __main__: payload json.load(sys.stdin) html payload.get(html, ) if not html and payload.get(url): # 这里留一个requests抓取入口生产环境请加超时与重试 try: import requests resp requests.get(payload[url], timeout10) html resp.text except Exception as exc: print(json.dumps({error: str(exc)}), filesys.stderr) sys.exit(1) result {content_markdown: clean_html(html)} print(json.dumps(result, ensure_asciiFalse))注意几点输出JSON永远使用ensure_asciiFalse避免中文被转成\uXXXX不然模型读起来非常痛苦后续处理也容易出乱码。错误信息给到STDERR并且返回非零退出码便于Agent判断调用失败。生产环境里抓取逻辑一定要加超时和重试机制我之前有个技能因为没加超时一个无法访问的URL卡了90秒直接把Agent的响应时间拖垮了。3.4 技能注册与触发测试技能文件写好了脚本也跑通了接下来就是让Agent能看到、能调用。最简单的注册方式是写一个技能清单文件在Agent启动时加载[ { name: web_content_cleaner, path: skills/web_content_cleaner, version: 1.2.0, enabled: true } ]启动加载时系统会把每个技能的name和description字段做一个轻量索引放进Agent系统消息的可用技能区。模型看到web_content_cleaner在用户提供URL并要求提取正文时使用就可能在合适的时候请求读取完整SKILL.md并调用脚本。触发测试是整个流程的关键。我通常会准备一批典型输入反复检验同一个技能在不同说法下的触发稳定性。比如把这个网页整理成笔记帮我抓一下这个链接的内容把这个页面的正文存到文件里——这三句话说的是同一件事但措辞差异很大只有description写得足够贴近用户真实语言技能才会被稳定触发。这一步靠拍脑袋是测不出来的必须积累足够的样本反复测。4. 避坑指南技能调用不生效的排查清单技能体系跑久了你会发现大部分问题都集中在几个固定的环节里。我把高频问题和排查思路整理成了一份清单每次技能出问题就按这个顺序查基本都能快速定位。4.1 描述问题为什么就是不触发不触发有很多种原因。我的排查顺序是先看description有没有写不适用范围再看第一句话是否包含了用户最常用到的动词和名词。比如用户通常说抓取而不是提取你的description里如果只写了提取那模型匹配不到也很正常。另一个隐蔽的坑是多个技能的description互相重叠。我有一次做了网页摘要和网页正文提取两个技能description里都没写清楚区别结果模型随机二选一内部测试有一半的命中率。后来我在网页摘要的前面加了仅生成不超过300字的总结不输出全文在网页正文提取里加了输出完整正文不进行总结两个技能从此各司其职。4.2 输出问题模型解析技能结果失败技能脚本输出很规范但模型就是看不懂——这种情况一般出在输出结构过于复杂。比如输出JSON嵌了三层嵌套字段模型在往回复里转述时容易漏字段或改字段名。解决方式是在SKILL.md里给出一个输出转换示意图告诉模型最终面向用户时应该如何组织回复直接写清楚映射关系。我之前在数据统计技能里加了一行{total_count: 128} → 向用户输出共找到128条记录。就这一句话回复质量提升非常明显。模型不是不懂JSON而是不知道你在当前场景下希望它用什么形式呈现结果。4.3 环境问题脚本就是跑不起来这个问题在本地调试时很少出现一部署就集中爆发。最常见的两个原因一是脚本依赖的环境变量没设置二是Python版本不兼容。我有次排查一个技能报错日志显示某个第三方库的API变了再看SKILL.md里写的运行环境压根没标注Python版本要求。后来所有技能SKILL.md里都会多一行# 运行环境Python 3.10 或 Node 18以及必要的依赖安装命令。更稳妥的做法是给每个技能配独立的依赖声明文件部署时用虚拟环境隔离。虽然笨但能让你在升级主程序环境时不会被某个陈年技能拖累。如果条件允许把技能的运行wrapper化统一用沙箱执行对权限控制和资源限制都更友好。5. 把技能体系推向团队版本管理与共享自己做技能和一个团队维护几十个技能完全是两个世界。单人场景技能目录乱七八糟也能硬扛团队场景没有规范和工具支持技能库很快会腐烂成一堆没人敢动的脚本。这一章谈谈我在这方面趟出来的经验。5.1 技能仓库的组织方式我推荐把技能当成代码来管理独立建仓或者单独建目录与主应用代码分离。每个技能一个子目录里面挂自己的版本号和更新日志。主应用通过索引文件或子模块的方式引用技能版本而不是直接把代码拷来拷去。版本号建议直接套语义化版本规则。主版本号对应破坏性变更比如输入协议改了、输出结构变了次版本号对应兼容性功能新增补丁号对应修复。这套规则在多人协作时价值极大特别是有同事问你这个技能还能不能安全升级的时候你只需要看一眼版本号就能回答。5.2 跨项目复用的兼容策略跨项目复用的最大障碍是技能对特定环境的隐性依赖。比如某个技能内部写死了数据目录路径换一个项目就找不到文件。我的经验是把所有环境相关配置挪到技能外部注入SKILL.md只保留逻辑运行时通过环境变量或配置文件传入路径、密钥、URL等。还有些技能依赖主应用的能力比如需要调用某个内部接口。这类技能我会在SKILL.md的依赖部分明确标注接口名和版本约束避免换一个环境后静默地访问到不存在的接口。把依赖关系显式化是技能能否被大家放心复用的关键分水岭。5.3 技能评审像Review代码一样Review技能技能评审非常重要。一个写得不好的技能不仅仅不工作还会在Agent需要执行任务时被错误加载进而污染整个对话的后续输出。我们团队的评审清单包括这几项description触发条件是否明确SKILL.md是否存在超过两层以上的嵌套指令脚本输入输出是否符合统一JSON协议错误处理是否完备依赖是否声明清楚有没有做边界测试。每项打个勾或说明问题评审通过后才允许合并进正式技能库。这套流程跑起来后技能乱写导致Agent行为失控的事故明显少了很多。6. 进阶玩法从技能库到技能编排技能体系做到后面单个技能已经不是重点重点是多个技能如何配合、Agent如何知道自己该用哪些技能、流程中间状态如何传递。这是最考验体系设计能力的部分。6.1 技能链与主技能所谓技能链就是用一个主技能来串联多个子技能。比如我做一个市场调研报告生成技能它的流程不是一步到位而是先调用网页内容清洗抓取竞品信息再调用结构化数据提取提炼关键指标最后调用Markdown排版生成报告。主技能本身不处理具体内容它更像是一个指挥者负责规划步骤、传递中间结果、决定各子技能的调用顺序。写主技能SKILL.md时有几个要点要说清楚整个流程的步骤数每步使用哪个子技能每步输入从哪来、输出去哪哪一步可以失败不影响整体什么情况下应该中止流程并请用户补充信息。经过这种编排Agent从单点能力进化成组合能力这是技能体系相对插件体系最有优势的地方。6.2 给技能加上可观测性技能跑没跑、跑得好不好不能总靠感觉。我习惯在技能调用时记录三类日志调用日志、结果日志、异常日志。调用日志记录哪个技能、哪个步骤、处理的输入摘要、耗时多少结果日志记录输出是否成功、输出体量多大异常日志记录错误类型和可能的原因。有了这些日志就可以做简单的质量分析。比如某个技能调用成功率一直低于80%那大概率是description触发判断有问题或者脚本对某些输入格式没处理好。再比如某个技能平均耗时特别高就要考虑是不是抓取逻辑里忘了加页面大小限制。可观测性不是为了好看它是你持续迭代技能体系的地图。6.3 基于反馈的技能自优化更高阶的玩法是让Agent自己反馈技能的使用体验。在技能调用链最后加一个使用体验步骤让模型简要回答这次执行是否顺利哪个环节最不确定有没有参数不知道该填什么。把这些反馈聚合成结构化数据定期汇总分析。我这套机制跑了几个星期后发现很多我觉得写得挺清楚的技能在真实使用中都会暴露出模型理解偏差。比如某技能description里有一个专业术语模型总是误解其含义后来我把术语换成了日常说法触发准确率立刻提升。这种来自真实反馈的优化远比坐在电脑前反复调整措辞高效。最后总结一句个人体会构建Agent Skills体系这件事看起来是在写代码本质上是在做知识工程和流程设计。一份好的SKILL.md胜过上千行复杂代码一个精准的description能让技能调用的准确率翻倍。与其花大力气训练模型不如先把手头这套技能体系打磨清楚。这可能是投入产出比最高的优化方向。希望这篇内容能帮你少走一些我踩过的弯路。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑