
刚过去的一年里AI圈最热的关键词早就不再是“聊天”“写诗”“画图”了。各大模型厂商和开发者社区都在拼命解决同一件事怎么让大模型不只是“动嘴”而是真正“动手”。如果你一直在关注 Claude 的新功能一定绕不开一个概念——Skills。简单说它是一套让 AI 按需加载“技能包”的机制大模型不再是那个只会按提示词生成文本的对话机器而是可以临时拥有一个工具集、一段脚本、一套流程去完成文件整理、数据分析、系统巡检、批量处理这类真实任务。我在实际项目中重度使用了这套机制体验很直接它把“让 AI 干活”这件事的成本打到了极低。过去要写一堆 prompt 让模型记住你的工作流或者硬啃 Function Calling 的框架对接现在只需把一个技能文件夹摆到指定位置模型自己就知道什么时候该调用、怎么调用、用什么参数。这篇文章我把我从零搭建、调试、优化 Skills 的完整过程写出来包括设计思路、配置细节、踩坑实录和排查技巧。适合刚开始接触 AI Agent 开发或者已经用过 Claude 但还没深入折腾过 Skills 的读者。1. 什么是 Skills理解 AI 的“外挂技能包”1.1 从“会聊天”到“会干活”的一步之遥先厘清一个底层问题。传统的对话式 AI本质是“文本进、文本出”你给它一段指令它基于训练数据和你提供的上下文生成一段回答。它很聪明但它没有手也没有脚。你让它“帮我把这个目录下所有文件名里的日期格式统一一下”它只能给你一段 Python 代码然后你自己去跑你让它“检查一下这台服务器上 Nginx 的配置有没有问题”它只能说个大概思路没法真的登进去看。Skills 把这层墙拆了。它的设计哲学很简单给模型配一副“可穿戴设备”。平时模型还是那个模型一旦任务环境需要它可以从技能库里选出一个合适的技能读取技能的使用说明加载技能附带的脚本或工具然后真正去执行。打个比方这就像你请了一个万能助理他不会修水管但你给了他一本水管工手册和一套工具箱他接到修水管的活儿时会自己翻手册、拿工具上手。Skills 就是那本手册加工具箱。1.2 一个 Skill 的组成说明文件 可执行代码从技术实现上看一个 Skill 不是一个魔法开关它就是一个普通文件夹里面放两样东西SKILL.md技能说明文件用 Markdown 写的。作用是告诉模型这个技能能干什么、什么时候用、怎么用、有哪些限制。这就是给模型看的“使用手册”。可执行脚本或其他资源通常是 Python 脚本、Shell 脚本也可以是一个本地服务的接口定义。这是真正干活的“手”。模型会在对话过程中根据用户请求判断“该不该用这个技能”然后读取 SKILL.md 理解用法再调用脚本执行。整个过程对使用者来说几乎是透明的——你不需要自己写代码去调用工具模型负责协调。1.3 它和 Function Calling、MCP 有什么区别很多读者会想到 OpenAI 的 Function Calling或者当下很火的 MCPModel Context Protocol。这几个概念确实容易混淆但定位完全不同对比维度Function CallingMCPSkills核心思路让模型输出结构化调用指令统一工具调用协议标准化对接模型自主决定何时加载何种能力谁主导调用开发者预设函数模型选择客户端按协议分发请求模型读取文档后自主使用对开发者的要求需要写 API 封装定义 schema需要搭建 MCP Server遵循协议只需写一个 Markdown 文档 脚本灵活性中函数列表固定高可动态发现工具极高按任务场景动态加载使用门槛中偏高高低我做过的项目里如果只是给模型暴露一两个固定的数据查询接口Function Calling 够用如果要对接很多外部系统MCP 是正确的选择但如果你的目标是让模型具备“一系列可复用的做事能力”Skills 是最自然、最轻量的方式。三者不是互斥关系实际项目里甚至可以混合用。2. 设计 Skills 前的思考什么样的任务才值得做成技能2.1 任务拆解不是所有需求都适合塞进技能包一开始容易犯的错是恨不得把所有操作都做成 Skill。我试过把一个包含十几步判断逻辑的运营流程硬塞进一个技能里结果模型在“先用哪个子步骤”上反复纠结效果很差。后来我学乖了做技能前先把任务拆到单个“动作”的粒度。一个合适的 Skill 任务通常满足这几个条件目标明确一个技能只做好一件事输出结果可验证。流程固定输入相似执行路径相似不需要模型临时发挥太多。有真实执行价值模型靠文本回答解决不了必须调用脚本或工具操作。可以自动化验收跑完能看得到结果文件、日志或者状态变化。举个例子“把照片按拍摄日期归档到文件夹”是一个好技能“帮我策划一场市场营销活动”不是一个好技能。后者需要大量创意判断模型直接对话完成度更高硬做成技能反而画蛇添足。2.2 定义“接口”输入、输出、边界所有好的工程实践都是先定义接口写 Skill 也一样。动手写 SKILL.md 之前我会先在脑子里或草稿纸上明确三件事输入是什么这个技能接收什么信息是文件路径、URL、一段原始文本还是几个参数用什么方式传给脚本环境变量、命令行参数还是标准输入输出是什么执行完返回什么是写一个文件、打印一段结构化 JSON还是直接对系统做了修改返回的结果要能被模型读懂并继续处理。边界在哪里哪些事绝对不能做比如不能删除文件、不能执行危险命令、不能访问外网。这些边界必须白纸黑字写进 SKILL.md模型才会约束自己。我见过不少失败的技能问题都出在接口定义模糊。脚本读取参数的方式没写明白模型凭感觉传参脚本直接报错输出格式没有约定模型不知道结果成不成功只能猜。接口定义清晰了这个技能就成功了一半。2.3 选择脚本语言Python 依然是首选但不是唯一目前社区里大部分 Skill 示例都是用 Python 写的我也默认用 Python原因很现实生态全、跨平台、AI 训练语料里 Python 代码最多模型写 Python 脚本的准确率明显高于其他语言。不过如果你处理的是纯系统运维场景Shell 脚本更直接不用带着 Python 环境的依赖到处跑如果是 Windows 环境下的自动化PowerShell 也有它的优势。我的建议是按场景选语言但优先保证脚本能在目标机器上一键运行。为了让技能可移植性更强我通常会在脚本里做依赖检查缺什么库时输出明确的提示而不是直接崩溃这样模型看到报错也知道怎么处理。3. 实战手把手实现一个 Skill——自动批量重命名文件讲完理论我们直接上手。下面我用一个真实可用的例子走一遍完整流程做一个技能让模型能够自动把指定目录下的所有文件按“创建时间_原始文件名”的格式批量重命名。这个场景非常典型既涉及文件系统操作又适合展示 Skills 的核心机制。3.1 项目结构规划先规划目录结构。假设我们把这个 Skill 放在~/.claude/skills/下面具体位置取决于你在用的客户端建议先查自己客户端的文档各个工具的实现可能不同。以 Claude Desktop 和 Claude Code 为例都支持通过配置指定技能目录。skills/ └── batch-rename/ ├── SKILL.md └── rename_script.pybatch-rename是技能目录名SKILL.md 和脚本都放在里面。技能目录名建议用小写加连字符可读性好也不容易在跨平台时出问题。3.2 编写 SKILL.md写给模型看的“使用手册”这是整个技能里最关键的文件。我见过很多人把 SKILL.md 写成给人类看的 README这完全跑偏了——它的目标读者是 AI 模型所以要写得让模型一看就懂、一读就能照着执行。我的 SKILL.md 全文如下可以直接参考--- name: batch-rename description: 按创建时间批量重命名指定目录下的所有文件格式为“YYYYMMDD_HHMMSS_原文件名”。 --- # Batch Rename Skill ## 适用场景 - 用户要求整理某个目录下的文件希望文件名带上创建时间。 - 用户说“把这个文件夹里的文件按时间重命名”或类似的表达。 - 目录下文件较多手工重命名费时且容易出错。 ## 不适用场景 - 用户只需要重命名单个文件。 - 用户要求按文件大小、类型等其他规则命名。 - 用户指定的目录不存在或没有读取权限。 ## 使用前必须确认 1. 向用户确认目标目录的完整路径。 2. 确认重命名规则是否需要保留原始文件名主体部分。 3. 确认是否需要对子目录中的文件递归操作默认不递归。 ## 执行步骤 1. 使用 Python 脚本 rename_script.py传入目标目录路径作为第一个位置参数。 2. 脚本会扫描目录下所有普通文件跳过子目录和隐藏文件。 3. 对每个文件读取其创建时间Linux 下为 birth timemacOS 下为 birth timeWindows 下为创建时间如果没有创建时间则回退到修改时间。 4. 生成新文件名前缀时间戳 原文件名。如果文件名已符合格式跳过。 5. 重命名时如果目标文件已存在自动追加序号避免覆盖。 6. 执行完成后脚本会输出 JSON 格式的结果包含重命名成功、跳过、失败的文件列表。 7. 将执行结果摘要反馈给用户。 ## 重要限制 - 不要删除任何文件。 - 不要重命名目录。 - 不要修改文件内容。 - 如果目录路径中包含空格或特殊字符调用脚本时务必将路径用引号包住。写好之后我必须给你画个重点SKILL.md 不是写给人看的是写给模型看的。所以不要炫技不要写废话不要用含糊的形容词。模型读取文档时是在“学习规则”规则越清晰它的执行就越可靠。上面我用“适用场景”“不适用场景”“使用前必须确认”“执行步骤”四个模块本质上是在做流程拆解降低模型自主决策时的随机性。3.3 实现核心逻辑脚本SKILL.md 是说明书脚本才是真正干活的“手”。我来写一个健壮性比较高的版本#!/usr/bin/env python3 batch-rename 技能的核心执行脚本 用法: python rename_script.py /path/to/target_directory import sys import os import json from datetime import datetime def get_file_timestamp(filepath): 获取文件时间戳优先创建时间回退到修改时间 stat_info os.stat(filepath) # 优先创建时间不同平台字段不同 if hasattr(stat_info, st_birthtime): ts stat_info.st_birthtime else: ts stat_info.st_ctime return datetime.fromtimestamp(ts) def build_new_name(original_name, timestamp_str): 构建新文件名时间戳_原始名如果已符合格式就直接返回原名称 if original_name.startswith(timestamp_str): return original_name, False new_name f{timestamp_str}_{original_name} return new_name, True def rename_file(directory, filename): 执行重命名返回结果状态 old_path os.path.join(directory, filename) # 跳过目录、隐藏文件和符号链接 if os.path.isdir(old_path) or filename.startswith(.) or os.path.islink(old_path): return {file: filename, status: skipped} try: ts_obj get_file_timestamp(old_path) timestamp_str ts_obj.strftime(%Y%m%d_%H%M%S) new_name, should_rename build_new_name(filename, timestamp_str) if not should_rename: return {file: filename, status: skipped} new_path os.path.join(directory, new_name) # 如果目标存在追加序号 counter 1 final_new_path new_path while os.path.exists(final_new_path): name, ext os.path.splitext(new_name) final_new_path os.path.join(directory, f{name}_{counter}{ext}) counter 1 os.rename(old_path, final_new_path) return {file: filename, new_name: os.path.basename(final_new_path), status: success} except Exception as e: return {file: filename, status: error, message: str(e)} def main(): if len(sys.argv) 2: print(json.dumps({error: 缺少目标目录参数}, ensure_asciiFalse)) sys.exit(1) directory sys.argv[1] if not os.path.isdir(directory): print(json.dumps({error: f目录不存在: {directory}}, ensure_asciiFalse)) sys.exit(1) results [] success_count 0 for filename in sorted(os.listdir(directory)): result rename_file(directory, filename) results.append(result) if result[status] success: success_count 1 summary { directory: directory, total: len(results), success: success_count, skipped: sum(1 for r in results if r[status] skipped), failed: sum(1 for r in results if r[status] error), details: results, } print(json.dumps(summary, ensure_asciiFalse, indent2)) if __name__ __main__: main()这个脚本有几个值得留意的设计我用了st_birthtime拿创建时间但 Linux 上 Python 的os.stat默认是没有这个字段的不同平台行为不一样所以做了回退到st_ctime在 Linux 上更接近元数据变更时间的处理。技能执行跨平台时这类兼容逻辑能减少很多莫名奇妙的报错。输出统一用 JSON并且打印在 stdout 上。这么做的原因是模型的“眼睛”就是那段标准输出文本它需要从输出里判断任务成没成、哪些文件有问题。JSON 结构化输出比纯文本更容易让模型解析。重名处理我用了“追加序号”而不是直接覆盖。文件操作最怕数据丢失宁可多生成几个带序号的副本也不要互相覆盖。3.4 本地测试与调试先让脚本自己跑通脚本写完别急着丢给模型先在终端里手动测几轮。这是我最强调的步骤——技能出问题九成是脚本本身的问题不是模型的问题。测试几步找一个测试目录塞几个测试文件运行python rename_script.py /tmp/test_files看看输出的 JSON 里 total、success、skipped 是否符合预期。检查目录里文件是否真的重命名了时间戳格式对不对。再次运行脚本确认文件被跳过而不是重复改名。测试错误路径传入一个不存在的目录看脚本是否输出友好报错。跑通之后再接进客户端里做集成测试。你会逐渐发现最耗时的地方不是我预想里的界面或配置而是“把模型的调用习惯调顺”——它有时候不按 SKILL.md 里写的参数传递路径里带空格时不加引号这些都需要靠调整描述文件和测试对话来纠正。4. 进阶配置与运行机制让技能更聪明、更安全4.1 技能描述里的“触发词”教你如何在文档里提高命中率模型的技能调用是基于语义匹配的它读到“批量重命名”相关请求时会自动调出 batch-rename 技能。但这个匹配不是 100% 准确的特别是用户表述很模糊的时候。这时候SKILL.md 里的description字段就承担了“路标”的作用。我在描述里会刻意埋入用户可能使用的各种说法比如“整理文件”“按时间重命名”“给文件加日期前缀”“批量改文件名”确保用户的真实表达能撞上技能关键词。这跟搜索引擎做 SEO 的核心思路一模一样——你优化的是 AI 对文档的检索命中率。4.2 权限控制与安全边界防止 AI “手滑”任何一个让 AI 直接操作系统的功能都必须正面回答安全问题。模型不是不会犯错它在参数传递、路径拼接这种细节上比人类更容易产生离奇操作。为了让技能在实际使用中“出不了大事”我给自己定了几条铁律绝不删除文件脚本里不提供任何删除接口。如需清理可以先移动到回收站目录人肉确认后再删。只在用户指定的目录里活动脚本强制锁定目标目录禁止使用..跳转到上级目录。如果用户传了绝对路径必须校验它在允许范围内。敏感操作前置确认如果技能需要修改大量文件比如超过 100 个在 SKILL.md 里要求模型先向用户发确认提示等用户点了同意再执行。安全不能只靠模型自觉脚本层面就要把危险操作堵死。我在实际项目里甚至会把关键脚本里的os.remove直接物理删除防止模型在生成新调用时绕过限制。4.3 上下文与状态管理技能执行完了然后呢一个很常见的坑是技能执行完模型就把结果丢了用户问“刚才改到第几个文件了”模型一脸懵。原因在于技能脚本是无状态的它跑完就结束不保留任何信息。解决方式我推荐两个脚本每次输出完整的 JSON 结果让模型基于输出继续对话这样结果就存在于对话上下文里。如果任务跨多次对话脚本可以把执行记录追加到一个日志文件里模型需要时读取日志。状态管理这层本质上是给模型搭记忆脚手架。技能的“事后处理”有没有做扎实决定了用户的体验是“AI 帮我搞定了”还是“AI 做了但我不确定它做了什么”。5. 常见问题与排查实录我把踩过的坑都列在这里5.1 技能没有被加载模型无视技能库这是新手最容易碰到的问题。排查顺序我建议这么来确认技能目录路径配置正确。不同客户端读的技能目录不一样用前先查文档。确认 SKILL.md 文件名大小写。有些系统严格区分大小写写skill.md和SKILL.md可能是两回事。确认描述文件里有没有name和description字段缺少任何一个都可能导致解析失败。换个更直白的表达方式测试比如直接说“用批量改名技能处理这个目录”。如果这样能触发说明语义匹配没问题是用户表达离技能关键词太远了。5.2 技能调用了但脚本报错脚本报错有几种典型情况报错类型可能原因解决方式No such file or directory目录路径传错或路径里有空格没加引号在 SKILL.md 里反复强调路径必须用引号包裹Permission denied当前用户对目标目录没有写权限给脚本加权限检查提前给出清晰提示ModuleNotFoundError脚本依赖第三方库但环境里没装脚本开头做依赖检测缺库时输出安装指引中文文件名乱码不同系统的编码差异脚本里统一用 UTF-8并在描述里要求模型不要改动原始文件名编码5.3 模型对技能的理解有偏差SKILL.md 写得再细模型也有跑偏的时候。比如用户明明只要求重命名一个文件它却调用了批量技能把整目录都改了。这种问题要从两个方向同时修在 SKILL.md 的“不适用场景”里写清楚哪些情况不要调用这个技能。在“使用前必须确认”部分添加“确认用户是否要求处理整个目录如果不是拒绝执行”。5.4 技能执行速度太慢或者文件太多超时批量处理几千个文件时如果脚本还是同步等待所有文件跑完很容易触发客户端的超时限制。我的做法是给脚本加一个--limit参数分批次处理每批处理完输出一次进度给模型一个可以持续反馈的过程。同时处理大目录之前先和用户确认“本次要处理 5000 个文件可能耗时较长是否继续”。6. 写在最后的经验和建议Skills 这套机制最打动我的地方是——它把“AI 能力扩展”的门槛从“工程问题”降到了“文档问题”。任何一个能写清楚使用说明的人都可以给模型武装一项新能力不一定要精通后端开发或 API 设计。但是门槛低不代表不需要用心我在反复调试中最大的体会是技能的可靠性不取决于脚本写得多炫而取决于边界画得多清楚。几个具体的建议送给准备入坑的读者从一个小而具体的场景开始不要一上来就做“全能工作流”。一个能稳定完成文件重命名的技能比一个什么都想管但经常出错的技能有价值得多。SKILL.md 要持续迭代。每当你发现模型在某类表达下用错了技能就去描述文件里补一句限制或规则它就会越来越“懂事”。脚本的输出一定要结构化、可被模型解析。JSON 是默认选项别用一堆无格式的 print 文本。安全底线要提前画好。删除操作、危险命令、外网访问这类高风险行为最好从一开始就不出现在技能能力范围内。最后分享一个我在实践中养成的小习惯每次新增一个技能我都会准备一个“测试对话”模板里面包含正常请求、边界请求、错误请求三类问题每轮改完配置就先跑一遍模板确保任何场景下模型的行为都可预期。这套流程虽然简单却帮我省下了大量线上出问题的补救时间。