ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

从零手写AI Agent Skill:把经验固化成可复用的能力包

从零手写AI Agent Skill:把经验固化成可复用的能力包 开头部分先聊个真实场景我最初接触AI Agent时最头疼的不是模型不够聪明而是每次让它做专业任务它都要重新“摸着石头过河”。让它审一遍代码它只会泛泛说“注意空指针”让它整理论文它按自己的理解自由发挥。后来我才弄明白真正管用的做法是把这些高频、重复、需要专业经验的任务固化成一个个可复用的“技能包”也就是现在AI圈里特别火的Skill。这篇博文就从零讲起聊聊Skill到底是什么、跟Agent有什么区别以及最重要的是——你自己怎么动手写一个能用的Skill而不是停留在收藏一堆别人的Skill却不知道怎么改。我默认你至少用过Codex、Claude Code、OpenCode、Spring AI这类工具中的某一种听说过“Skill”这个词但没实际写过。这个过程门槛不高关键是你得理解Skill的设计思路它不是一个普通提示词也不是一个乱放的脚本文件夹它是一套“让AI在正确场景下按你的方法做完一件事”的完整方案。下面我会用最近项目中真实用过的例子来说明读完你就能照着做一个自己的Skill。1. 先搞清楚Skill到底是什么它和Agent的关系1.1 Skill不是提示词也不是插件它是一个可执行的“能力包”先说结论Skill是一组按约定组织的文件里面描述了某个任务的处理方法、执行步骤、所需脚本和判定标准。它比提示词多了可执行性比插件多了流程描述。一个完整的Skill通常包含三块内容描述文件告诉Agent“我是什么、解决什么问题、什么时候该调用我”这是Agent决定是否启用Skill的依据名字一般叫SKILL.md或者skill.yaml。指令主体拆解任务的详细步骤告诉Agent先做什么、后做什么、每步做到什么程度才算合格。资源脚本负责具体动作的代码、模板、数据比如一个Python脚本、一份JSON模板、一组参考示例。这三块合在一起Agent拿到用户请求后会先扫描所有已安装的Skill根据描述文件判断哪个能用然后读取指令主体按步骤执行过程中按需调用脚本。所以你可以把Skill理解成“操作手册工具箱检查清单”的合体。它解决的核心问题是让AI不只靠模型本能干活而是靠你沉淀下来的经验干活。传统软件工程里也有Skill这个词比如Cadence Allegro的SKILL语言、嘉立创EDA里的Skill插件它们本质上是给专业软件写自动化脚本。这个概念被AI Agent圈借过来之后含义从“脚本语言”扩展成了“可复用的能力单元”。理解这一点很重要Skill的本质不是某个平台的功能而是一种思维方式——把复杂任务分解成标准动作让机器替你重复执行。1.2 Skill和Agent到底是什么关系很多新手总在问“Skill和Agent的区别”我打个比方你就懂了。Agent像是一个入职的员工他聪明、有干劲、什么都懂一点Skill像是这个员工手里的工作手册和专用工具。员工没有手册也能干活但有了手册他能按你的标准干活不出幺蛾子。Agent负责“理解意图、规划动作、调用资源、汇总结果”它是执行的载体Skill负责“定义方法、约束动作、提供工具、判断结果”它是经验的载体。一个Agent可以挂十几个Skill对应它能胜任的各种专业任务同一个Skill也可以被多个Agent共用。所以Agent和Skill不是竞争关系而是配合关系。在设计上Skill做的是“把不确定性变成确定性”。AI模型天然有随机性同一个问题问十次可能得到十种答案但如果你在Skill里写清楚“第一步读取文件第二步提取所有未捕获异常第三步按严重程度排序”把随机性锁在固定的流程里输出就会稳定得多。这也是为什么项目复杂度越高越需要把关键步骤拆成Skill——你在用流程对抗模型的自由发挥。1.3 为什么现在各大平台都在推Skill从Codex到OpenCode从Spring AI到各种Agent框架几乎都在支持Skill机制。表面原因是大家发现纯提示词工程不够用了深层原因是Agent的应用正在从“聊天问答”走向“自动化生产”。聊天场景下模型自由发挥也能让用户满意生产场景下错误的格式、缺失的步骤、不严谨的验证都会造成实际损失。Skill提供了一条标准化的路让Agent在特定领域内表现得像一个懂行的老手而不是一个随机的“通才”。还有一个现实原因知识复用。一个团队里最有价值的不是模型而是那套跑通了的业务方法。把方法写成Skill团队成员甚至跨团队都能复用后面迭代时只需改Skill文件所有调用它的Agent都会跟着升级。这种资产化、可维护的能力沉淀方式才是Skill真正受欢迎的原因。2. 动手前需要确定的三个设计问题2.1 你的使用场景和用户到底是谁很多人一上来就写Skill结果写完发现Agent根本不调用。我告诉你九成原因是场景没定义清楚。写Skill之前先回答两个问题第一这个Skill是为哪种任务准备的是代码审查、论文润色、数据分析还是文档生成第二用的人是谁是零基础的运营同事还是写过三年代码的工程师。同一个任务给不同的人用Skill里写的东西完全不一样。举例来说同样是“代码审查”Skill给初级开发者用的你应该写清楚“检查空指针、未关闭连接、缺少异常处理”给资深工程师用的反而要写“关注架构耦合、事务边界、并发安全”。Skill的指令不是越详细越好而是要跟目标用户的水平匹配。Level不够的人需要步骤化教学水平高的人更需要判断框架。我建议你把场景描述写进Skill描述文件里哪怕多写几句。频率更高的做法是明确指出“当用户提到代码审查、Review Pull Request、检查代码质量时应优先使用本Skill”。这能大幅提高Agent匹配Skill的准确率后面我会给出具体字段写法。2.2 Skill的边界只管一件事还是管一串流程第二个容易翻车的点是边界设计。一个常见的误解是“我要把整个研发流程塞进一个Skill里”结果写出来的指令又长又乱Agent执行到第三步就丢三落四。更好的做法是遵循单一职责原则一个Skill只封装一条完整的业务动作线。什么叫一条完整的动作线比如“审查代码”就是一条读取代码、分析问题、生成报告三步结束。而“修复代码问题”是另一条理解问题、设计修改方案、改代码、跑测试、确认通过又是另一条。这两件事别混在一个Skill里否则Agent在审查阶段就可能直接动手改代码你会很难控制。当然你可以做一个编排型的Skill它把多个子Skill串起来。比如“处理一个Bug”这个高层Skill内部按顺序调用“复现Bug”“定位问题”“修复代码”“补充测试”四个子Skill。这种组合设计的好处是每个子Skill可以单独测试、单独复用编排层只负责顺序和决策。理解了这种分层思维你的Skill就能从小工具长成一套体系。2.3 输入输出契约怎么定Skill和函数一样必须有明确的“接口约定”。输入是什么输出是什么中途失败怎么办这些要在设计阶段定清楚。否则Agent调用Skill的时候脚本接收不到正确参数或者生成的结果格式不统一后面处理就全是坑。输入方面我习惯在SKILL.md里定义一个“Inputs”小节写清楚这个Skill需要哪几个关键字段。例如“代码审查Skill”需要的输入是“代码路径必填”“审查重点可选默认全部”“输出格式可选默认markdown”。输出方面必须定义交付物的格式是输出一份报告、修改一批文件、还是返回一个JSON结构化结果。格式定了Agent才能把你的Skill输出当作下一步输入继续处理。另外边界条件和失败处理一定要写。比如代码审查时如果代码路径不存在应该直接返回错误信息而不是继续如果代码文件超过一定行数应该分段处理而不是一次性读入。这些“异常路径”看着不起眼却决定了Skill在真实环境里能不能扛住。我见过太多Skill正常流程跑通了一遇到边界情况就崩掉根源就是设计阶段没考虑失败处理。3. 从0到1编写Skill的完整实操过程3.1 目录结构先搭一个干净的文件骨架我以当前最主流的Skill格式为例目录结构大致如下。不同平台的字段名略有差异但骨架基本通用。my-skill/ ├── SKILL.md # 描述文件Agent首先读取这里 ├── instructions.md # 指令主体详细动作步骤 ├── scripts/ │ ├── analyze.py # 具体执行脚本 │ └── utils.py # 辅助函数 └── examples/ ├── example_input.json └── example_output.md这个结构不是随便定的。SKILL.md放在根目录是因为Agent扫描Skill时默认会找这个名字scripts放脚本是因为执行逻辑要和描述逻辑分离examples放示例是为了让Agent在不确定时能参考输入输出样例。目录名和文件名的命名尽量用英文小写加短横线避免空格和中文跨平台兼容性会好很多。我在实际项目中还习惯加一个README.md写这个Skill的维护人、版本号、变更记录。虽然AI不一定读它但下次你三个月后回来看这个文件夹不用猜当时为什么写那段代码。维护文档是写给未来的自己看的别省。3.2 写SKILL.md让Agent在正确的时候想起你SKILL.md是整套Skill的“入口”Agent会优先解析它判断当前任务是否需要调用你。因此这个文件里面最重要的不是写多详细而是把“触发条件”写准。我见过太多人把SKILL.md写成了一篇论文Agent解析半天也不知道何时该用结果就是彻底不调用。一个结构清晰的SKILL.md长这样--- name: code-reviewer description: 用于对指定代码目录执行系统化代码审查发现潜在缺陷、性能风险和可维护性问题并输出结构化审查报告。 when_to_use: 当用户请求代码审查、代码走查、Review Pull Request、检查代码质量或要求找出代码中潜在Bug时优先使用本Skill。 version: 1.0.0 ---name字段是Skill的唯一标识别起重复名description是对能力的一句话概括when_to_use是触发条件写得越具体匹配率越高。有些平台还支持keywords字段你可以把同义词都列进去比如“代码评审”“Code Review”“质量检查”提高召回率。还有一点容易被忽略version字段。Skill是会持续迭代的你在本地调试的时候改过好几版不给版本号的话你根本不知道自己用的是哪版。每次改动功能、修复Bug我都建议顺手升一个版本号并在末尾加一行备注例如“1.0.1修正了空目录导致崩溃的问题”。3.3 写instruction主体步骤化、可执行、可校验指令主体是Skill的灵魂。读取完SKILL.md之后Agent会加载指令文件按里面的步骤逐步执行。写指令的核心原则有三个步骤化、可执行、可校验。步骤化指的是把任务拆成一二三四五步每一步都有清晰动作可执行指的是每一步的指令要具体到能从文件系统中拿数据、能调用某个脚本、能对结果做判断可校验指的是每一步都要有“完成标准”让Agent知道自己做完了没有。举个反例。很多新手写的是“审查代码并找出所有潜在问题。”这句话毫无约束力Agent可能看两眼就输出了“代码总体质量良好”。正确的写法是1. 读取目标目录下所有源代码文件忽略 node_modules、dist 等生成目录。 2. 对每个文件按以下维度检查 - 空指针与未初始化变量 - 资源未关闭文件、网络连接、数据库连接 - SQL注入和路径穿越等安全风险 - 明显可优化的循环和重复查询 3. 将发现的问题按严重程度分级Critical / Warning / Suggest。 4. 输出审查报告格式见 examples/example_output.md。这种写法Agent每一步都知道要干什么也能自我检查有没有漏掉。instructions.md里还可以写“禁止行为”比如“不要修改任何源代码文件”“不要在报告中堆砌没有确认的问题”这些约束能帮Agent守好边界。你可以在指令末尾加一段“完成判定”例如“报告生成完毕前必须确认每个文件都被检查过若未检查请在报告开头声明”。3.4 把“经验”变成自动化脚本是怎么参与进来的纯靠文字指令Skill也有能力上限要想真正有杀伤力必须让脚本参与。最常用的模式是Agent读取文件列表把数据喂给Python脚本脚本跑完输出结构化结果Agent再基于结果生成最终反馈。这种“模型负责推理、脚本负责计算”的分工能同时发挥模型的理解能力和代码的精确能力。以代码审查Skill为例我写了一个Python脚本作用是统计一个Python文件里所有try...except块找出吞掉异常的地方import ast import sys from pathlib import Path def find_empty_except_block(path: Path): tree ast.parse(path.read_text(encodingutf-8)) issues [] for node in ast.walk(tree): if isinstance(node, ast.ExceptHandler): has_raise False for child in ast.walk(node): if isinstance(child, ast.Raise): has_raise True break if not has_raise: issues.append({ file: str(path), line: node.lineno, type: except_swallow, suggestion: 此处异常被静默吞掉建议至少使用 logging.exception 记录上下文 }) return issues if __name__ __main__: for p in sys.argv[1:]: issues find_empty_except_block(Path(p)) for issue in issues: print(issue)Agent的执行路径就会变成读完代码发现某个文件有except块调用脚本拿到返回的JSON或文本识别问题信息再组织语言写进报告。脚本不需要多复杂但一定要稳定。建议脚本统一从命令行参数接收输入、把结果打印到stdout避免Agent猜“脚本结果存在哪个文件里”。约定越简单Agent越不容易出错。3.5 测试与调试怎么确认Skill真的生效Skill写完不能直接用你得测。我的习惯是先准备一个最小用例集包含一个正常用例、一个边界用例、一个错误用例。正常用例验证主流程跑通边界用例验证极限情况错误用例验证失败处理符合预期。比如代码审查Skill正常用例给一个带明显Bug的Python文件边界用例给一个空目录错误用例给一个不存在的路径。有了用例集你可以在聊天环境下直接调用。观察三个关键点第一Agent有没有自动选中这个Skill第二它执行每一步时有没有偏离指令第三最终输出格式是不是你期望的。如果Agent完全不调用问题大概率出在SKILL.md的when_to_use写得不够明白如果调用了但执行乱问题在instructions.md的步骤不够细如果输出格式不对那就要检查examples里的样例是不是不够典型。调试过程中我的救命技巧是开“详细模式”。大多数Agent框架都支持打印Skill的加载过程和调用记录你会看到Agent解析了哪个文件、跳过了哪个文件夹、执行了哪条命令。看到这个日志比看最终结果有用一百倍。记住一个原则Skill调试不是在调代码是在调“AI对你的Skill描述的理解”所以日志里Agent的措辞比代码行为更值得关注。4. 一份可参考的完整Skill模版以代码审查为例4.1 场景定义和目录搭建为了让上面的理论落地我完整贴一个我实际用过的Skill模版。场景是“对指定代码目录做快速审查并输出结构化报告”。这个Skill我天天用体量不大但系统性很完整。先在项目里建目录mkdir -p my-code-review-skill/{scripts,examples}然后逐个创建文件。这里我特别强调Skill文件是给你自己和AI协作的所以不要追求一次写完先搭骨架、再填充细节、最后反复调。新手容易一次性憋一个大而全的文件憋到最后反而不愿改了。我的建议是先放一个最简版跑起来再迭代。4.2 SKILL.md和instructions.md的具体内容SKILL.md我这样写--- name: code-reviewer description: 系统化审查代码定位缺陷、性能隐患与可维护性问题输出分级报告。 when_to_use: 用户要求审查代码质量、检查代码Bug、Review Pull Request、进行代码走查时使用。 version: 1.0.1 --- # 工作流程概述 1. 定位目标代码目录或文件。 2. 使用 scripts/analyze.py 辅助扫描。 3. 汇总问题并分级。 4. 输出 Markdown 格式审查报告。SKILL.md简洁一点详细动作放instructions.md# 执行步骤 ## 第1步确认审查范围 - 若用户给出具体文件直接审查该文件。 - 若给出目录递归扫描所有 .py、.js、.ts 文件跳过 node_modules、venv、.git 等目录。 - 若范围不确定先向用户确认不要擅自猜测。 ## 第2步逐文件分析 对每个文件执行 - 肉眼检查逻辑重复代码、过长函数、明显死分支。 - 调用脚本辅助检查python scripts/analyze.py file_path。 - 记录每个问题所在行号和原因。 ## 第3步问题分级 - Critical可导致崩溃、安全漏洞或数据丢失。 - Warning可能引发错误或性能严重下降。 - Suggest改进建议不影响当前功能。 ## 第4步生成报告 报告格式参考 examples/example_output.md。 报告必须包含审查范围、问题列表、总体评价。 禁止修改用户的任何源代码文件。这里每个步骤都有动作对象和完成标准Agent才不会跑偏。特别注意“禁止修改代码”这一条代码审查Skill最怕Agent手滑改了代码你到时候哭都来不及。4.3 脚本和示例文件怎么配合scripts/analyze.py就是我上面贴的那个Python脚本你直接复制保存就能用。实际应用中我还会再加一个utils.py用来统一处理文件路径和输出格式防止路径里有空格时脚本崩溃。脚本的核心输出是结构化JSON或文本行Agent拿到之后自己拼报告。这样分工的好处是脚本逻辑越简单出bug的可能越小Agent负责阅读理解弥补脚本处理不了模糊语义的短板。examples/example_output.md这样写# 代码审查报告auth_service.py ## 审查范围 - 文件src/auth_service.py共186行 ## 问题列表 ### Critical - 第42行用户输入直接拼接进SQL存在注入风险建议改用参数化查询。 ### Warning - 第76行每次请求都新建数据库连接高并发下连接池被打满建议复用连接。 ### Suggest - 第15行LOGIN_ATTEMPT_LIMIT 可以提取到配置文件中便于环境差异化。 ## 总体评价 主流程思路清晰但安全性和连接管理需要重点优化。示例不是摆设Agent遇到“报告长什么样”这种模糊问题时会直接模仿示例。所以示例的质量直接决定输出质量。你把示例写得越规范Agent生成的结果就越规范如果你写得很随意AI也会很随意。4.4 让Skill“注册”进平台写完文件后需要把Skill放到平台指定的目录里。不同平台的路径不一样大体思路都是有一个skills文件夹把整个my-code-review-skill目录放进去重启或刷新后Agent就能识别。如果你用的是Codex、OpenCode这类工具命令行里往往有skill list或skill add之类的命令照着执行即可。注册完成后问Agent一句“你能做代码审查吗”如果它能准确回答说明SKILL.md的描述已经被正确解析了。需要说明的是不同平台对Skill的约定字段会有差异比如有些平台用yaml格式而不是markdown头。这个不要慌逻辑是一样的只是包装壳不同。你先学会写一套标准的迁移到其他平台只是改改字段名而已。5. 常见问题与排查技巧实录5.1 为什么Agent就是不调用我的Skill这是所有新手第一个遇到的坑。排查顺序我建议按下面这张表来排查点检查方法解决办法目录放错位置确认Skill被放进了平台指定的skills目录查阅平台文档找到正确路径描述文件名字不对确认根目录存在SKILL.md严格按平台约定的文件名命名触发条件不明确看description和when_to_use是否覆盖了用户提问的关键词补全同义词和典型说法已存在同名Skill搜索是否有一个Skill抢占了同一场景改名或删除旧的平台没有启用新Skill重启会话或手动执行刷新命令按平台方式刷新我印象最深的一次是某个Skill放了三天Agent都没调用。后来查日志发现它的when_to_use写成了英文“code reviewcode inspection”而我测试时问的是中文“帮我审查一下代码”。补上中文触发词之后立刻生效。这就是触发条件没覆盖真实用户说话方式的典型教训。5.2 Skill里的脚本报错了怎么办脚本报错分两类。第一类是语法错误或依赖缺失这种最简单本地先跑通脚本再让Agent调用。第二类是运行时环境问题比如Agent调用脚本时的工作目录跟你本地不一样导致相对路径失效。我建议脚本里所有路径都基于脚本自身所在目录计算不要依赖当前工作目录。比如Python里用Path(__file__).parent来定位资源文件。还有一种情况特别隐蔽Agent并行执行多个命令时脚本里如果有全局状态或临时文件互相同步会出错。解决办法是不要让脚本依赖临时文件改成全部从stdin/stdout传递数据如果必须写临时文件给文件加随机后缀用完就删。这条是我在反复调试中总结出来的非常实用。5.3 多个Skill之间互相干扰装了二十几个Skill之后你会发现有的Agent面对两个相似Skill时会随机选一个或者干脆不选。这时你要做两件事第一梳理Skill之间的触发词避免两个Skill覆盖同一个强关键词第二在SKILL.md里明确写清楚“本Skill专注什么如果用户提到X场景请使用另一个Skill”。我通常的做法是做一张“Skill能力矩阵”每个Skill一行列明触发场景、输入、输出贴在项目文档里。这一方面帮你理清Agent的能力边界另一方面排查冲突时一眼就能看出来。Skill不是越多越好宁可少而精每个都是真正跑通过的价值单元也别堆一堆没人调用的废文件。5.4 Skill如何迭代和维护Skill是活的需要长期养的。我一般把迭代流程分成四步收集失败案例、分析失败原因、修改指令和脚本、补充回归测试。尤其第一点平时用Skill时只要发现输出有问题我会立刻把“用户问题错误输出”存成case周末统一复盘。这比你在那凭想象优化一千遍都管用。版本管理我也不建议跳过。Skill目录放进Git仓库每次改动提交一次提交信息写清楚“修复了什么场景下的什么问题”。这样你三个月后定位回归真的会谢天谢地。技能资产是有复利效应的每次迭代都在把经验固化到文件里团队其他人克隆仓库就能用这是最划算的共享方式。6. 进阶如何把Skill写得“不像AI像个老手”6.1 少用空泛形容词多用可验证动作AI生成的指令天生爱说“全面分析”“深入理解”“考虑各种因素”这些话对Agent来说等于没说因为不构成任何可执行约束。真正有效的指令是带动作、带对象、带验收标准的。把“深入分析代码”改成“逐行阅读函数X找出所有可能返回None但调用方未判空的位置”这就是可验证动作。我写Skill时的自我审问是如果一个人严格按我的指令执行他能知道自己做完了没做完吗判断题标准如果只有他自己能定义那AI一定也会按它的理解自由发挥。宁可把标准定得机械一点、笨一点也不要追求那种听上去高级但其实模糊的措辞。你是在写操作手册不是在写散文。6.2 给边界条件和失败处理留足篇幅真实世界永远有例外。Skill里我习惯专门开一节“异常情况处理”把可能性最大的失败列出来并给解决路径。比如代码审查Skill里我会写“如果文件编码不是UTF-8跳过并标记为‘未能解析’如果目录为空直接输出‘未发现需审查的文件’不要报错。”给AI一条兜底路径比让它在失败时自作聪明好得多。还有一种边界是“用户输入不符合预期”。比如用户说“审查一下”但没说审查哪个项目Skill里可以写“当审查范围不明确时向用户确认后再执行不要默认用当前目录”。AI默认选一个目录的后果很严重它可能审了半天审的根本不是你想看的东西。6.3 用真实案例反哺Skill我自己编写Skill的经验是第一版永远不是写出来的是从真实使用里改出来的。你先基于自己的理解写一个粗糙版然后故意拿它处理几个真实案例看它哪里理解偏了、哪里漏了、哪里多余再针对性修正。循环两三轮之后Skill才算真正属于你的。判断一个Skill是否成熟的标志是它能不能处理你没预料到的情况。成熟度高的Skill指令里往往包含大量的“如果...那么...”这些分支不是你拍脑袋想象的是你在实际应用中撞出来的。所以我的最终建议是不要追求第一个版本完美先让它能用再让它好用。每次“踩坑-修复”的过程最后都会变成Skill里的那几行看似不起眼、实则价值千金的判断逻辑。
返回列表