Agent Skills 实战指南:从零开发到多 Agent 编排的完整笔记
最近在调试自己的 Agent 项目时我花了不少时间折腾 Agent Skills。以前实现一个 AI Agent 的能力基本是把工具函数一股脑塞给模型然后在 prompt 里写清楚每个函数是干嘛的祈祷它能在正确的时候调用正确的那一个。可项目一复杂工具列表变得又臭又长模型开始频繁选错工具我才意识到真正缺的不是更多工具而是一套“能力如何被发现、加载和复用的机制”。Agent Skills 正是冲着这个问题来的。Claude Agent Skills、Codex Skills、GitHub Skills 这些名字频繁出现在我收藏夹里连带着 agent 开发、skills 开发、agent 架构、多 agent 这些关键词在一段时间内占据了我的搜索记录。折腾完几个框架后我理解到可以把 Skills 理解成 Agent 世界里的“插件包”它把任务描述、可执行工具、依赖和示例打包成一个标准单元让 Agent 在需要的时候自己找到并调用。这篇内容会把我从零开发、安装、测试 Agent Skills 的完整笔记放出来包括踩过的坑和不方便写在官方文档里的细节。适合正在做 AI Agent 开发、想给前端项目接入 Agent 能力、或者单纯对 Codex Skills 这类新东西感兴趣的开发者参考。1. Agent Skills 到底是什么从“塞满工具”到“按需加载”的设计转变很长时间里我做 Agent 都是这么写的先定义一堆 Python 函数然后用 JSON Schema 把它们描述成一个 tools 数组最后在 system prompt 里加上一句“你可以使用以下工具”。这个模式在工具数量少于十个的时候确实够用但一旦超过二十个模型的选择准确率明显下降上下文也被工具描述撑得越来越长。Agent Skills 换了一种思路不再把每个工具赤裸裸地暴露给模型而是把一组相关操作打包成一个可以被“按需发现”的独立模块。1.1 为什么 function calling 不够用Function calling 的本质是给模型一张“菜单”模型每次都要浏览整张菜单再决定点什么菜。菜单短的时候没问题可当你把“发送邮件”“解析 PDF”“查天气”“操作数据库”“生成图表”全部塞进去模型光理解菜单就要消耗大量 token而且经常发生“看着 A 工具的描述却调用了 B 工具”的尴尬情况。Skills 的做法更像给每个能力配了一份独立说明书然后把说明书放进一个可控的目录里。模型不再需要在一开始就读完全部工具描述而是在执行某个环节时根据当前任务自动检索对应的 Skill读取它的 SKILL.md 说明文件再决定怎么调用。这个过程把“所有能力常驻上下文”变成了“能力按需加载”上下文压力小很多工具的扩展也不再受上下文窗口的硬性限制。我一开始也觉得这只是包装方式的变化直到我亲眼看到一个项目从 30 个平铺工具改成 6 个 Skills 之后模型在任务中选错工具的概率明显下降响应速度也快了。这背后其实是把“模型自己从大列表里猜”变成了“先用检索缩小范围再让模型读更详细的指令”。1.2 Skill 与 Tool、Plugin 的核心区别很多人会混淆 Skill、Tool、Plugin 这三个词包括我自己早期也经常混用。Tool 是最小粒度的可调用函数比如“获取天气”“打开文件”Plugin 通常是面向已有软件系统的集成包比如 Jira 插件、Slack 插件而 Skill 的定义更贴近“面向任务的能力单元”它内部可能封装了好几个 Tool还可能包含步骤说明、示例、依赖配置和约束条件。可以这样理解Tool 是“一只手”Skill 是一套“怎么用手完成一项工作的完整方案”。一个名为analyze_logs的 Skill可能内部包含读取文件、正则匹配、生成报告这三个 Tool外加一份说明文档告诉模型应该按什么顺序调用什么时候该放弃输出格式长什么样。从设计目标来看Tool 解决“模型能不能调用”的问题Plugin 解决“系统之间怎么集成”的问题而 Skill 解决“模型怎么知道自己应该用什么、以及怎么用得更好”的问题。这也是为什么很多 Agent 框架开始把 Skills 作为一级公民来对待而不是简单把工具列表换个名字。1.3 “描述优先”是 Agent Skills 的灵魂使用 Claude Agent Skills 时我最大的一点体会是模型真的会认真读 SKILL.md 里的描述然后根据描述决定是否调用。这意味着描述文件写得像“代码注释敷衍了事”还是像“给接手项目的同事写交接文档”直接决定了 Skill 会不会被正确使用。我做过一个对比实验同一个 Node.js 脚本一个 SKILL.md 只写了两行“格式化 JSON 数据”另一个写了详细的输入输出示例、注意事项、错误码含义。结果显示用第二份描述时模型能够正确处理异常情况甚至会在输出不合法时主动重试而第一份描述下面模型经常把字符串当对象处理然后直接抛错。所以我把“描述优先”视为 Agent Skills 的灵魂所在。要让一个 Skill 真正好用描述文件至少要说清楚四件事这个 Skill 什么时候用、什么时候不要用、输入长什么样、输出长什么样。最好再附带一两个最小可运行示例模型会像人看菜谱一样照着示例的格式去执行。2. 手写一个 Agent Skill目录结构、元信息与代码实现前面讲了一堆概念现在进入最实用的部分自己动手写一个 Agent Skill。无论你用的是 Claude Agent Skills、Codex Skills 还是自己写的 Agent 框架核心套路其实差不多都是把“说明 代码 依赖”放进一个标准目录里再通过配置文件或目录约定让 Agent 发现它。2.1 最小可用的 Skill 目录长什么样我以 Claude Agent Skills 的目录约定为例一个最小的 Skill 通常长这样~/.claude/skills/ json-formatter/ SKILL.md tool.py requirements.txt当然具体放在哪个根目录取决于你的 Agent 框架。Codex 通常会扫描当前项目里的.agents/skills目录也有些框架支持通过AGENT_SKILLS_PATH环境变量指定路径。关键是遵循“一个 Skill 一个目录”的原则目录名用短横线连接的小写英文也就是 kebab-case方便模型从名称中理解用途。json-formatter就比JSONFormatterSkillV1清晰得多。SKILL.md 是这个 Skill 的身份证也是模型最先读取的文件。requirements.txt 里写 Python 依赖tool.py 是实际执行的逻辑文件一般会暴露一个命令行入口或者一个可调用的函数。某些框架还支持在同级目录放examples/子目录用来存放输入输出样例。这个目录本身不是必须的但加上之后Skill 在测试阶段会很方便。2.2 SKILL.md 里的描述怎么写模型才肯用我见过很多人把 SKILL.md 写成了 README通篇讲这个项目是谁维护的、版本迭代历史、作者感言。这些东西模型不关心它只想知道“遇到什么任务时该使用这个 Skill以及该怎么使用”。我的模板一般是# json-formatter 当用户需要把非标准 JSON 内容转换为可供程序读取的 JSON 文件时使用此 Skill。 适合处理带有大量注释、尾随逗号或单引号的 JSON 数据。 ## 何时不要使用 如果输入已经是合法 JSON不要使用本 Skill直接返回原内容。 ## 输入 - 源文件路径 - 目标文件路径 ## 操作步骤 1. 读取源文件内容。 2. 使用 tool.py 的 fix 命令清理注释和尾随逗号。 3. 将结果写入目标文件。 4. 如果解析失败返回具体错误行号。 ## 示例 python tool.py fix --input examples/messy.json --output output/clean.json写完这份说明之后模型对 Skill 的“触发时机”和“执行方式”就有了明确预期。需要提醒的是不要在这里写太多复杂嵌套的子标题模型读取 Markdown 的能力再强也是一种线性阅读理解平铺直叙反而效果更好。2.3 用 Python 实现一个简单的数据处理 Skill下面是我在实际项目中写的一个极简示例功能是把“带注释的 JSON”清理成标准 JSON。它的目录结构就是上面那个 json-formatter。tool.py 的核心逻辑长这样#!/usr/bin/env python3 import argparse import json import re import sys def strip_comments(text: str) - str: # 去掉 // 和 # 开头的行注释但不要影响字符串内部的 # 符号 lines text.splitlines() cleaned [] for line in lines: stripped line.strip() if stripped.startswith(//) or stripped.startswith(#): continue # 简单的字符串外注释清理正式场景建议用 JSON5 解析库 cleaned.append(line) return \n.join(cleaned) def fix_json(source: str, target: str) - None: with open(source, r, encodingutf-8) as f: raw f.read() raw strip_comments(raw) # 先用 JSON5 或 json.loads 解析解析失败时手动补充缺失字段 try: data json.loads(raw, parse_constantlambda x: None) except json.JSONDecodeError as err: print(fERROR: line {err.lineno}, col {err.colno}: {err.msg}) sys.exit(1) with open(target, w, encodingutf-8) as f: json.dump(data, f, indent2, ensure_asciiFalse) print(fOK: wrote {target}) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(fix, helpsubcommand to fix json) parser.add_argument(--input, requiredTrue) parser.add_argument(--output, requiredTrue) args parser.parse_args() fix_json(args.input, args.output)这个脚本本身很简单但它展示了 Skill 的一个关键设计输入输出尽量用命令行参数控制不要让模型去猜文件位置。模型调用 Skill 时通常会自己拼接命令行所以参数名必须和 SKILL.md 里描述一致不然就会出现 Agent 生成了一个不存在的参数最后报错的情况。2.4 调试 Skill 时的三个小技巧第一先在终端里手动跑通命令再交给 Agent 调用。我很长一段时间都是直接让 Claude 调用 Skill结果报错了还以为是模型的问题后来才发现是脚本自身的路径写错了。手动跑一遍能排除掉大部分低级错误。第二在 SKILL.md 里加入“输出格式”示例。比如要求工具在成功时输出OK: ...失败时输出ERROR: ...。Agent 看到输出前缀后会更容易判断是否要重试。如果输出全是堆栈模型很难知道错误严重程度。第三善用--dry-run参数。在脚本里加一个--dry-run模式只打印将要执行的操作不真的写文件。调试时让 Agent 先跑 dry-run确认没问题后再正式执行。这一个设置可以避免很多不可逆操作。3. 把 Skills 装进 AgentClaude、Codex、GitHub 与第三方框架的安装路径写好了 Skill下一步就是把它安装到 Agent 环境里。现在市面上的安装路径五花八门有人用 Claude Agent Skills 的~/.claude/skills目录有人用 Codex 的项目级配置还有人用 GitHub Skills 的模板仓库。我挨个试过之后发现虽然路径和命令不同但底层逻辑都围绕同一个点让 Agent 在运行时能发现 Skill 文件。3.1 Claude Agent Skills 的目录约定与安装步骤如果你用 Claude 桌面版或 CLI一般把 Skill 放到用户目录下的~/.claude/skills即可。每个子目录就是一个 Skill启动 Agent 后它会在会话里自动扫描这些目录。某些版本支持全局 skills 和项目级 skills项目级目录常见于.claude/skills。安装步骤其实只有两步把 Skill 文件夹复制到目标目录然后重启或新开一个会话。如果 Agent 没有识别到检查目录名是否为 kebab-caseSKILL.md 文件是否存在以及文件权限是否可读。有一个非常容易被忽略的坑很多人把 SKILL.md 写成了 skill.md 或 SKILL.MD在 Linux 下文件名大小写敏感Agent 就找不到了。3.2 Codex Skills 的安装逻辑与 kebab-case 规则OpenAI 的 Codex 是命令行编码 Agent它的 Skills 机制更贴近“项目内工具集”。我通常在项目的.agents/skills目录里放项目专属 Skill这样团队克隆仓库后不需要额外配置Agent 就能发现全部技能。Codex 对 Skill 目录名有严格要求必须是小写字母加短横线比如create-tests、review-changes不能用驼峰。安装现成 Skill 也比较直接从 GitHub 下载后放到指定目录然后跑一句类似codex login或者新开会话让它重新扫描。Codex 还支持远程加载但实践中我更推荐直接放本地目录因为远程 URL 一旦失效Agent 的可靠性会大打折扣。有个值得注意的细节Codex 倾向于把 Skills 和系统命令绑定得更紧密。比如一个post-commitSkill可以写成 shell 脚本形式Codex 会直接当作终端命令执行。所以写 Codex Skill 时不一定要用 Python只要是能在 shell 里跑起来的脚本都可以这让 Skill 的复用成本降低了很多。3.3 GitHub Skills 和社区平台哪里找靠谱的现成 SkillsGitHub 上已经有大量现成 Skills搜索awesome agent skills能发现一批整理好的列表。除了 GitHubClaude Skills 官方市场以及一些第三方工作台也在做 Skill 分发比如 Hermes Agent 就支持从第三方工作台拉取 Skills。Reasonix 这类工具也出现了安装新 Skills 的专门入口。选型时我通常按三个标准判断是否包含完整的 SKILL.md而不是只有一段 README 代码。是否有明确依赖声明比如 requirements.txt、package.json 或 Dockerfile。最近的提交时间是否在半年内太老的 Skill 很可能因为框架 API 变化而失效。另外要注意 Skill 的授权协议。很多 Skills 仓库用的是 MIT 或 Apache 2.0但也有一些仅限个人使用。如果要在商业项目里用提前看 LICENSE 文件能避免后面扯皮。3.4 多框架兼容一套 Skill 如何通吃因为我手头同时有 Claude、Codex 和一个自研的小 Agent 框架所以我希望一套 Skill 能尽量通吃。做法是底层脚本保持语言无关用标准输入输出交互SKILL.md 按通用模板写配置文件尽量遵循.agents/skills和~/.claude/skills两套目录约定都放一份。在实际落地时我会在项目仓库里建一个skills/目录存放原始文件然后用一个 shell 脚本把文件同步到不同框架需要的目录。这样既不会污染项目源码又能让多个 Agent 共享同一份 Skill。同步脚本本身写得很简单就是cp -r加路径映射但它帮我省掉了每次手动复制更新的麻烦。还有一点不同框架对“Skill 是否激活”的默认策略不一样有的默认全部加载有的默认全部禁用需要显式启用。安装新 Skill 后如果发现 Agent 没有任何反应先去配置里看一眼它到底有没有被加入激活列表。4. 实战排查与避坑从报错到安全折腾 Agent Skills 的过程中我遇到最多的不是功能逻辑问题而是环境、配置、上下文带来的各类奇葩报错。其中最有代表性的就是agent execution terminated due to error这句话在日志里出现时很多人第一反应是模型不行但大部分时候锅在 Skill 自己身上。4.1 agent execution terminated due to error 排查思路这是 Agent 调用 Skill 过程中发生未捕获异常时常见的终止信息。排查顺序我建议是从外到内先看 Agent 日志里的最后几步操作确认是创建进程失败、脚本返回非零退出码还是网络请求超时。我把主要的可能原因整理成了一张排查表可能原因表现解决方式脚本路径错误日志里显示 “No such file or directory”用绝对路径调用或确认启动目录依赖缺失报 ImportError / Command not found重新安装 requirements.txt或改用容器运行文件权限不足Permission denied检查脚本可执行权限和用户权限输入参数和预期不一致脚本收到空字符串或错误类型在 SKILL.md 中加强参数约束示例输出过长输出超过上下文限制进程被截断脚本压缩输出只返回摘要信息一个真正有用的技巧是不要让 Agent 直接执行复杂的 Python 模块而是先用python -m加模块名的方式测试是否能正常启动。如果模块启动时就炸后面所有操作都会跟着失败。4.2 Token 暴涨的元凶过度膨胀的 SKILL.mdSkill 虽然能按需加载但 SKILL.md 一旦写了上万字每次加载依然会占据大量上下文。我之前见过有人把整个项目的接口文档都塞进 SKILL.md 里结果模型还没干活上下文就已经满了每次调用都像在烧钱。正确做法是让 SKILL.md 保持精炼只写“如何调用”和“关键注意点”把详细接口文档放到同级目录的docs/里并在 SKILL.md 里用链接或相对路径指向它。模型只有在需要更多细节时才去读附加文档。这里有一个取舍linked docs 虽然不被默认加载但能显著降低日常 token 消耗非常适合大项目。如果你发现 Agent 响应变慢可以先统计一下每次会话加载了哪些 Skill 的 SKILL.md把那些从不被触发的 Skill 从全局目录里移除只保留当前项目真正需要的。我的项目从 20 个全局 Skill 减到 8 个之后整体响应速度提升非常明显。4.3 同名 Skill 冲突与加载顺序当~/.claude/skills和项目.claude/skills里存在同名 Skill 时具体生效的是哪个和框架的优先级策略有关但最常见的规则是“项目覆盖全局”。这既是好事也是坑你可能因为全局里有个旧版本 Skill把项目新版本 Skill 覆盖掉导致 Agent 一直用旧逻辑。排查方法很简单启动 Agent 时在日志里搜 Skill 名称看它实际加载了哪个路径下的文件。如果发现加载了预期之外的版本直接删除或重命名那个目录。另外不要在一个 Skill 目录里塞多个同名脚本文件名用语义化前缀区分比如json_fix.py和json_validate.py避免模型混淆。4.4 来源不明的 Skill 千万别乱装安全边界Skill 本质上是一段可以执行任意代码的程序。如果从不可信来源下载了一个 Skill它完全可以在脚本里读取你的环境变量、篡改项目文件甚至把数据上传到远程服务器。这跟安装来路不明的浏览器插件一样危险。我在下载社区 Skill 后第一件事永远是把代码从头到尾读一遍确认没有奇怪的网络请求、没有eval执行外部字符串、没有在代码里隐藏“发行后门”。阅读 SKILL.md 之外还要看requirements.txt里的依赖包是不是正规包名。另一个安全习惯是在沙箱容器或虚拟环境里运行来源不明的 Skill即使脚本炸了也不会影响到宿主机器。这里给出几条最低限度原则不要给 Skill 过大的文件系统权限不要让 Skill 直接访问凭证管理库不要用 root 用户运行 Agent。如果把 Agent 部署在服务器上尽量给每个 Skill 单独建系统用户配合 seccomp 或容器隔离。多一层隔离就能少一次事故。5. 多 Agent、记忆与 Skill 编排的进阶玩法当你的 Agent 从单个子任务变成一个完整系统时Skills 的作用就不再局限于“用某个工具”而是开始承担模块划分、状态管理和多智能体协作的责任。这应该算 Agent Skills 的高级话题也是我目前正在实践的方向。5.1 Skill 如何在不同 Agent 之间跨会话共享多 Agent 架构里一个常见的做法是“一个主调度 Agent 多个执行 Agent”。每个执行 Agent 可以挂一组自己的 Skills但有时候主 Agent 需要调用子 Agent 的能力。直接复制 Skill 太笨我通常把公共 Skills 放到独立的共享目录里比如/srv/agents/skills-shared然后通过框架的配置项让多个 Agent 引用同一个目录。共享目录带来的一个问题是并发写冲突。如果两个 Agent 同时用一个 Skill 写同一个输出文件文件会被覆盖。解决办法是让 Skill 内部生成带时间戳或会话 ID 的文件名写完后把路径返回给 Agent。这个细节看着小但在多 Agent 场景里特别重要很多数据丢失都是这么来的。5.2 给 Skill 增加记忆能力从状态文件到向量库单独的 Skill 本身是无状态的但多 Agent 协作时往往需要“记住上次做到哪了”。我最早的做法是在 Skill 目录里放一个state.json每次执行完更新它。这个方法简单可靠缺点是并发时容易冲突。后来我尝试给 Skill 外挂一个向量记忆库让 Skill 每次执行前先从记忆库里检索相关的历史上下文。效果很好但复杂度也上来了需要处理向量库的连接、索引更新和权限隔离。对于中小型项目我仍然推荐从状态文件开始只有当 Agent 表现出明显的“遗忘历史”问题时再考虑升级到向量记忆。有一点需要注意不论用哪种记忆方案都要把记忆内容和 Skill 的业务逻辑分开存放。不然 Skill 更新后旧版本残留的状态文件可能会让新逻辑崩溃。5.3 高性能场景Rust 框架里的 Skill 运行沙箱最近社区里关于“基于 Rust 语言实现 AI Agent”的讨论越来越多我也试过用 Rust 写 Agent 调度层。Rust 生态里对 Skills 的支持目前还比较早期但有一个明显优势可以借助 Rust 的安全能力把 Skill 运行在 with-deny 这样的沙箱里限制文件系统访问、网络访问和进程创建。在这类高性能场景下Skill 的形态往往不再是一个 Python 脚本而是编译成 wasm 或二进制可执行文件由 Rust Agent 直接调用。好处是启动快、内存占用低而且因为二进制接口明确Agent 不需要依赖解释器环境。缺点是开发调试成本相对高不适合快速原型阶段。我的建议是如果团队已经有 Python/Node 的 Agent 项目没必要为了追新强制切 Rust。但如果你想做超低延迟、高并发的 Agent 服务研究一下 Rust wasm 的运行沙箱是非常值得的。5.4 一条经验先从最小闭环开始在我自己写的一套 Skills 系统里最初的版本只包含一个 Skill功能是“从 CSV 生成 HTML 表格”。就是这么小的一个闭环帮我验证了目录结构、描述文件、参数传递、错误输出这一整条链路。之后我每加一个新的 Skill都先拿最小闭环跑通再逐步丰富细节。这个习惯帮我避开了很多纸上谈兵的问题。比如我之前规划了一个“自动撰写测试报告”的 Skill设计文档写了一堆流程但真正实现后发现模型根本不会按预期触发它。原因很简单SKILL.md 里的“何时不要使用”写得太复杂模型看到那么多条件反而犹豫了。删掉一半约束之后触发率立刻上来了。最后说一个我个人的体会Agent Skills 最有趣的部分不是它封装了多少工具而是它让 Agent 的能力边界变得可以像乐高一样自由组装。你可能会在先手动跑通一个 Python 脚本时觉得无聊但当你看到模型通过 SKILL.md 自己找到了正确工具、完成了预期操作时那种感觉确实像是打开了新世界。这个方向还在快速演进框架之间的规范也在相互借鉴早点上手至少不会被时代甩下。