ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

awesome-claude-skills 深度解析:Agent Skills 与 MCP 协同开发实战

awesome-claude-skills 深度解析:Agent Skills 与 MCP 协同开发实战

1. 从“awesome-claude-skills”说起:这个仓库到底解决了什么问题

第一次看到awesome-claude-skills这个标题,很多人会下意识以为它又是一个“资源导航”式的清单仓库——毕竟awesome-xxx这个命名习惯在开源圈已经流行了快十年,从awesome-python到awesome-mcp-servers,几乎每个热门技术方向都有一个对应的 awesome 列表。但如果你真的把这类仓库当成“收藏夹”来用,那就完全跑偏了。awesome-claude-skills的核心价值不在于“收集了多少条链接”,而在于它把 Claude 生态里最稀缺的东西——可复用的技能(Skills)定义——给系统化地整理了出来。

我大概是从去年年底开始密集接触 Claude Code 和 Agent Skills 这套体系的。当时最直观的感受是:模型能力本身已经足够强,真正卡住效率的是“每次都要重新告诉它怎么做”。比如你让 Claude 帮你写一个符合团队规范的 React 组件,第一次要解释目录结构、命名约定、状态管理选型;第二次换个项目,又得从头讲一遍。这种重复劳动在awesome-claude-skills出现之前,基本靠个人维护的 prompt 片段或者零散的 gist 来解决,既不可移植,也没法版本管理。

awesome-claude-skills做的事情,本质上是把“技能”从“提示词”里抽离出来,变成一种有明确边界、有输入输出约定、可被 Agent 自动发现和调用的模块。它解决的不是“模型会不会做”的问题,而是“模型知不知道在这个项目里该怎么做”的问题。适合谁来参考?三类人最应该认真看:一是已经在用 Claude Code 做日常开发、但还在靠复制粘贴 prompt 提效的工程师;二是想把内部工具链封装成 Agent 可调用能力的平台团队;三是正在评估 MCP(Model Context Protocol)和 Skills 两条路线该怎么配合的技术负责人。

这里有个概念需要先厘清,因为热词里反复出现mcp和skills,很多人会把它们混为一谈。MCP 是一套协议,解决的是“Agent 怎么和外部工具、数据源通信”的问题,你可以把它理解成 Agent 世界的 USB 接口标准;而 Skills 更像是插件本身,它描述的是“这个能力是什么、什么时候该用、怎么用”。一个 Skill 可以内部调用 MCP Server,也可以完全不依赖 MCP,纯靠文件系统和脚本完成。awesome-claude-skills里收录的内容,大部分是后者——轻量、自包含、直接放进项目就能跑。

2. 拆解一个 Skill 的骨架:为什么它比 prompt 片段更靠谱

2.1 Skill 的目录结构与元数据约定

要理解awesome-claude-skills的价值,得先知道一个标准 Skill 长什么样。我拿自己项目里最常用的一个“API 路由生成器”Skill 举例,它的目录结构大致是这样:

skills/ api-route-generator/ SKILL.md scripts/ generate.py templates/ route.ts.hbs references/ conventions.md

核心是SKILL.md这个文件,它承担了“说明书 + 触发器”的双重角色。文件头部用 YAML frontmatter 声明元数据,正文部分才是给模型看的操作指南。一个典型的 frontmatter 长这样:

--- name: api-route-generator description: 当用户需要新增 REST API 路由时使用,自动生成符合项目约定的 controller、service 和类型定义 version: 1.2.0 tags: [backend, typescript, rest] ---

这里每个字段都有讲究。name必须是 kebab-case,因为 Agent 在内部索引时会用它做唯一键;description是最关键的一行,它直接决定了 Agent 在什么场景下会“想起”这个 Skill——写得太窄,该触发的时候不触发;写得太宽,又会污染上下文。我的经验是,description里一定要包含触发条件(“当用户需要……”)和产出物(“生成……文件”),这两者缺一不可。

正文部分通常分几块:适用场景、前置检查、执行步骤、输出格式、边界情况。awesome-claude-skills里质量高的条目,正文都写得像一份给新人的 SOP,而不是给模型的技术文档。这个区别很微妙但很重要——模型不需要你解释“什么是 REST”,它需要你告诉它“在这个仓库里,REST 路由必须放在src/routes/v1/下,文件名用复数形式”。

2.2 渐进式披露:Skill 如何避免撑爆上下文

这是 Skills 体系里我最欣赏的一个设计,也是它和“把一大段 prompt 塞进 system message”最本质的区别。Agent 在启动时并不会把所有 Skill 的全文都加载进上下文,它只加载每个 Skill 的name和description,形成一个轻量级的“技能索引”。只有当某个 Skill 被判定为相关时,才会把SKILL.md的正文读进来;如果正文里引用了references/下的文件,那些文件要等到真正需要时才加载。

这个机制叫渐进式披露(progressive disclosure),类比一下就是:你办公室里有一整面工具墙,你不需要记住每把螺丝刀的规格,只需要知道“拧螺丝去工具墙”;走到墙前看到标签,再决定拿哪一把;真正拿起来的时候才看它的扭矩参数。这种分层加载让一个项目里挂几十个 Skill 也不会把上下文窗口挤爆,这是纯 prompt 方案做不到的。

我在实际项目里做过对比:同样是把“数据库迁移规范”这件事交给 Agent,用一段 800 字的 prompt 常驻 system message,和用一个 Skill 按需加载,后者在长对话里的表现明显更稳。因为常驻 prompt 会随着对话轮次被反复“稀释”,而 Skill 是在需要的那一刻才注入,注意力更集中。

2.3 和 MCP 的分工:什么时候该写 Skill,什么时候该起 Server

热词里mcp出现频率极高,很多人一上来就想把所有能力都做成 MCP Server。我的建议是先用一个简单判断:这个能力需不需要访问 Agent 沙箱之外的东西?如果只是读写项目文件、跑本地脚本、按模板生成代码,那写 Skill 就够了,成本低、调试快、不需要起进程。如果是要连数据库、调内部 API、访问某个 SaaS 服务,那才考虑 MCP Server,因为 Skill 本身没有网络凭据管理能力。

awesome-claude-skills里有一批条目其实是“Skill + MCP”的组合拳:Skill 负责描述“什么时候用、参数怎么填”,MCP Server 负责“真正去执行”。这种分层的好处是,业务逻辑变更时只需要改 Server,触发逻辑变更时只需要改 Skill,两边解耦。我在一个内部数据平台项目里就是这么做的,Skill 文件不到 50 行,MCP Server 那边有完整的鉴权和重试逻辑,维护起来非常清爽。

3. 从零复现一个 Skill:完整实操流程与关键参数

3.1 环境准备与目录初始化

假设你现在有一个用 Claude Code 开发的前端项目,想加一个“组件脚手架”Skill。第一步是确认你的 Claude Code 版本支持 Skills——这个能力在不同版本里的入口位置不太一样,我踩过的坑是:早期版本需要手动在配置里开启,后来才默认可用。你可以先在项目根目录跑一次claude --version,然后在交互模式里输入/skills看看有没有列表输出,有就说明环境没问题。

目录初始化我习惯放在项目根的.claude/skills/下,这样和代码一起进版本控制,团队共享。如果你想让 Skill 对所有项目生效,可以放到用户级的~/.claude/skills/,但我不推荐这么做——Skill 往往和项目约定强绑定,全局挂载反而容易误触发。初始化命令很简单:

mkdir -p .claude/skills/component-scaffold/{scripts,templates,references} touch .claude/skills/component-scaffold/SKILL.md

这里有个细节:目录名和SKILL.md里的name字段最好保持一致,虽然系统不强制,但排查问题时能省很多事。我见过有人目录叫comp-gen、name 写component-generator,结果 Agent 日志里两个名字混着出现,定位半天。

3.2 编写 SKILL.md:description 的写法决定触发率

前面说过description是最关键的一行,这里展开讲怎么写。我总结了一个模板:

当用户[触发场景]时使用,[执行动作],产出[具体产物],遵循[关键约束]。

拿组件脚手架举例,我最终写的是:

description: 当用户需要新增 React 组件时使用,基于 templates/component.tsx.hbs 生成组件文件、样式文件和测试文件,遵循项目的目录分层和命名约定

对比一下我第一版写的“生成 React 组件”,差别很明显。第一版太泛,Agent 在用户只是问“React 组件怎么写”的时候也会触发,造成干扰;加上触发场景和产物之后,命中率明显提升。我实测下来,description控制在 60 到 120 个字符之间比较合适,太短信息不足,太长会被截断。

正文部分我一般按这个结构写:

## 适用场景 - 用户明确要求新增组件 - 用户提供了组件名和大致用途 ## 前置检查 1. 确认 src/components/ 目录存在 2. 读取 references/conventions.md 获取命名规范 ## 执行步骤 1. 解析用户提供的组件名,转换为 PascalCase 2. 运行 scripts/generate.py,传入组件名和类型参数 3. 检查生成的文件是否符合 templates 中的结构 ## 输出格式 - 列出所有新建文件的路径 - 提示用户需要手动补充的业务逻辑位置 ## 边界情况 - 组件名已存在时,中止并提示 - 用户未指定类型时,默认生成函数组件

注意“前置检查”这一块,很多人会忽略。它的作用是让 Agent 在执行前先验证环境,避免跑到一半失败。我在一个 monorepo 项目里就因为没写前置检查,Agent 在错误的子包里生成了文件,清理起来很麻烦。

3.3 脚本与模板:把确定性逻辑从模型手里拿走

Skill 里最容易被低估的是scripts/目录。我的原则是:凡是能用代码确定性完成的,就不要让模型去“生成”。比如组件名的 PascalCase 转换、文件路径拼接、模板变量替换,这些用 Python 或 Node 脚本几行就能搞定,交给模型反而可能出错。

generate.py我通常写成接收命令行参数的形式:

import sys import argparse from pathlib import Path from string import Template def to_pascal_case(name): return ''.join(word.capitalize() for word in name.replace('-', '_').split('_')) def main(): parser = argparse.ArgumentParser() parser.add_argument('--name', required=True) parser.add_argument('--type', default='function') args = parser.parse_args() component_name = to_pascal_case(args.name) template_path = Path(__file__).parent.parent / 'templates' / 'component.tsx.hbs' content = Template(template_path.read_text()).substitute( name=component_name, type=args.type ) out_dir = Path('src/components') / component_name out_dir.mkdir(parents=True, exist_ok=True) (out_dir / f'{component_name}.tsx').write_text(content) print(f'created: {out_dir / f"{component_name}.tsx"}') if __name__ == '__main__': main()

然后在SKILL.md里告诉 Agent 去调用这个脚本,而不是让它自己拼字符串。这样做的收益在团队协作里特别明显:新人不管用什么措辞描述需求,只要触发了这个 Skill,产出的文件结构就是一致的。

3.4 本地验证与调试技巧

Skill 写完不是就完事了,得验证它到底会不会被正确触发。我的调试流程分三步。第一步是静态检查,确认SKILL.md的 frontmatter 能被正确解析——YAML 缩进错一个空格就可能导致整个 Skill 被忽略,这个坑我踩过不止一次。第二步是触发测试,在 Claude Code 里输入几种不同措辞的请求,看日志里有没有加载这个 Skill。第三步是执行测试,完整跑一遍看产出物对不对。

触发测试有个小技巧:故意用模糊的说法,比如“帮我搞个新组件”,看它会不会触发;再用明确说法“用 component-scaffold 生成一个 Button 组件”,看它能不能被显式调用。两种都能命中,说明description写得比较到位。如果只有显式调用能命中,说明触发条件写得太窄,需要放宽。

提示:调试期间可以在SKILL.md正文开头临时加一行DEBUG: loaded at {timestamp},这样从输出里就能确认它到底有没有被加载。验证完记得删掉。

4. 常见问题与排查技巧实录

4.1 Skill 不触发:从 description 到索引的排查链路

这是被问得最多的问题。排查顺序我建议从外到内:先确认 Skill 目录位置对不对,.claude/skills/和~/.claude/skills/是两个不同的作用域,放错了就不会被扫到;再确认SKILL.md文件名大小写,必须是全大写,skill.md是不认的;然后检查 frontmatter 的 YAML 语法,用在线 YAML 校验器过一遍最稳妥;最后才是看description的措辞。

我遇到过一个特别隐蔽的案例:description里用了中文全角逗号,YAML 解析时把整行当成了字符串的一部分,导致 description 变成了一长串乱码,Agent 自然匹配不上。这种问题肉眼很难发现,建议写完用python -c "import yaml; print(yaml.safe_load(open('SKILL.md').read().split('---')[1]))"验证一下。

4.2 触发过于频繁:如何收窄 Skill 的适用范围

和上一条相反,有些 Skill 因为description写得太宽,在任何相关对话里都会被加载,反而干扰了正常交互。典型症状是:你只是随口问一句“这个组件怎么用”,Agent 却开始给你生成新组件。解决办法是在description里加限定词,比如把“当用户需要 React 组件时”改成“当用户明确要求新增 React 组件文件时”,加上“明确要求”“新增文件”这类动作词,能过滤掉大量误触发。

另一个手段是在正文的“适用场景”里写清楚不适用的情况。比如“如果用户只是询问组件用法,不要触发本 Skill”。模型对否定指令的遵循度比想象中好,前提是你得写出来。

4.3 脚本执行失败:路径与权限的坑

Skill 里的脚本执行失败,九成是路径问题。Agent 执行脚本时的工作目录不一定是项目根目录,所以脚本里所有路径都要用Path(__file__).parent这种相对脚本自身的方式推导,不能假设cwd。我在一个项目里就因为脚本里写了open('templates/xxx'),结果 Agent 从子目录调用时直接报文件不存在。

权限问题在类 Unix 系统上比较常见,scripts/下的.py或.sh文件如果没有执行权限,Agent 调用时会失败。保险做法是在SKILL.md里明确写调用命令,比如python scripts/generate.py,而不是./scripts/generate.py,这样就不依赖执行位。

4.4 常见问题速查表

症状可能原因排查动作
Skill 完全不触发目录位置错误确认在.claude/skills/下
Skill 完全不触发frontmatter 语法错误用 YAML 校验器检查
Skill 完全不触发文件名非全大写改为SKILL.md
触发过于频繁description 过宽加入动作限定词
脚本报文件不存在路径依赖 cwd改用__file__推导
脚本无权限执行缺少执行位改用解释器显式调用
产出物格式不对模板变量未替换检查 Template 占位符
多 Skill 冲突description 重叠收窄各自触发条件

4.5 几个我踩过的坑和对应心得

第一个坑是在 Skill 里写太多“教学”内容。我一开始把SKILL.md写成了技术教程,解释什么是函数组件、什么是 props,结果模型反而被这些无关信息带偏。后来我意识到,Skill 是给“已经会做这件事、只是不知道项目约定”的 Agent 看的,教学部分应该放在references/里按需加载,而不是塞进主文件。

第二个坑是版本管理没做好。Skill 改了之后,团队里有人还在用旧版本,产出物不一致。后来我在 frontmatter 里加了version字段,并且在SKILL.md正文开头写了一句“本 Skill 版本 X.Y,如与团队约定不符请先同步”。虽然不能强制,但至少能提醒。

第三个坑是过度依赖 Skill 处理复杂逻辑。有些流程分支特别多,写成 Skill 之后SKILL.md膨胀到几百行,维护成本比直接写代码还高。我的判断标准是:如果一个 Skill 的正文超过 150 行,就该考虑把部分逻辑下沉到脚本或 MCP Server 里了。

5. 把 Skills 用出复利:团队协作与生态扩展

5.1 团队共享 Skill 的组织方式

个人用 Skill 和团队用 Skill 是两回事。个人可以随意命名、随时改,团队就得考虑命名空间和归属。我的做法是在.claude/skills/下按领域分子目录,比如frontend/、backend/、infra/,每个子目录里放对应的 Skill。这样一眼就能看出哪些是前端约定、哪些是后端约定,新人上手时也容易找到该看哪些。

命名上我建议加团队前缀,比如fe-component-scaffold、be-api-route,避免和社区 Skill 撞名。awesome-claude-skills里收录的很多 Skill 名字都很通用,直接拿来用容易和自己的产生冲突,改个前缀是最省事的办法。

5.2 从 awesome 列表里挑选 Skill 的判断标准

awesome-claude-skills这类列表里条目很多,但不是每个都值得引入。我的筛选标准有三条:一看description是否具体,泛泛而谈的直接跳过;二看有没有scripts/或references/,纯文本的 Skill 往往深度不够;三看最近更新时间,超过半年没动的要谨慎,因为 Claude Code 的 Skill 规范还在演进。

引入外部 Skill 之前,我一定会先在一个隔离项目里跑一遍,确认它的产出物符合预期,再决定要不要进主仓库。直接复制到生产项目里,出问题的时候排查成本很高。

5.3 Skill 与 MCP、Codex 等生态的配合思路

热词里codex、mcp、skills经常一起出现,说明大家已经在思考多工具协同的问题。我的经验是:Skill 负责“项目内的约定和流程”,MCP 负责“跨系统的能力接入”,两者通过 Skill 正文里的调用指令衔接。比如一个“发布检查”Skill,正文里可以写“调用 MCP Server 的ci-status工具获取构建结果,然后按references/release-checklist.md逐项核对”。这样 Skill 保持轻量,重活交给 MCP。

至于 Codex 这类工具,它们和 Claude Code 在 Skill 概念上并不完全互通,但思路是相通的。我一般会把核心约定抽成独立的references/文件,两边各自写薄薄的 Skill 壳去引用同一份约定,这样约定变更时只需要改一处。这个模式在同时用多个 Agent 工具的团队里特别实用。

5.4 一个真实项目的落地记录

最后分享一个我最近落地的项目。团队要统一前端组件的创建流程,之前每个人写法都不一样,code review 时经常为目录结构吵架。我花了一个下午写了一个fe-component-scaffoldSkill,包含一个 40 行的 Python 脚本、两个 Handlebars 模板、一份 30 行的约定文档。上线两周后,新组件的目录结构一致性从大概六成提升到了接近百分之百,review 时关于结构的讨论基本消失了。

过程中最大的收获不是技术上的,而是意识到:Skill 的价值在于把“隐性约定”变成“显性资产”。以前这些约定散在几个老员工的脑子里,现在它们躺在版本控制里,可以被 review、被迭代、被新人直接继承。awesome-claude-skills这个仓库的意义也在这里——它不只是收集链接,而是在推动一种“把经验封装成可复用模块”的工作方式。我个人的体会是,与其花时间调更复杂的 prompt,不如先把项目里重复三次以上的操作抽成 Skill,收益来得更直接。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进