AI Agent Skills技能包实战:从零搭建高效可复用工作流
你是不是也有这种经历每天让 AI 助手帮忙处理重复性任务比如把一篇文章改成固定格式、整理一批文件的目录结构、按团队规范生成代码模板每次都得把需求翻来覆去讲一遍稍不留神它还给你自由发挥跑偏。我折腾了大半年 Agent 开发最后发现真正让效率起飞的不是更复杂的框架而是一个听起来特别朴素的东西——skills。这里的 skills 不是简历上的个人能力清单而是 AI Agent 生态里正在快速普及的一种“技能包”机制。简单说你把一段可复用的操作流程、判断规则、脚本工具打包成一个标准目录放进 Agent 能扫描到的位置它就能在遇到对应任务时自动加载并使用这个技能。这篇文章我会从头拆解 skills 的设计思路、目录结构、编写方法和避坑经验内容偏向实操适合刚接触 Agent 开发、或者已经在用类似机制但总觉得不够顺手的开发者。看完你就能自己写一个能用的 skill。1. 先搞清楚Skills 到底是什么它解决了什么问题1.1 一个场景为什么我最终选择了 Skills 而不是别的方式一开始我接触 Agent 开发时习惯把所有指令塞进系统提示词里。比如“你是一个文章格式化助手请按以下规则处理文本……”规则写了上千字效果却一直不稳定。原因很简单系统提示词越长模型对每条规则的注意力权重就越低真正执行时经常顾此失彼。后来我尝试把规则拆成多个 Prompt 模板放在不同文件里让 Agent 按需读取效果好了不少但又引入了新问题——模板之间没有统一结构Agent 不知道什么时候该读哪个文件经常把完全无关的模板也加载进来。Skills 这个设计恰好解决了这个尴尬。它把“触发条件”和“执行内容”明确分开描述区告诉 Agent 这个技能是干什么的、什么时候用执行区才是具体的操作步骤和脚本。Agent 只看描述区就能做判断只有确认任务匹配时才去读完整内容。这种“先看标签、再拆包裹”的模式让复用性和稳定性一下子都上来了。我后来分析过一套管理得当的 skills 体系比同体量的提示词工程至少节省 60% 的上下文开销。1.2 Skills 的核心构成SKILL.md 加脚本缺一不可一个标准 skill 通常是一个独立目录目录里至少要有一个SKILL.md文件脚本和资源文件按需添加。SKILL.md是整个技能包的中枢用 Markdown 写成又分成两部分文件开头用 YAML 格式做了 frontmatter声明技能的名称、描述、适用场景frontmatter 后面是正文用自然语言写清楚执行步骤、判断规则和注意事项。辅助脚本不是强制要求的但实际使用中大部分 skill 都会带。原因很直接纯文字指令适合“怎么想”的任务比如写方案、做总结但凡是“怎么做”的任务比如批量重命名、解析 JSON、调用某个 API让 Agent 现场手写代码去实现出错几率远高于调用预先写好的脚本。所以我的习惯是凡是涉及数据操作的任务核心逻辑都写成 Python 或 Shell 脚本SKILL.md里只保留“何时执行、如何调用脚本、怎么判断结果”这些指导性内容。脚本负责确定性模型负责判断力各干各的活。1.3 它和 MCP、Function Calling、插件到底有什么区别很多人会把这几个概念搞混我整理一个表格帮你理清对比维度SkillsMCPFunction Calling传统插件本质指令加脚本的技能包标准化工具调用协议模型输出结构化调用参数宿主应用的扩展模块触发方式模型根据描述自动判断模型调用已注册的工具模型生成函数调用指令用户手动启用或触发是否需要服务端不需要需要独立服务端不需要但需注册函数取决于宿主应用上下文开销按需加载开销可控工具描述常驻函数定义常驻常驻内存或按需加载典型场景复用固定工作流连接外部系统与数据源执行单个原子操作扩展应用功能从表格能看出来MCP 解决的是“Agent 怎么接入外部世界”的问题比如连数据库、调第三方 API、读写某个 SaaS 服务Skills 解决的是“Agent 怎么按照你的方式做事”的问题比如处理 Markdown 的格式规范、生成符合团队代码风格的脚手架。两者完全可以配合使用一个技能内部需要读取远程数据时再通过 MCP 工具去拉取。Function Calling 则更偏底层通常是你给模型一个函数清单模型决定调哪个适合一次性的、细粒度的操作不适合封装多步骤工作流。至于插件那是宿主应用层面的扩展和 skills 所处的“模型能力层”不太是一个层级不过一些 Agent 应用也开始把 skills 作为插件的内部实现形式了。提示选型时别迷信某一个方案。我的判断标准很简单——任务是“一次性的原子操作”就用 Function Calling任务是“要连外部系统”就搭 MCP任务是“希望每次都能稳定复现同一套流程”就直接做成 skill。大多数实际需求最后都能归纳进第三种。2. 动手搭建第一个 Skill给博客文章做自动格式化2.1 先搭目录结构别小看文件命名我建议你把所有 skills 集中放在一个目录里比如~/.claude/skills/或者你正在用的 Agent 工具指定的 skills 目录。每个技能一个子目录目录名就是技能名用短横线连接全小写。例如skills/ └── blog-post-formatter/ ├── SKILL.md └── scripts/ ├── format_article.py └── check_frontmatter.py这里有个容易踩的坑目录名不能乱起因为很多 Agent 框架会直接拿目录名当技能的唯一标识。如果你把目录命名为blog formatting tools中间带空格解析时很可能出问题。我见过有人用中文目录名某些框架也能识别但为了跨平台、跨工具复用老老实实用英文短横线命名最稳。SKILL.md必须放在技能目录的根目录文件名大小写也别搞错。脚本建议统一放scripts子目录其他资源比如模板文件、配置文件可以另建templates、assets等目录。结构上保持简洁别把无关文件塞进来因为部分 Agent 在加载技能时会把整个目录内容索引进去文件越多、加载越慢。2.2 SKILL.md 的 frontmatter 怎么写才能让模型准确触发frontmatter 是模型判断“要不要用这个技能”的第一依据重要性占整个 skill 的一半。标准格式长这样--- name: blog-post-formatter description: 用于将 Markdown 博客文章规范化为团队发布格式。 当用户要求调整文章格式、补充 frontmatter、统一标题层级、 整理代码块语言标注时使用。 不适合用于 HTML 文件处理或非 Markdown 文档转换。 ---几个关键点拆开讲。name字段要保持和目录名一致否则有些框架会用它做路径拼接不一致就会报错。description字段是重中之重它决定了技能召回率。写法上有三个原则第一用动词开头直接说清楚这个技能能做什么第二多给触发场景的变体表达比如“整理格式”“规格化”“统一风格”这些说法都能被考虑到第三明确排除不需要处理的场景也就是“不适合用于什么”这一点很多人忽略实际上能把误触发率降得很低。这里有个细节我试过很多次description 的结尾不要用句号。模型在解析 YAML 时句号偶尔会粘连到后面字段上虽然概率不高但没必要冒这个风险。另外description 长度控制在 200 字以内比较合适太长会让模型抓不住重点太短又覆盖不了变化多端的用户表达。2.3 指令正文的写法把操作流程写成人话模型才听得懂frontmatter 下面是正文也是这个 skill 的执行手册。正文不需要长篇大论但要结构清晰、步骤明确。一个容易犯的错是把正文写成“产品需求文档”全是抽象原则模型看了不知道怎么落地。我推荐用“观察 → 判断 → 操作 → 验证”的流程式写法# Blog Post Formatter ## 任务目标 将输入的 Markdown 文章转换为符合团队发布规范的格式并补齐元数据。 ## 执行步骤 1. 读取目标文件内容检查文件开头是否包含 YAML frontmatter。 2. 若缺少 frontmatter根据文章首段内容推断标题、摘要、日期生成 frontmatter。 3. 统一标题层级文章中只能有一个一级标题其余标题从二级开始多级标题按层级递增。 4. 检查代码块每个代码块必须标注语言类型未标注的要根据内容推断并补充。 5. 调用 scripts/format_article.py 将处理后的内容写回文件。 6. 调用 scripts/check_frontmatter.py 校验结果校验失败则回滚修改。 ## 注意事项 - 不要改动文章正文的实质内容只做格式与元数据调整。 - 不要为图片补充 alt 之外的其他 HTML 属性。 - 遇到无法推断语言类型的代码块统一标记为 text。 - 处理完成后应输出简要变更摘要包括修改的文件名、新增的 frontmatter 字段、代码块补充情况。这种写法最大的好处是给模型提供了一条清晰的决策路径先做什么、遇到分支怎么处理、最后怎么验证。模型在执行时相当于拿着流程图走了一遍比“请你规范一下这篇文章”这样模糊的指令可靠得多。2.4 辅助脚本怎么写参数、dry-run 与结构化输出脚本部分是 skill 里确定性最强的环节。我建议所有脚本都写成命令行工具风格支持从标准输入读取数据或接收文件路径参数输出结果要有明确的结构。下面是一个简化但能直接用的示例#!/usr/bin/env python3 格式化 Markdown 文章的辅助脚本。 import argparse import re import sys from pathlib import Path FRONTMATTER_PATTERN re.compile(r^---\s*\n(.*?)\n---\s*\n, re.DOTALL) def parse_args(): parser argparse.ArgumentParser(descriptionFormat a Markdown article.) parser.add_argument(file, helpPath to the Markdown file.) parser.add_argument(--dry-run, actionstore_true, helpPreview changes without writing.) return parser.parse_args() def ensure_frontmatter(content: str) - str: if FRONTMATTER_PATTERN.match(content): return content title_match re.search(r^#\s(.)$, content, re.MULTILINE) title title_match.group(1).strip() if title_match else Untitled fm f---\ntitle: \{title}\\ndate: \{datetime.date.today()}\\n---\n\n return fm content def normalize_headings(content: str) - str: lines content.splitlines() in_code False for i, line in enumerate(lines): if line.strip().startswith(): in_code not in_code if not in_code and re.match(r^#\s, line): lines[i] re.sub(r^#\s, ## , line) return \n.join(lines) def main(): args parse_args() path Path(args.file) if not path.exists(): print(fERROR: file not found: {path}, filesys.stderr) return 1 content path.read_text(encodingutf-8) processed normalize_headings(ensure_frontmatter(content)) if args.dry_run: print(processed) return 0 path.write_text(processed, encodingutf-8) print(fOK: processed {path.name}) if __name__ __main__: sys.exit(main())我为这个脚本设置了三个关键设计你在写自己的脚本时可以直接抄第一支持--dry-run参数。Agent 执行任务时不一定每次都想直接改文件尤其是用户只说了“看看格式有什么问题”的时候。有 dry-run 模式模型可以先预览改动再决定是否落盘安全性高很多。第二所有输出都走标准输出或标准错误流错误信息以ERROR:开头。这样模型解析脚本结果时就不需要去猜状态看到ERROR:就知道要停下来说明原因看到OK:就继续下一步。第三路径处理要稳。脚本接收文件路径作为参数而不是在脚本内部拼路径。这样 Agent 在调用脚本前已经完成了工作目录的切换或路径解析脚本自身不需要知道自己在哪个目录也避免了“相对路径找不到文件”这种天生难排查的问题。3. 一个完整实操用 skill 清洗一篇 Markdown 文章3.1 准备一个测试文件模拟真实使用理论讲再多不如跑一遍。我准备了一个典型的“脏” Markdown 文件作为测试样本# 我的标题 ## 二级标题 第一段文字没有 frontmatter直接进入正文。 ### 三级标题 下面是代码块 ​python print(hello) ​能看到问题不少没有 YAML frontmatter一级标题出现了但按团队规范文章里不应该存在一级标题代码块没有语言标注。现在我来模拟用户向 Agent 发起请求“帮我把文章article.md格式化为标准格式。”3.2 在 Agent 环境中加载 skill观察调用过程如果你的 Agent 工具支持 skillsClaude Code、Codex CLI 这一类的都在逐步支持它会经历这样一个过程先把用户的请求和已有技能的描述做语义匹配匹配到blog-post-formatter这个技能后读取完整的SKILL.md按里面的步骤执行。从日志面板能看到几个关键节点先是matching skill: blog-post-formatter接着loading skill然后开始调用脚本。整个过程大概是这样的[1] 匹配到技能blog-post-formatter [2] 读取技能指令确认执行步骤 [3] 运行 python3 scripts/format_article.py article.md --dry-run [4] 检查 dry-run 输出确认没有破坏正文内容 [5] 运行 python3 scripts/format_article.py article.md [6] 运行 python3 scripts/check_frontmatter.py article.md [7] 返回处理摘要给用户这里有个很关键的地方Agent 在步骤 3 先跑了 dry-run看了输出之后才决定正式执行。如果SKILL.md里没有写“先 dry-run 再执行”很多模型会直接跳到步骤 5安全性就差了不少。所以指令正文里的流程描述本质上是给模型戴了一个行为约束。3.3 校验结果哪些环节最容易出偏差处理完之后打开article.md你会看到一个相对规范的版本。但实操中我发现这类任务有三个环节最容易出偏差需要你特别关注。第一个是标题层级的调整。如果原始文章里既有#又有##脚本要判断哪些该降级、哪些该保留规则稍微复杂一点就出错。比如文章里只有一个一级标题且它是全文标题应该改成 YAML 里的title字段而不是机械地降为##。这个判断逻辑最好写进脚本不要指望模型现场推理。第二个是代码块语言推断。模型对没标注的代码块做语言推断时经常把python写成python3把js写成javascript这个倒不算错但如果团队规范里对语言标签有严格批注脚本里就得做一层映射。更麻烦的是有些代码块是日志片段、配置示例模型很容易误判成某种语言所以我建议推断置信度不高就标text多一事不如少一事。第三个是 frontmatter 的日期字段。很多文章是在旧文件上改的如果脚本直接取当天日期可能会把原始发布日期覆盖掉。我的处理方式是在SKILL.md里额外说明如果原文已有 frontmatter 且包含日期字段不要自动更新只有新增 frontmatter 时才补日期。这种“规则兜底”的方式比依赖模型自觉靠谱得多。3.4 进阶如何让 Skill 在团队里共享单个技能写好了下一步自然是想让团队其他人一起用。我的建议是把整个skills目录纳入 Git 仓库管理而不是用网盘分享压缩包。好处有三个改动有记录哪次更新导致行为变化可以回滚可以做 Code Review团队成员都能检查技能逻辑是否合理可以通过 Git Submodule 或 vendoring 的方式嵌入到不同的项目里。仓库结构可以这样组织skills-repo/ ├── README.md ├── blog-post-formatter/ ├── code-review-helper/ ├──>import os from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent def resolve_asset(relative_path: str) - Path: return BASE_DIR / relative_path这样不管外部从哪个目录调用脚本脚本内部拿到的路径都能正确定位到技能目录下的资源文件。同理SKILL.md里给模型的工作目录指令也要说明清楚先确定工作目录、再调用脚本传递的路径应该用绝对路径或相对于工作目录的路径。4.3 Skill 内容污染上下文一个设计不佳的 skill 可能让上下文爆炸。比如技能目录放了一堆参考资料、日志文件、示例输出Agent 加载技能时把这些全读了多轮对话下来上下文窗口被无关内容挤占模型回复质量明显下降。控制方法有几个。首先SKILL.md里只写必要内容详情放在独立文件中按需读取。其次脚本输出用摘要格式不要打印大量数据尤其是日志类内容。最后给技能设置“一次性对话”模式如果在某轮对话中技能已经执行完成后续对话中就不要再保留技能的全部内容只保留结果摘要——有些 Agent 框架会做自动摘要没有的话你可以把“执行完毕后输出简要摘要”写进指令正文模型就会主动压缩。还有一个容易被忽视的点技能里的敏感信息。如果脚本里有 API Key、数据库连接串这些内容会随着技能加载暴露给模型存在一定泄漏风险。我的原则是技能目录里绝不保存密钥需要凭据时通过环境变量或密钥管理服务动态注入。5. 我的使用体验与后续扩展方向5.1 用了一个季度之后的体会从零开始搭建自己的 skills 库到现在我最大的感受是这玩意儿跟乐高积木很像单个技能功能有限但组合起来能搭出很复杂的东西。比如我手头有一个“日志分析”技能输入日志文件后输出错误分类统计另一个“周报生成”技能能读取一个项目目录下的提交记录和任务看板。单独看都不稀奇但我还写了一个“工作流编排”技能能把前面两个串起来日志分析结果作为周报的“风险与问题”章节素材这样每周五跑一遍半小时的工作缩短到三分钟。之前我只在工具层面看待 skills后来意识到它更本质的价值是“组织经验的载体”。团队里最有经验的人他的那些判断标准、做事顺序、避坑策略以前只能通过口口相传或者冗长的文档来传递现在可以把它固化成技能包新人装好就能用而且还能不断迭代。这套机制的想象空间比“写个更好的提示词”大得多。5.2 还可以怎么发展从个人技能包到团队知识库后续我有几个想推进的方向供你参考。一个是技能的市场化共享社区里已经有人在整理公开的技能库仓库类似于 npm 之于 JavaScript、Homebrew 之于 macOS以后安装一个技能可能就一条命令的事。另一个方向是技能与项目描述文件的联动让技能不仅“能干活”还知道在什么样的项目上下文里用什么样的处理标准这会让自动化和规范化更进一步。我目前还在尝试的是给每个技能写自动化测试。以前觉得技能是给模型用的没法测后来发现脚本部分完全可以做单元测试SKILL.md里的规则描述也可以用“用户指令样本 期望行为”的语料来回归验证。每次改动技能先跑一遍测试心里踏实很多。最后分享一个个人习惯每次处理完一个没沉淀过的重复任务我都会反问自己一句——“这个任务以后还会不会再遇到”如果答案是会那当周我就会把它固化成技能。周末花半小时写文档和脚本换回来的是之后每周都能省下半天。这笔账怎么算都划算。