ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agent Skills 实战:从零编写可复用的 AI 能力包

Agent Skills 实战:从零编写可复用的 AI 能力包 1. 从“skills”这个标题说起它到底指什么第一次看到“skills”这个标题很多人会以为是某个泛泛而谈的能力清单或者一份简历上的技能罗列。但结合热搜词里反复出现的 Agent Skills、Google Cloud、npx、GKE、claude agent skills、codex skills 这些词基本可以确定这里说的 skills 不是人类的能力而是给 AI Agent 使用的一套可插拔能力包。你可以把它理解成给一个刚入职的智能体发的“工具腰带”——每挂上一个 skill它就多会干一件事比如查数据库、调云服务、跑测试、生成分镜、做安全扫描。我最早接触这个概念是在折腾自动化工作流的时候。当时手头有一堆重复任务拉取云端日志、跑一遍端到端测试、把结果整理成报告。每次都要重新写提示词、重新贴上下文效率极低。后来发现 Agent Skills 这套机制把“怎么做某件事”的流程、脚本、依赖、说明打包成一个独立单元Agent 按需加载用完即走。这一下就把我从“每次重新教”的泥潭里拽了出来。所以这篇内容适合谁看如果你是刚听说 skills、不知道它和普通提示词有什么区别的新手我会从零讲清楚它的结构和加载逻辑如果你已经在用 Claude、Codex 这类工具想自己写 skill 或者找现成的 skill 来用我会把安装、调试、避坑的细节都摊开讲。核心关键词 skills、Agent Skills、npx、GKE 会自然贯穿全文不堆砌只在该出现的地方出现。需要先明确一点skills 不是某个厂商独有的东西它更像一种约定俗成的组织方式。不同平台对它的叫法和加载方式略有差异但底层思路一致——把能力从模型里解耦出来变成可版本化、可复用、可组合的外部资源。理解了这一点后面所有的操作你都能自己推导。2. 整体设计思路为什么要把能力拆成 skills2.1 从“万能提示词”到“按需加载”的转变早期用大模型大家习惯写一个超长提示词把角色、任务、约束、示例全塞进去。这种做法在单一场景下能用但一旦任务变多提示词就会膨胀到几千字模型注意力被稀释效果反而下降。更麻烦的是不同任务需要的工具和知识完全不同硬塞在一起会互相干扰。Agent Skills 的思路正好相反主提示词只负责调度和决策具体能力放在独立的 skill 里用到哪个加载哪个。这就像公司里不会让一个人同时干财务、法务、运维而是设不同岗位需要时找对应的人。每个 skill 有自己的说明文档、执行脚本、依赖清单Agent 在规划阶段先看有哪些 skill 可用然后按需调用。这种设计带来的直接好处有三个。第一上下文干净模型不用在无关信息上浪费 token。第二能力可复用一个写好的“查 GKE 集群状态”skill可以在多个项目里反复用。第三维护成本低某个 skill 的逻辑变了只改它自己不影响其他部分。2.2 skills 的目录结构与元数据约定一个标准的 skill 通常是一个文件夹里面至少包含一个描述文件常见的是SKILL.md或skill.yaml和若干执行脚本。描述文件里会写清楚这个 skill 叫什么、干什么用、需要哪些参数、依赖什么环境、输出什么格式。Agent 读取这个描述后才知道什么时候该调用它。我自己的习惯是把每个 skill 做成自包含的目录结构大致如下skills/ gke-cluster-check/ SKILL.md check.sh requirements.txt playwright-e2e/ SKILL.md run_test.py package.jsonSKILL.md里我会写三段用途说明一句话讲清楚解决什么问题、输入参数每个参数的类型和含义、使用示例给 Agent 看的调用样例。这三段写清楚Agent 基本不会用错。很多人写 skill 只写脚本不写说明结果 Agent 不知道什么时候该用等于白做。2.3 为什么选 npx 作为分发和运行入口热搜词里 npx 出现频率很高这不是偶然。npx 是 Node.js 生态里的包执行工具它最大的好处是不需要全局安装就能运行某个包。对于 skills 来说这意味着你可以把 skill 发布成一个 npm 包用户用npx直接拉取并执行不用关心安装路径和版本冲突。举个例子如果有个 skill 叫agent-skill-gke用户只需要在 Agent 配置里写npx agent-skill-gke运行时会自动下载最新版本并执行。这对 skill 的传播非常友好——分享一个 skill 就像分享一个命令门槛极低。当然前提是你的 skill 逻辑要足够独立不能依赖一堆本地才有的文件。注意用 npx 分发 skill 时一定要在 package.json 里锁定依赖版本否则某天某个依赖升级导致行为变化用户那边会莫名其妙失败。我踩过这个坑一个测试 skill 因为底层库小版本升级断言逻辑变了结果误报了一周才被发现。3. 核心细节解析一个 skill 从编写到被调用的完整链路3.1 描述文件怎么写才让 Agent 不迷惑描述文件是 Agent 理解 skill 的唯一入口写得好不好直接决定调用准确率。我的经验是用途说明要用“动词对象结果”的句式比如“检查 GKE 集群中所有节点的就绪状态并返回异常节点列表”而不是“GKE 相关工具”。前者让 Agent 明确知道什么时候该用后者太模糊Agent 可能在该用的时候想不起来。输入参数部分每个参数都要写清楚是否必填、默认值是什么、取值范围。比如一个查询集群的 skillcluster_name必填region可选默认us-central1timeout可选默认 30 秒。这些信息写全Agent 在调用时才能正确填充参数减少来回确认。使用示例部分我通常会放两到三个不同场景的调用样例包括正常情况和边界情况。这相当于给 Agent 做 few-shot 示范实测能明显提升首次调用成功率。3.2 执行脚本的健壮性设计skill 的执行脚本和普通脚本最大的区别是它会被 Agent 自动调用没有人工干预所以必须自己处理异常。我见过太多 skill 脚本一遇到网络抖动就抛异常Agent 拿到一堆报错信息也不知道怎么办整个任务就卡住了。我的做法是在脚本里做三层防护。第一层参数校验入口处检查必填参数是否存在、格式是否正确不合法直接返回结构化错误。第二层超时和重试对外部调用设置合理超时对可重试的错误做有限次重试。第三层输出规范化无论成功失败都返回统一格式的 JSON包含status、data、error三个字段。这样 Agent 拿到结果后能稳定解析不会因为输出格式变化而懵掉。import json import sys def main(): try: params json.loads(sys.argv[1]) cluster params.get(cluster_name) if not cluster: print(json.dumps({status: error, error: cluster_name is required})) return # 实际逻辑 result check_cluster(cluster) print(json.dumps({status: ok, data: result})) except Exception as e: print(json.dumps({status: error, error: str(e)})) if __name__ __main__: main()这段模板我用了很多次核心就是永远给 Agent 一个可解析的返回哪怕出错也要出错得规规矩矩。3.3 依赖管理别让环境问题毁掉一个 skillskill 的依赖分两类系统级依赖和语言级依赖。系统级比如gcloud、kubectl、playwright的浏览器二进制语言级比如 Python 的requests、Node 的axios。这两类都要在描述文件里声明清楚并且提供安装脚本或说明。热搜词里有npx playwright install失败这就是典型的依赖问题。Playwright 需要下载浏览器二进制网络不好或者权限不足就会失败。我的处理方式是在 skill 里加一个setup.sh先检测依赖是否存在不存在就自动安装安装失败给出明确提示。同时在SKILL.md里写明“首次使用需要运行 setup.sh”让用户有心理预期。提示依赖尽量用固定版本号不要用latest。我有个 skill 因为用了latest的某个 CLI 工具工具升级后参数变了skill 直接失效排查了半天才发现是版本问题。3.4 权限与安全边界Agent 自动调用 skill 意味着它会以你的身份执行操作所以权限控制必须提前想清楚。我的原则是最小权限一个只读查询的 skill绝不给写权限一个只操作某个命名空间的 skill绝不给集群管理员权限。具体做法上我会在 skill 描述里标注所需权限等级并在脚本里做二次校验。比如一个删除资源的 skill脚本入口会检查环境变量ALLOW_DESTRUCTIVE是否为true不是就直接拒绝。这样即使 Agent 误判了场景也不会造成不可逆的破坏。另外涉及敏感信息的 skill比如需要访问密钥的我会把密钥读取逻辑封装在脚本内部不通过参数传递避免密钥出现在 Agent 的上下文里。这一点很多人忽略但一旦上下文泄露密钥就跟着泄露了。4. 实操过程从零写一个 GKE 集群检查 skill4.1 环境准备与前置检查假设我们要写一个 skill功能是检查 GKE 集群里所有节点的状态返回异常节点列表。先确认本地环境需要gcloudCLI、kubectl、Python 3.8。检查命令很简单gcloud version kubectl version --client python3 --version如果gcloud没装去官方文档按系统装好并完成初始化。kubectl可以用gcloud components install kubectl装。这些前置步骤看起来琐碎但跳过它们后面一定报错。然后创建 skill 目录mkdir -p skills/gke-node-check cd skills/gke-node-check4.2 编写 SKILL.md 描述文件描述文件我写成这样# gke-node-check ## 用途 检查指定 GKE 集群中所有节点的就绪状态返回 NotReady 节点列表及原因。 ## 输入参数 - cluster_name (必填): 集群名称 - region (可选): 集群区域默认 us-central1 - project_id (必填): GCP 项目 ID ## 输出 JSON 格式包含 status 和 data 字段。data 里是异常节点数组。 ## 示例 输入: {cluster_name: prod-cluster, project_id: my-project} 输出: {status: ok, data: [{name: node-1, reason: DiskPressure}]}这个描述写完后Agent 就能理解什么时候该调用它、怎么传参、期待什么输出。4.3 实现检查脚本脚本逻辑分三步获取集群凭证、查询节点状态、筛选异常节点。核心代码如下import json import subprocess import sys def run_cmd(cmd): result subprocess.run(cmd, shellTrue, capture_outputTrue, textTrue, timeout60) return result.stdout, result.stderr, result.returncode def main(): params json.loads(sys.argv[1]) cluster params[cluster_name] project params[project_id] region params.get(region, us-central1) # 获取凭证 cred_cmd fgcloud container clusters get-credentials {cluster} --region {region} --project {project} _, err, code run_cmd(cred_cmd) if code ! 0: print(json.dumps({status: error, error: fget credentials failed: {err}})) return # 查询节点 out, err, code run_cmd(kubectl get nodes -o json) if code ! 0: print(json.dumps({status: error, error: fget nodes failed: {err}})) return nodes json.loads(out)[items] bad_nodes [] for node in nodes: for cond in node[status][conditions]: if cond[type] Ready and cond[status] ! True: bad_nodes.append({ name: node[metadata][name], reason: cond.get(reason, Unknown) }) print(json.dumps({status: ok, data: bad_nodes})) if __name__ __main__: main()这段代码的关键点是每一步都检查返回码任何一步失败都返回结构化错误不让异常直接抛给 Agent。4.4 本地测试与参数验证写完脚本先本地测。构造一个参数 JSONpython3 check.py {cluster_name: test-cluster, project_id: my-project}如果集群不存在应该返回get credentials failed的错误。如果集群存在但没有异常节点返回空数组。测试时要把正常、异常、边界三种情况都覆盖到。我一般会故意传一个不存在的集群名确认错误处理路径是通的。测试通过后把 skill 目录放到 Agent 能扫描到的位置。不同平台路径不同常见的是项目根目录下的skills/或者用户目录下的.agent/skills/。放好后重启 Agent 或触发一次 skill 列表刷新确认新 skill 被识别。4.5 用 npx 打包分发如果想让别人也能用可以把 skill 发布成 npm 包。在目录下初始化 package.json{ name: agent-skill-gke-node-check, version: 1.0.0, bin: { gke-node-check: ./check.py }, dependencies: {} }然后npm publish。别人用的时候在 Agent 配置里写npx agent-skill-gke-node-check运行时自动拉取执行。注意 Python 脚本作为 bin 需要加 shebang 并赋予执行权限否则 npx 调用会失败。注意npx 默认会检查缓存如果发布了新版本但用户那边还是旧行为让用户加--yes或者清一下 npx 缓存。这个坑我在更新 skill 时遇到过明明发了新版用户反馈还是老逻辑查了半天是缓存问题。5. 常见问题与排查技巧实录5.1 skill 不被识别或加载失败最常见的原因是描述文件格式不对或路径不对。先确认 Agent 扫描的目录是哪个把 skill 放对位置。然后检查描述文件是否有语法错误YAML 对缩进敏感多一个空格都可能解析失败。如果是 Markdown 格式确认标题层级和字段名符合平台约定。另一个原因是 skill 名称冲突。如果两个 skill 同名Agent 可能只加载其中一个。我的做法是给 skill 名加前缀比如myorg-gke-check避免和别人的冲突。5.2 脚本执行超时或卡死Agent 调用 skill 通常有超时限制脚本跑太久会被强制中断。排查时先看是不是外部命令卡住了比如gcloud在等认证、kubectl在等集群响应。给每个外部调用加超时参数Python 的subprocess.run用timeoutShell 用timeout命令。如果确实是任务本身耗时长考虑把 skill 改成异步模式脚本先返回一个任务 IDAgent 后续用另一个 skill 查询结果。这样不会阻塞主流程。5.3 依赖缺失导致运行失败npx playwright install失败这类问题根源通常是网络或权限。排查步骤先手动运行安装命令看报错信息如果是网络问题配置镜像源或代理注意这里指正常的包管理镜像不是其他用途如果是权限问题检查目录写权限。安装成功后把依赖固化到 skill 的 setup 脚本里下次自动处理。5.4 输出格式不符合预期Agent 解析 skill 输出时如果格式和描述文件里写的不一致就会解析失败。排查时先看脚本实际输出再对照描述文件里的输出说明。常见问题是脚本在出错时输出了非 JSON 内容比如 Python 的 traceback 直接打到 stdout。解决办法是用 try-except 包住主逻辑确保任何情况下 stdout 只有 JSON。下面这张表是我整理的高频问题速查问题现象可能原因排查动作解决方式skill 不加载路径错误/格式错误检查目录和描述文件放对位置修正格式调用超时外部命令卡住手动运行看卡在哪加超时改异步依赖安装失败网络/权限手动装看报错配镜像改权限输出解析失败非 JSON 输出看实际 stdout统一 JSON 格式权限不足凭证过期/角色不够检查认证状态重新认证补角色5.5 独家避坑经验第一个坑不要在 skill 里硬编码路径。我早期写 skill 用了绝对路径换台机器就失效。后来全部改成相对路径或环境变量可移植性好了很多。第二个坑skill 的日志不要打到 stdout。Agent 把 stdout 当结果解析日志混进去就解析失败。日志统一打到 stderr或者写到文件里。第三个坑版本更新要写 changelog。skill 被多个项目引用时改了行为不通知下游会莫名其妙失败。我在每个 skill 目录里放一个CHANGELOG.md改了什么、影响什么写清楚用的人心里有数。第四个坑测试用例要跟着 skill 一起走。没有测试的 skill 不敢改改了不知道会不会坏。我习惯在 skill 目录里放一个test.sh跑一遍核心路径改完先跑测试再发布。这些经验没有一条是从文档里看来的全是实际用的时候踩出来的。skill 这东西写出来能用只是第一步能稳定用、能放心改才是真正省时间的阶段。
返回列表