
“skills”这个词如果你最近泡在 AI 开发圈里大概率不是指“个人能力提升”而是指 AI Agent 领域里越来越火的一种玩法——给模型配置可复用的“技能包”。我曾经在一个项目里被反复 PK 的几轮长上下文折腾到怀疑人生后来靠一个精心设计过的 skills 目录把模型的回复质量、工具调用率、甚至 token 成本都拉到了新档位。今天这篇就不绕弯子直接聊聊我理解的 Skills 到底是什么、怎么设计一份真正能打的技能文件、以及我踩过哪些坑。这套内容既适合刚接触 Agent 开发的新手也适合那些已经用 Prompt 拼过几个自动化场景、却始终觉得“不够稳”的进阶玩家。看完你至少能搞清楚Skills 和普通 Prompt 的边界在哪、一份规范技能文件应该长什么样、如何让模型在正确时机自动调起技能而不是靠你每次手写要求。1. 理解 Skills 的核心本质1.1 Skills、Prompt 和工具调用到底有什么区别很多人第一次接触 Skills 时都会有一个困惑这玩意儿不就是把一段 Prompt 存成文件吗确实它本质上存的是文本指令但关键区别在于“结构”和“触发逻辑”。普通 Prompt 是对话开始前塞给模型的一段总纲它影响的是模型对整个会话的全局行为。比如你写“你是一个资深前端工程师回答要简洁”这段指令会伴随整场对话。但你让它“遇到需要算汇率的时候自动用某个公式”对不起普通 Prompt 不保证模型真的每一步都记得去用尤其是上下文一长最初指令的约束力就开始衰减。工具调用Function Calling则走的是另一条路模型先判断用户意图然后输出一个结构化的函数调用请求由程序去执行代码、查询数据库、调外部 API再把结果返回模型。它的优势是精确、可编程但劣势也很明显——维护成本高。每增加一个工具就要写对应的 schema、参数校验、错误处理十几个工具共存时模型的选择准确率反而会下降。Skills 是夹在两者之间的更优雅的方案。它把“特定任务的完整解决路径”打包成一个独立文件里面可以包含指令、示例、边界条件、甚至可以引用本地脚本。模型不是被迫始终遵守而是“按需加载”——当它判断当前任务匹配某个技能描述时才会主动启用该技能的内容。你完全可以这样理解Prompt 是给模型定的世界观Function Calling 是给程序定的 API而 Skills 是给模型准备的“操作手册”。模型不会天天翻手册但遇到对应业务场景时它知道去哪翻、翻到哪一页。1.2 为什么 Skills 能成为主流方案Skills 能在短短几个月里被主流 Agent 框架和各家大模型产品采纳背后有几个非常实在的原因。第一它解决了长上下文的指令衰减问题。传统方式下你只能把几十条规则全部塞进系统提示词但 Claude 等模型在超长上下文中对中后段指令的遵循率会明显下降而把规则按场景拆分成技能文件后模型每次只需读取当前任务对应的那几段内容命中率和执行准确率都稳定得多。实测中同样是执行代码审查任务技能加载前后的规则遵循度差异非常显著。第二它天然适配多场景复用。同一个模型实例可能今天写文案、明天审代码、后天做数据分析。如果在系统提示词里把所有能力都堆进去模型会“精神分裂”输出风格和回复质量在两个场景间互相干扰。用 Skills 做隔离后每个技能都是独立空间互不污染。第三它对团队协作非常友好。传统 Prompt 是聊天记录里的碎片没法版本管理、没法 review、没法测试。Skills 把技能做成目录文件天然适配 Git 管理和 CI 流程团队里谁改了什么一目了然。所以我个人的判断是Skills 并不是概念上的全新发明但它把过去 Prompt Engineering 里的最佳实践拆分、结构化、按需加载固化成了工程标准。这一点是它和普通 Prompt 最根本的区别。2. 设计一个高效的 Skills 文件2.1 设计规范SKILL.md 的结构拆解在很多主流框架里一个标准技能包的目录结构大概是这样的my-skill/ ├── SKILL.md ├── scripts/ │ ├── review.py │ └── format.py ├── assets/ │ ├── checklist.md │ └── prompt_templates/ └── reference/ └── docs/其中SKILL.md是技能的核心入口相当于这份“操作手册”的目录和正文。严格来说框架只要找到这个文件就能识别该技能并加载其元数据。其余目录是扩展用于存放脚本、参考文档和模板。一个合格的SKILL.md至少需要包含三块核心内容元数据区声明技能名称、描述、适用场景等信息这部分主要给模型“判断是否该使用”提供依据。正文指令区告诉模型在启用了这项技能之后具体要遵循哪些步骤、输出格式是什么、有哪些禁忌。示例区可选但强烈推荐给模型一两个输入输出对帮助它理解“这个技能到底期望什么样的结果”。下面是一个基于实际项目经验整理出的最小可用模板--- name: code-reviewer description: 对代码变更进行系统性审查识别逻辑错误、安全隐患与性能瓶颈。适用于 Pull Request 审查、代码走查场景。 category: engineering --- # 代码审查技能 执行任务时严格遵循以下流程 1. 理解变更范围识别本次 PR 的 diff 涉及哪些模块梳理改动的前置依赖。 2. 逐项检查按【正确性】-【安全性】-【性能】-【可维护性】的顺序依次审查。 3. 输出报告使用 Markdown 格式按严重程度分级Critical / Major / Minor输出问题清单。 ## 关键注意事项 - 优先指出会导致线上事故的逻辑错误而非风格问题。 - 若涉及密码、密钥等敏感信息标记为 Critical 并立即提示。 - 所有建议必须附上具体修改示例禁止给出空泛的“请优化”。 ## 示例 输入一段 Python 代码包含 SQL 字符串拼接。 输出识别 SQL 注入风险给出使用参数化查询的修改方案。不要小看这个结构。元数据是给模型“闻味儿”的正文是给模型“照做”的示例是给模型“对齐标准”的三样缺一不可。早期版本我写过只有一句描述的技能文件效果非常拉胯模型经常答非所问或者输出风格跑偏。2.2 元数据字段的选择与配置元数据是整个技能文件里“性价比”最高的部分因为模型判断“要不要用这个技能”时依赖的就是这一段里的描述信息。写得不好技能再优秀也没机会出场。主流框架里常用的字段有这几个字段名作用配置建议name技能的唯一标识简短、全小写、用连字符分隔建议控制在 3 个单词以内description描述技能用途、适用场景需包含“何时用”“解决什么问题”“关键输入特征”三个维度category技能分类按业务域划分如 engineering、writing、data-analysisversion版本号每次修改递增便于追踪回归allowed-tools允许使用的工具白名单限制模型调用范围防止走偏model适用的模型版本不同模型能力差异大必要时手动指定描述字段是最容易写废的。很多人会写成“用于代码审查”这种粒度对模型来说太模糊。合格的描述应该像这样description: 在用户提交 Pull Request 或要求检查代码片段时对变更内容进行正确性、安全性、性能与可维护性四个维度的系统性审查输出分级的问题清单与修复建议。这里的关键是把“触发条件”和“处理方式”都写进描述里。模型看到一段代码时它会拿当前场景和这段描述做匹配描述里明确写了“提交 PR”“检查代码片段”这类触发词命中率就会大幅提升。2.3 用 YAML 还是纯 Markdown常见格式取舍技能文件的格式选择会直接影响解析效率和兼容性。目前主流框架普遍支持两段式结构——文件头部的 YAML Front Matter 加正文 Markdown但不同框架对这两段内容的解析优先级不同。早期我习惯全写 Markdown把元数据也用标题来组织结果发现有些框架根本不解析这种结构导致技能库扫描时无法识别。后来切换到 YAML 头问题直接解决。但纯 YAML 也有局限。你不可能把完整的任务流程、边界规则、示例对话全部写进 YAML 结构里那样可读性会崩溃。我的建议是组合使用YAML Front Matter只放结构化元数据控制在 10 行以内。Markdown 正文放流程指令、注意事项、示例按需使用标题分割。另外有一个细节点值得注意YAML 区域必须放在文件最顶部且用---包围这是所有主流解析器的通用约定。之前我把 YAML 放在 Markdown 标题之后部分框架直接识别失败排查了半天才发现是位置问题。3. 从零复刻一个真实可用的技能代码审查技能3.1 需求定义与技能边界理论讲再多不如手把手做一个。我以“代码审查技能code-reviewer”为例演示完整落地过程。第一步是定义需求边界。我的团队经常有 PR 需要 review但模型默认状态下对代码的理解比较泛不给约束时它会纠结于变量命名、注释风格这类噪音真正严重的并发问题反而漏掉。所以我需要它聚焦在四个维度正确性、安全性、性能、可维护性。同时要明确不做什么不做代码格式化建议那是 lint 工具的活不做架构级重构规划超出单次 PR 的审查范围不逐行解释代码除非用户主动要求。这个边界设定非常重要。没有边界的技能会让模型过度发挥你以为它在帮你 review实际上它在写一篇代码讲解作文看得人血压飙升。3.2 核心配置与 Prompt 编写基于上面的需求我的SKILL.md正文指令部分做了更细的设计。核心思路是把“审查维度”和“输出标准”拆开写让模型先按维度过一遍代码再按标准输出结果。审查维度部分## 审查维度 1. 正确性Critical 优先 - 逻辑分支是否有遗漏是否存在除零、空指针、数组越界等运行时错误 - 并发场景下是否有竞态条件或死锁风险 - 错误处理是否合理异常被吞掉还是被正确传递 2. 安全性 - 是否存在注入风险SQL、命令、XSS - 敏感信息是否泄露硬编码密钥、明文密码是否出现 - 输入校验是否缺失 3. 性能 - 是否有明显的 N1 查询、无效循环、大数据量下性能退化 - 是否需要缓存缓存失效策略是否合理 4. 可维护性 - 命名是否清晰函数是否过长、职责是否单一 - 是否重复代码过多 - 是否有明显的可读性问题输出标准部分则把它压缩为固定格式避免模型自由发挥## 输出格式 使用如下模板输出审查结果 ### 变更概览 - 审查范围... - 风险等级Critical / Major / Minor ### 问题清单 | 严重级 | 文件位置 | 问题描述 | 修复建议 | | --- | --- | --- | --- | | Critical | src/main.py:42 | ... | ... | ### 修复优先级建议 按风险等级从高到低列出前 3 个必须修复的问题。这一步对实际效果的影响非常大。默认状态下模型输出的审查报告经常是一大段散文你得自己从中提炼问题清单。有了固定模板后输出直接变成可读性极高的结构化报告后续接自动化流程也方便得多。3.3 附加脚本与资源组织如果技能只停留在“给模型讲规则”的层面那它还是个高级 Prompt。真正的 Skills 可以在此基础上扩展出半自动化的能力让模型自己决定“什么时候需要调用外部脚本”。我的代码审查技能里放了一个辅助脚本scripts/analyze.py它的用途是快速定位代码中的高风险模式#!/usr/bin/env python3 轻量代码风险扫描脚本查找常见风险模式。 用法: python analyze.py 文件路径 import re import sys RISK_PATTERNS [ (reval\s*\(, Critical, 使用 eval 执行动态代码存在代码注入风险), (rexec\s*\(, Critical, 使用 exec 执行动态代码存在代码注入风险), (rpassword\s*\s*[\][^\][\], Major, 检测到硬编码密码), (rSELECT\s.*\sFROM.*WHERE\s\w\s*\s*[\]\s*\, Critical, 可疑 SQL 字符串拼接), ] def scan_file(path: str) - list: findings [] try: with open(path, r, encodingutf-8) as f: for lineno, line in enumerate(f, 1): for pattern, level, message in RISK_PATTERNS: if re.search(pattern, line, re.IGNORECASE): findings.append({line: lineno, level: level, message: message}) except FileNotFoundError: findings.append({line: 0, level: Major, message: f文件 {path} 不存在}) return findings if __name__ __main__: target sys.argv[1] if len(sys.argv) 1 else . if target.endswith(.py) or target.endswith(.js) or target.endswith(.ts): results scan_file(target) for item in results: print(f{item[level]}: line {item[line]} - {item[message]}) else: print(请提供代码文件路径支持 .py/.js/.ts 后缀)在 SKILL.md 中我加入了这样一段说明## 工具调用策略 当审查对象的代码量超过 200 行时优先运行 python scripts/analyze.py 目标文件 进行初步风险扫描将扫描结果作为审查线索当代码量少于 200 行时直接静态分析无需调用脚本。这里有个很重要的经验不要让模型无脑调脚本。脚本是辅助手段真正的问题判断还是靠模型本身的能力。给模型一个“何时用工具”的条件阈值比让它每次都用工具高效得多。4. Skills 的加载机制与调试技巧4.1 模型是如何“看到”你的技能文件的很多人想知道模型在对话时是怎么感知到技能文件存在的。这个问题不同框架的实现方式不同但大体的机制可以归纳为两步。第一步是技能扫描。框架启动时会扫描配置的技能目录读取每个SKILL.md的 YAML 元数据注册到一个可用的技能索引里。此时模型并没有真正看到技能全文它看到的只是每个技能的名称、描述、分类这种“索引信息”。第二步是触发决策。用户发出消息后框架会将用户输入和所有技能的描述信息一起放入模型上下文通常是压缩后的摘要形态由模型判断当前任务该启用哪个技能。一旦命中框架才把对应技能文件的完整内容注入后续上下文。理解这个机制对调试很有帮助。你会发现很多“技能不被调用”的问题根源不在正文写得好不好而是描述信息没有让模型在第一步就产生命中。模型连技能全文都没看到内容再优秀也白搭。另外我在实践中发现一个细节技能的“索引信息”往往比正文更早、更频繁出现在上下文中。因此描述字段里的关键词覆盖范围要尽量贴近真实业务语言。比如我的代码审查技能描述里同时写了“Pull Request”“代码走查”“Code Review”命中率就比只写“代码审查”高得多。4.2 技能命中率优化写好描述是关键既然命中靠描述这块就值得花心思打磨。我总结了几条非常实用、也是反复试出来的优化思路。第一描述里要有“动词 宾语 场景”结构。不要只写“代码审查功能”要写“审查用户提交的代码变更识别潜在错误并输出修复建议”。重点是让模型看到一个行为动词能够把它和用户请求的动作配对。第二多写几个同义触发词。同一个技能用户可能有完全不同的说法。比如代码审查技能有人会说“帮我看看这个 PR”有人会说“检查一下这段代码有没有问题”还有人会说“Code Review”。把这些词都纳入描述模型配对的成功率高很多。这个操作不需要堆砌而是自然融入描述句式中。第三控制描述长度。太短则语义不明确太长则稀释关键词的密度。经验值在 50~150 字之间最合适。超出这个范围反而容易出现模型“理解了但匹配不精确”的情况。第四描述中写明“不做的事情”。这个方法比较反直觉但效果非常好。例如“适用于 PR 审查场景不处理代码格式化与架构重构建议”。模型在决策时会把排除条件也纳入考虑当用户请求是重构方案时它会理智地转向其他技能或默认能力。4.3 调试技能时最容易踩的三个坑技能文件不是写一次就完事的调试过程才是让技能真正变可用的关键。我分享一下自己踩得最深的三次坑。第一个坑技能文件太大导致上下文膨胀。我最初写的一个技能文件洋洋洒洒 2000 多字把各种场景都铺满了。结果每次触发时框架会把全文注入上下文那一次会话还没干活token 已经被吃掉一大截。更严重的是过长的指令会让模型在上下文中迷失重点反而对一些矛盾指令无所适从。后来我把长文档拆分到reference/目录SKILL.md 保持精简状态只在需要时让模型去读参考文档。第二个坑过度依赖示例导致“例样化”。我给技能写了一大堆示例想让模型模仿得更准。结果模型在遇到类似但不完全相同的输入时开始机械套用示例模板产出的报告格式好看但内容对不上问题。后来我把示例缩减为每组维度一条并且明确标注“示例仅展示输出格式请勿直接复制内容”问题才缓解。第三个坑多个技能描述语义重叠。当技能库里有“代码审查”和“代码安全扫描”两个技能时模型经常分不清该调哪个。后来我把语义边界写清楚安全扫描只关注漏洞与敏感信息代码审查关注全面质量。同时把重叠场景的处理逻辑写成“若两个技能都匹配优先使用代码安全扫描。”这种显式的优先级说明能有效减少随机决策。5. 常见问题与经验速查表5.1 技能失效或没被调起怎么办这是我被问得最多的一个问题。我的排查顺序通常是这样检查元数据格式YAML 区的字段名是否拼写正确---分隔线是否完整用解析器跑一遍验证文件是否合法。检查描述信息把描述和测试输入放一起看是否自然包含触发词。如果描述里写的是“分析代码质量”而用户说“检查这段代码”模型确实不容易自动匹配。检查框架配置有些框架需要手动开启技能目录的环境变量或者在配置文件中显式声明技能路径。漏掉这一步技能永远不被扫描。做最小复现临时写一个只有 5 行描述的最小技能文件测试框架是否能正常识别。如果最小技能正常、你的完整技能异常那问题基本出在文件结构或内容矛盾上。强制加载排查某些框架支持手动指定“强制启用某技能”绕过自动决策直接注入技能内容。用这个方式能快速定位是“触发问题”还是“内容问题”。这里补充一个细节不同框架对“技能被调起”的定义不太一样。有的框架把技能视为一次性注入技能内容会在整轮对话持续存在有的框架则在每轮都重新决策技能可能在对话中途“被关闭”。如果你的技能在前两轮正常、后面突然失效可能是后一种机制在起作用。此时需要在正文中写明“执行本技能相关的指令时请持续遵守本文件规则直到任务完成”能提高技能的跨轮次保持率。5.2 多个技能冲突时如何取舍技能库一大冲突避无可避。我的经验是提前做好两级预案。第一级设计期规避冲突。在写第二个技能之前先搜索现有技能库里的描述信息如果发现语义重叠要么合并要么在描述里显式划清边界。比如“数据清洗”和“数据可视化”两个技能看似不冲突但当用户说“把这份数据做成图表并清洗异常值”时模型就可能纠结。此时在“数据清洗”技能描述里加上“仅处理数据预处理环节不涉及图表生成”就能有效拆解。第二级运行期指定优先级。如果冲突还是发生了在技能正文里追加优先级说明。例如## 优先级 本技能与数据分析技能存在部分重叠。当用户请求同时涉及数据清洗与可视化时优先执行本技能的数据清洗流程数据可视化由专门技能处理。这里有一个有意思的现象模型往往更信任描述中“具体而明确的限制”而不是泛泛的“综合判断”。写得越具体的优先级规则执行得越稳定。5.3 我实际使用中的几个技巧和教训最后聊几个在长期使用中沉淀出来的小细节都是常规文档里不会写的东西。一是技能的版本号真的有用。一开始我嫌麻烦每次改动都不跟版本结果线上模型表现异常时根本没法定位是技能变更导致的还是模型升级导致的。后来我强制要求每次改动必须递增 version并在变更记录里写明改动原因。现在定位问题就变得特别快。二是别把所有逻辑都塞进 SKILL.md 里。技能文件的定位应该是“入口指令 关键规则”而不是百科全书。复杂知识、完整示例、详细说明文档放到reference/子目录里让模型按需阅读是控制上下文开销的好办法。我在实际使用中养成了一个习惯如果 SKILL.md 正文超过 800 字就会问自己“哪些内容可以外置”。三是技能的“命名”值得多想一步。技能名是给框架用的也是给模型“理解世界”用的。如果你给它起一个特别抽象的名字比如sync-analyzer模型在判断是否使用时对这个词的语义理解远不如一个名字叫>