
1. 为什么“Skill”才是 AI Agent 真正落地的关键1.1 从“会聊天”到“会干活”的鸿沟过去一年我接触过不少 AI Agent 项目从个人开发者的小工具到团队内部的自动化流程一个很普遍的现象是模型能力越来越强但真正能稳定完成一件具体事情的 Agent 却不多。问题往往不出在模型本身而是出在“技能”这一层。大模型像一个知识渊博但没上过手的顾问你问它什么它都能聊但让它按固定格式写文件、按固定流程调接口、按固定规范产出内容它就开始飘了。这就是 Skill 存在的意义。你可以把 Skill 理解成 Agent 的“肌肉记忆”——不是每次都要重新思考怎么做而是把一套已经验证过的操作流程固化下来需要的时候直接调用。它和传统的函数调用、工具调用有重叠但更强调“可复用、可组合、可版本管理”。一个 Skill 通常包含三部分触发条件什么时候用、执行逻辑具体怎么做、输出规范产出长什么样。我见过太多人把 Agent 做成了“万能聊天框”用户问什么它答什么但一旦要求它完成多步骤任务比如“帮我把这份会议记录整理成结构化文档并生成待办清单”就开始胡言乱语。根本原因就是缺少 Skill 层。模型负责理解和决策Skill 负责执行和兜底两者分工明确Agent 才能真正下地干活。1.2 Skill 和普通 Prompt、工具调用的区别很多人会问我写个详细的 Prompt 不就行了吗为什么还要搞 Skill这个问题我早期也纠结过。实测下来Prompt 适合一次性、探索性的任务而 Skill 适合重复性、规范性强的任务。区别主要体现在三个方面。第一是稳定性。Prompt 每次都要重新解释一遍规则模型可能这次理解对了下次就偏了。Skill 把规则写死在文件里每次调用都是同一套逻辑输出一致性高很多。第二是可维护性。Prompt 散落在代码各处改一处可能影响另一处。Skill 是独立文件改哪个改哪个还能做版本管理。第三是可组合性。一个 Skill 可以调用另一个 Skill像搭积木一样组合出复杂流程而 Prompt 很难做到这一点。至于工具调用它更偏向“原子操作”比如查天气、发邮件、读文件。Skill 则是“复合操作”一个 Skill 内部可能调用多个工具还包含条件判断、循环、错误处理。打个比方工具调用是螺丝刀、扳手Skill 是“换轮胎”这个完整流程告诉你先松螺丝、再顶千斤顶、再拆轮胎、再装新胎、再拧紧。1.3 一个 Skill 文件到底长什么样目前社区里比较通用的做法是用 Markdown 加 YAML front matter 来定义 Skill。Markdown 负责写人类可读的说明和步骤YAML 负责写机器可解析的元数据。这种格式的好处是人看着舒服机器也能读还能直接用 Git 管理。一个典型的 Skill 文件结构大概是这样--- name: meeting-notes-to-todo description: 将会议记录整理成结构化文档并提取待办事项 version: 1.2.0 trigger: - 整理会议记录 - 生成待办 inputs: - name: raw_notes type: string required: true outputs: - name: structured_doc type: markdown - name: todo_list type: json --- ## 执行步骤 1. 读取原始会议记录识别参会人、议题、结论 2. 按议题分段每段包含讨论要点和决议 3. 从决议中提取待办事项标注负责人和截止时间 4. 输出结构化文档和待办 JSON这个文件里YAML 部分定义了 Skill 的名字、描述、版本、触发词、输入输出。Markdown 部分写具体执行步骤。Agent 在运行时会先读 YAML 判断是否触发再按 Markdown 步骤执行。这种设计的好处是非技术人员也能看懂和修改 Markdown 部分而 YAML 部分由开发维护分工清晰。注意YAML 里的缩进必须用空格不能用 Tab这是新手最容易踩的坑。我见过好几次因为一个 Tab 导致整个 Skill 加载失败排查半天才发现是缩进问题。2. 从零手撸一个 Skill 的完整过程2.1 先想清楚这个 Skill 解决什么问题动手写之前先问自己三个问题这个任务是不是会重复做步骤是不是相对固定输出是不是有明确规范如果三个都是“是”那就值得做成 Skill。如果只是偶尔做一次或者每次流程都不一样那写个 Prompt 就够了没必要过度工程。我拿一个真实场景举例把网页内容保存成 Markdown 文件。这个任务我每周要做十几次步骤固定——打开网页、提取正文、转换格式、保存文件。每次手动做很烦用 Prompt 让模型做又不稳定有时候漏内容有时候格式乱。所以我就把它做成了一个 Skill。2.2 拆解步骤把“人怎么做”翻译成“机器怎么做”写 Skill 的第一步不是写代码而是把你手动做这件事的步骤一步步写下来。注意要写到“傻瓜级”详细因为机器不会脑补。比如“提取正文”这一步人知道要跳过导航栏和广告但机器不知道你得明确告诉它忽略 header、footer、sidebar 标签内的内容只保留 article 或 main 标签内的文本。我当时的拆解是这样的接收一个 URL 参数请求该 URL获取 HTML解析 HTML定位正文区域将正文转换为 Markdown 格式处理图片链接改为本地相对路径生成文件名保存到指定目录返回保存路径和文件大小每一步都要考虑异常情况。比如请求失败怎么办正文区域找不到怎么办图片下载失败怎么办这些都要在 Skill 里写清楚处理逻辑不能留给模型临场发挥。2.3 写 YAML元数据是 Skill 的“身份证”YAML 部分决定了 Agent 能不能正确识别和调用这个 Skill。几个关键字段必须写清楚。name要唯一最好用英文小写加连字符比如webpage-to-markdown。description要一句话说清楚这个 Skill 干什么因为 Agent 在决定用哪个 Skill 时主要看的就是 description。trigger是触发词列表用户说的话里包含这些词时Agent 就会考虑调用这个 Skill。inputs和outputs定义输入输出格式方便 Agent 做参数校验和结果处理。这里有个经验description不要写得太泛比如“处理网页”就太泛了Agent 不知道你到底要处理什么。要写成“将网页正文提取并保存为 Markdown 文件”这样 Agent 一看就知道什么时候该用。name: webpage-to-markdown description: 将网页正文提取并保存为 Markdown 文件 version: 1.0.0 trigger: - 保存网页 - 网页转 Markdown - 抓取网页内容 inputs: - name: url type: string required: true description: 要抓取的网页地址 - name: output_dir type: string required: false default: ./output outputs: - name: file_path type: string - name: file_size type: integer2.4 写 Markdown执行逻辑要“无脑可执行”Markdown 部分写执行步骤原则是每一步都要具体到不需要思考就能执行。不要写“提取正文”要写“使用 readability 算法提取正文如果失败则回退到提取 article 标签内容”。不要写“保存文件”要写“以 URL 的 slug 作为文件名保存到 output_dir 目录如果文件已存在则覆盖”。我习惯在 Markdown 里用有序列表写步骤用引用块写注意事项用代码块写具体命令或参数。这样结构清晰Agent 解析起来也容易。## 执行步骤 1. 验证 url 参数是否合法不合法则返回错误 2. 使用 HTTP 客户端请求 url超时时间设为 30 秒 3. 如果响应状态码不是 200返回错误并附上状态码 4. 使用 readability 算法提取正文 HTML 5. 如果提取失败回退到查找 article 或 main 标签 6. 将正文 HTML 转换为 Markdown 7. 遍历 Markdown 中的图片链接下载图片到本地 8. 将图片链接替换为本地相对路径 9. 生成文件名取 url 的最后一段路径去掉特殊字符 10. 保存文件到 output_dir返回文件路径和大小 注意如果 output_dir 不存在需要先创建目录 注意图片下载失败时保留原始链接不要中断流程2.5 测试别等上线了才发现问题Skill 写完一定要测试而且要用真实场景测试。我一般会准备三类测试用例正常情况、边界情况、异常情况。正常情况就是标准网页边界情况比如超长网页、纯图片网页、需要登录的网页异常情况比如 URL 不存在、网络超时、磁盘空间不足。测试的时候要观察 Agent 的实际行为看它是不是按你写的步骤执行。有时候 Agent 会“自作聪明”跳过某些步骤或者把步骤顺序搞乱。这时候就要在 Markdown 里加更强的约束比如“必须按顺序执行以下步骤不得跳过”。我踩过的一个坑是Skill 里写了“如果提取失败则回退”但 Agent 看到“回退”两个字就以为可以随便选一种方式结果两种都试了但都没成功。后来我把逻辑改成“先尝试 A如果 A 失败则尝试 B如果 B 也失败则返回错误”Agent 就老实了。3. 让 Skill 自动生成从手撸到“肌肉记忆”的进化3.1 为什么要自动生成 Skill手撸 Skill 有个问题写一个两个还行写十个二十个就累了。而且很多 Skill 的结构是相似的比如“读取文件-处理内容-保存文件”这个模式在文档转换、数据清洗、格式整理等场景里反复出现。这时候就可以考虑让 Agent 自己生成 Skill。自动生成 Skill 的核心思路是让 Agent 观察你手动执行任务的过程提取出步骤和参数自动生成 Skill 文件。这有点像“录制宏”但比宏更智能因为它能理解语义不是简单记录鼠标键盘操作。3.2 自动生成的基本流程我目前用的方案分四步录制、解析、生成、验证。录制阶段Agent 以“观察者”模式运行记录你执行任务时的所有操作包括打开的文件、调用的工具、输入的参数、产生的中间结果。解析阶段Agent 分析这些操作识别出哪些是核心步骤哪些是辅助操作提取出输入输出和参数。生成阶段按 Skill 模板生成 YAML 和 Markdown。验证阶段用生成的 Skill 跑一遍同样的任务对比结果是否一致。这个流程里最难的是解析阶段因为人的操作往往有冗余。比如你可能打开了错误的文件又关掉或者试了几个参数才找到对的。Agent 需要判断哪些操作是必要的哪些是试错。我的做法是让 Agent 关注“最终成功的那次操作序列”忽略之前的失败尝试。3.3 生成 Skill 的 Prompt 设计让 Agent 生成 SkillPrompt 很关键。我试过几种写法最后稳定下来的版本大概是这样你是一个 Skill 生成器。请根据以下操作记录生成一个 Skill 文件。 操作记录 {operations} 要求 1. 提取核心步骤忽略试错和冗余操作 2. 识别输入参数和输出结果 3. 用 YAML front matter 写元数据 4. 用 Markdown 写执行步骤 5. 每个步骤要具体可执行不要模糊描述 6. 标注可能的异常情况和处理方式 输出格式 --- name: ... description: ... ... --- ## 执行步骤 ...这个 Prompt 的关键点是“忽略试错和冗余操作”不加这一句Agent 会把所有操作都写进去生成的 Skill 又臭又长。另外“每个步骤要具体可执行”也很重要不然 Agent 会写“处理数据”这种废话。3.4 自动生成的质量控制自动生成的 Skill 不能直接用必须经过人工审核。我一般检查三个方面步骤是否完整、参数是否正确、异常处理是否到位。步骤完整性看有没有漏掉关键环节参数正确性看类型和默认值对不对异常处理看有没有考虑常见错误。有个技巧是让 Agent 自己生成测试用例。在生成 Skill 后追加一个 Prompt“请为这个 Skill 生成三个测试用例分别覆盖正常情况、边界情况和异常情况。”这样你审核的时候就有参考不用自己想测试场景。提示自动生成的 Skill 建议先放在draft目录人工审核通过后再移到skills目录。这样避免未验证的 Skill 被误调用。4. 实战中踩过的坑和排查技巧4.1 YAML 解析失败八成是缩进或特殊字符YAML 对格式要求很严缩进错了、冒号后面没空格、字符串里有特殊字符没转义都会导致解析失败。我遇到最多的是缩进问题特别是从网页复制代码的时候Tab 和空格混在一起肉眼看不出来但解析就是报错。排查方法很简单用在线 YAML 校验工具过一遍或者用 Python 的yaml.safe_load试一下。如果报错信息指向某一行就重点检查那一行的缩进和特殊字符。另外字符串里如果有冒号、井号、引号最好用双引号包起来。import yaml with open(skill.md, r) as f: content f.read() # 提取 YAML 部分 yaml_part content.split(---)[1] try: meta yaml.safe_load(yaml_part) print(YAML 解析成功) except yaml.YAMLError as e: print(fYAML 解析失败: {e})4.2 Skill 不触发检查 trigger 和 descriptionSkill 写好了但 Agent 不调用最常见的原因是 trigger 词没匹配上或者 description 写得太模糊。Agent 决定用哪个 Skill 时会拿用户输入和 trigger 列表做匹配同时参考 description。如果用户说“帮我存一下这个网页”而你的 trigger 里只有“保存网页”“网页转 Markdown”那就匹配不上。解决办法是 trigger 里多写几个同义词把用户可能说的各种表达都覆盖到。description 也要写清楚最好包含“什么时候用”的信息。比如“当用户需要将网页内容保存为本地 Markdown 文件时使用”这样 Agent 判断起来更准。4.3 执行到一半卡住加超时和重试Skill 执行过程中卡住通常是因为某个步骤在等一个永远不会来的响应。比如请求一个不存在的 URL或者等待一个不会触发的条件。这时候需要在 Skill 里加超时和重试机制。我的做法是所有网络请求设 30 秒超时所有文件操作设 10 秒超时所有循环设最大迭代次数。超时后要么重试最多 3 次要么跳过要么返回错误。具体选哪种取决于这个步骤是否关键。关键步骤失败就返回错误非关键步骤失败就跳过并记录日志。4.4 输出格式不稳定用模板约束Agent 执行 Skill 时输出格式可能每次都不一样。比如要求输出 JSON它有时候输出纯 JSON有时候包在代码块里有时候还加一段解释。这会让下游处理很头疼。解决办法是在 Skill 里明确输出模板并且加上“只输出以下格式不要添加任何额外内容”的约束。如果还是不稳定可以在 Skill 最后加一个“格式化”步骤用代码把输出强制转成目标格式。常见问题排查方向解决方法YAML 解析失败缩进、特殊字符用校验工具检查字符串加引号Skill 不触发trigger 词、description增加同义词description 写清楚场景执行卡住超时、重试加超时设置和最大重试次数输出格式乱模板约束明确输出模板加格式化步骤步骤顺序错约束不够强加“必须按顺序执行”的强制约束4.5 版本管理Skill 也要有 changelogSkill 多了以后改一个可能影响另一个。我现在的做法是每个 Skill 都带版本号改动时更新版本号并在文件末尾加 changelog。这样出问题可以快速回滚也能追溯哪个版本引入了 bug。## Changelog ### 1.2.0 - 增加图片下载失败时的回退逻辑 - 优化文件名生成规则 ### 1.1.0 - 增加超时设置 - 修复 YAML 缩进问题 ### 1.0.0 - 初始版本5. Skill 生态的扩展玩法5.1 Skill 组合小技能拼出大流程单个 Skill 能力有限但组合起来就很强。比如我有三个 Skillwebpage-to-markdown、markdown-to-pdf、pdf-merge。单独用每个都只能做一件事但组合起来就能实现“抓取多个网页、转成 Markdown、再转 PDF、最后合并成一个文件”的完整流程。组合的方式有两种一种是在 Skill 里直接调用其他 Skill另一种是让 Agent 自己编排。前者更稳定后者更灵活。我一般把稳定的组合写成“复合 Skill”把灵活的组合留给 Agent 临场决定。5.2 Skill 市场拿来主义也不错不是所有 Skill 都要自己写。社区里已经有不少现成的 Skill比如文件处理、格式转换、数据提取这些通用场景直接拿来用就行。我用过几个不错的开源 Skill省了不少时间。但要注意别人的 Skill 不一定完全符合你的需求可能需要改。改的时候先看 YAML 里的 inputs 和 outputs确认接口能不能对上。对不上的话要么改 Skill要么在调用时做一层适配。5.3 Skill 的调试和监控Skill 多了以后调试和监控就很重要。我现在的做法是给每个 Skill 加日志记录调用时间、输入参数、执行结果、耗时。这样出问题可以快速定位是哪个 Skill 哪一步出的错。日志格式我一般用 JSON方便后续分析{ skill: webpage-to-markdown, version: 1.2.0, timestamp: 2025-01-15T10:30:00Z, input: {url: https://example.com/article}, output: {file_path: ./output/article.md, file_size: 4096}, duration_ms: 2350, status: success }5.4 从 Skill 到“肌肉记忆”的最后一公里Skill 写好了、能用了还不算完。真正的“肌肉记忆”是 Agent 能在合适的时机自动调用合适的 Skill不需要用户明确说“用哪个 Skill”。这需要 Agent 对 Skill 的理解足够深能根据上下文判断该用什么。我目前的方案是在 Skill 的 description 里写清楚适用场景同时在 Agent 的系统 Prompt 里加一段“Skill 选择指南”告诉它什么情况下优先考虑哪个 Skill。实测下来这样能显著提高自动调用的准确率。另外定期回顾 Skill 的使用日志也很重要。哪些 Skill 经常用哪些从来没用过哪些经常出错这些信息能帮你优化 Skill 库。我每个月会花半小时过一遍日志把没用的 Skill 归档把常用的 Skill 优化一下。提示Skill 不是越多越好。我见过有人写了上百个 Skill结果 Agent 选择困难反而降低了效率。保持 Skill 库精简每个 Skill 都有明确用途比堆数量重要得多。6. 一些个人体会写 Skill 这件事最难的其实不是技术而是“把隐性知识显性化”。很多操作你做了很多遍已经变成下意识动作了但要让机器执行就必须把这些下意识动作拆解成一步步明确的指令。这个过程本身就能帮你发现自己流程里的冗余和低效。我刚开始写 Skill 的时候总想写得大而全一个 Skill 恨不得覆盖所有情况。结果就是 Skill 又长又复杂维护起来很痛苦。后来学乖了一个 Skill 只做一件事做精做透。需要复杂流程就组合多个小 Skill这样每个 Skill 都简单可控组合起来也灵活。还有一点是不要追求一次写完美。Skill 是迭代出来的先写个能用的版本跑起来看哪里有问题再改。我现在的 Skill 基本都改过至少三版第一版能用第二版稳定第三版才顺手。所以别怕改改得越多越接近“肌肉记忆”的状态。最后分享一个小技巧写 Skill 的时候想象你在教一个完全不懂这个任务的新人。你会怎么跟他说先说做什么再说怎么做最后说注意什么。按这个顺序写出来的 SkillAgent 理解起来也最顺。