OpenClaw从玩具到工具:手把手教你编写自己的Skills技能体系
先说说我自己的经历。某天我在准备一个AI编程专项的作业要求把 OpenClaw 从“能跑通 Demo”变成“能解决我的实际问题”。装好 OpenClaw 的第一个下午我兴致勃勃地让它试用内置的几个 Skills——写周报、搜信息、解释代码它们确实演示得很顺。但到了第二天我想让它把我电脑里乱七八糟的下载文件夹按我的规矩归类它愣住了。它甚至告诉我这个场景不在它已知的技能里。那一刻我彻底同意标题里的判断如果没有自己的 SkillsOpenClaw 只能是一个玩具。这篇复盘正是围绕这个判断展开的。我会先说清楚它为什么只是玩具然后告诉你我是如何从零编写一个下载目录整理 Skills 的最后总结出一套把单个技能扩展成技能体系的方法。内容不追求讲得多玄乎只希望你读完能直接在自己的环境里动手把 OpenClaw 变成真正替你干活的工具。1. 为什么说“没有自己的SkillsOpenClaw只是玩具”1.1 OpenClaw的本质是骨架Skills才是肌肉OpenClaw 这类智能体框架核心本质是一个调度器它调用大模型来理解你的意图再调用外部工具来执行动作。也就是说模型负责“思考”而平台负责“连接”。如果你什么都不配置它最多只能做三件事跟你聊天、读一些通用文档、调用平台自带的几个示例技能。我把这种状态叫作“骨架状态”。骨架没有肌肉看起来有头有脚但动弹不得。Skills 就是肌肉是真实操作的载体。一个智能体的价值不在于它拥有多大的模型参数而在于它被允许调动多少种经过设计、适配场景、边界清晰的操作能力。反过来说为什么很多人玩了几天 OpenClaw 就觉得没意思因为默认能力太薄。你问它“帮我写个周报”它能做到你问它“把上周所有项目文档汇总成一份带目录的清单”它就蒙了。这个“蒙”不是模型不聪明而是它不知道该调用什么技能、用什么规则去完成这件事。1.2 内置Skills的通病对谁都友好就是对你不友好几乎每个智能体框架都会带几个示例 Skills比如网页搜索、文件读取、发送消息、跑一段 Python 代码。这些技能有个共同特点设计者为了让所有人都能跑通把触发方式、参数结构、输出格式全部标准化了。标准化的另一面就是无法适配个人规则。举个例子我习惯把下载目录里的安装包单独扔到一个叫软件的子文件夹但如果文件名里没有“setup”“install”等明显关键词内置技能根本不会按我的习惯归类。我还会希望它整理完以后打印一个“今日归档统计”而不是输出一堆原始路径。这些需求并不复杂但内置技能永远无法预见到。这就像一个公司只给你配了一把公用螺丝刀能用但拧不同形状的螺丝效率极低。你要解决的是自己的问题就必须亲手打磨自己的螺丝刀也就是自己的 Skills。1.3 “玩具”与“工具”的分界线在哪里我自己的判断标准有三条。第一能否匹配你真实的重复性工作流。如果 OpenClaw 不能每天固定帮你完成一件你本来就要做的事那它只适合演示。第二能否处理你个人的私有规则。比如特定命名格式、特定分类逻辑、特定的权限边界只有写进技能代码里它才真正属于你。第三能否被你自己调试和迭代。内置技能出了问题你只能等官方更新自建技能出了偏差你可以立刻看日志、改规则、重新跑。这三条合起来就是“工具感”的来源。工具不是拿来展示的是拿来干活并越用越顺手的。所以从今天开始如果你想认真使用 OpenClaw第一件事不是研究怎么把模型调得更好而是坐下来想想我最常做的一件事能否把它变成一个属于自己的 Skills。2. 从零搭建一个自己的Skills先理解它的文件结构2.1 Skills在OpenClaw里到底长什么样在我使用的 OpenClaw 版本里一个技能本质上是磁盘上的一个目录。目录里有三个关键部分描述文件、说明文档、执行脚本。结构大致长这样skills/ download-sorter/ manifest.yaml SKILL.md scripts/ sorter.pymanifest.yaml负责告诉平台“这个技能叫什么、能做什么、需要哪些参数、用什么命令执行”。SKILL.md负责告诉大模型“你什么时候该调用它、怎么把用户的话翻译成参数”。scripts/里的脚本才是真正干活的程序。很多人第一次写技能只重视最后那个脚本前面两个文件胡乱写。结果就是技能执行逻辑写得再完美大模型根本识别不到它等于白写。OpenClaw 的调度原理是大模型先看manifest.yaml里的描述和参数说明再读SKILL.md里的使用样例确定要用这个技能后才会按execute字段去启动脚本。2.2 manifest和SKILL.md里的字段哪些是真正要命的manifest.yaml里最重要的不是名称而是description和parameters_schema。description 写得越具体模型就越容易在“用户说了一堆话”时判断出这个技能能不能用。parameters_schema 则决定用户输入怎么落到脚本参数上格式不匹配脚本就会收到一堆莫名其妙的字符串。下面是我常用的一个配置骨架你可以照抄后按自己需要改name: download_sorter description: 整理指定目录中的文件按类型移动到对应子目录并规范命名。适合用户要求整理下载文件夹、桌面、临时目录时使用。 version: 1.0.0 trigger_examples: - 帮我把下载目录整理一下 - 把桌面的压缩包归类 - 整理一下我的临时文件目录 parameters_schema: directory: type: string description: 要整理的目录绝对路径默认取当前用户的下载目录 default: dry_run: type: boolean description: 是否只预览整理结果而不真正移动文件默认 true default: true execute: command: python3 args: - scripts/sorter.py - --directory - {directory} - --dry-run - {dry_run} allowed_actions: - file:read - file:list - file:movedry_run这个参数我强烈建议保留。它不是给人看的是给模型兜底的。模型在不确定操作影响时可以先跑一次只读预览把整理计划返回给用户确认然后再执行真正移动。这个设计能避免大量误伤。SKILL.md则是给大模型看的“说明书”我更愿意把它理解成一个小手册。里面要写清楚这个技能的边界是什么比如“不会处理隐藏文件”“不会覆盖同名文件”参数应该如何设置比如用户说“桌面”时对应哪个绝对路径如果遇到无法归类的情况应该返回什么提示而不是直接报错。2.3 为什么说脚本只是技能的一半另一半是“说明书”我最初犯过一个典型错误只写脚本不写说明书。脚本写的是“怎么执行”说明书写的是“什么时候执行”。大模型不像人它不会自己去看你的源码更不会理解你隐藏的意图。它只能通过SKILL.md的文字描述来判断。所以好的SKILL.md应该像你在给一个实习生布置任务说清楚触发条件、操作边界、常见坑、失败时怎么办。比如这样写# download_sorter 当用户想整理下载目录、桌面或临时文件夹时使用本技能。 输入目录必须是绝对路径。如果用户只说了“下载目录”或“桌面” 请获取系统默认路径后填入参数。 如果目录不存在直接告诉用户目录不存在不要尝试创建。 dry_run 参数默认为 true在执行真实移动前必须先以预览模式运行一次。 输出格式先输出分类统计再输出每个文件将被移到的目标位置。脚本负责“做到了”说明书负责“做对事”。两者缺一不可。3. 实战给OpenClaw写一个“下载目录整理器”Skills3.1 需求拆解从“看不过去”到“可执行的规则”我写这个技能的真实动机很简单我的下载目录常年堆着几十个文件各种命名混乱的文件、安装包、图片、压缩包混在一起。手动整理太累而 OpenClaw 恰恰可以自动完成。开始之前我先把需求拆成了四条规则。第一根据扩展名分类文档、图片、压缩包、安装包、其他。第二文件名要规范去掉首尾空格、把连续空格替换成下划线、保留原始扩展名。第三同名文件不能覆盖目标文件已经存在时自动加时间戳后缀。第四必须支持先预览再执行避免模型一上来就把文件移动错地方。拆完需求你就能发现这其实不是程序设计而是“规则建模”。所有让 OpenClaw 变得好用的技能都来源于你把模糊的生活需求翻译成确定性的判断逻辑。3.2 我写的manifest配置可以直接抄这个技能我命名为download_sorter实际配置就是上一节那段 YAML。其中parameters_schema我特意只留了两个参数一个是directory一个是dry_run。参数越少模型越容易理解。太多参数会增加模型填错的风险。还有一个细节allowed_actions我只给了file:read、file:list、file:move三个权限。没有给file:write或file:delete。为什么因为整理目录本身只需要这三个权限删除操作由用户自己决定。把权限列表收窄可以避免某个意外情况导致文件被大范围改写。3.3 处理脚本的几个关键片段scripts/sorter.py我用了 Python 写原因很简单文件操作逻辑清楚跨平台能力过得去写起来也不费劲。核心函数大概是这个思路import argparse import os import re from pathlib import Path def classify(name: str) - str: ext Path(name).suffix.lower() if ext in {.pdf, .docx, .txt, .md}: return 文档 if ext in {.png, .jpg, .jpeg, .gif, .svg}: return 图片 if ext in {.zip, .tar, .gz, .7z, .rar}: return 压缩包 if re.search(r(setup|installer|\.exe$|\.msi$|\.dmg$), name.lower()): return 安装包 return 其他 def normalize_name(name: str) - str: stem, ext os.path.splitext(name) stem stem.strip() stem re.sub(r\s, _, stem) return stem ext.lower() if ext else stem移动文件那一段我加了个保护逻辑先判断目标目录是否存在如果不存在则makedirs再判断目标文件是否已存在如果存在就在文件主名后追加-YYYYMMDDHHMMSS后缀。用户看起来名字长一点但不会出现丢失文件的情况。整个脚本不是几十行就完事但真正关键的就是这两块分类和去重。分类规则定了所有文件才有一个确定去向去重逻辑定了才敢让模型放手执行。3.4 测试时才发现的问题参数透传、相对路径、安全防护写完脚本不代表完事我至少调了四轮才让它稳定跑通。第一轮发现的问题是参数透传manifest.yaml里{directory}如果模型没传Python 脚本收到的是空字符串直接导致Path()指向当前目录差点把整个 skill 目录当成下载目录整理。解决办法是在脚本里再次校验参数如果不合法就返回提示而不是硬着头皮执行。第二轮是相对路径问题。OpenClaw 执行脚本时工作目录可能是技能目录也可能是用户项目目录不确定。所以我在脚本里把所有路径都做了expanduser().resolve()确保相对路径和波浪号都能转成绝对路径。从那以后我再也没遇到过“文件跑到莫名其妙位置”的情况。第三轮是安全防护问题。我先干跑一次--dry-run然后让模型把整理计划汇报给用户用户确认后才执行真实移动。实测下来这个步骤虽然多了交互但非常值得。因为模型理解“桌面”和“下载目录”时偶尔会填错路径干跑一次能提前暴露问题。第四轮是日志留痕。我在脚本里用了最简单的logging模块每次整理都在技能目录下写一个sorter.log。日志里包含调用时间、参数、处理文件数量和异常信息。这个习惯让我后面排查问题省了大量时间。4. 把Skills做“专”的五个决策点4.1 输入设计一句话说清楚“让技能做什么”很多人设计技能时会把参数设计得很“技术化”比如传一个 JSON 对象进去、传一个modefast之类的枚举值。我踩过这个坑之后明白了一个原则参数要设计成让大模型一看就懂、一填就对。比如我这个整理技能只保留了directory和dry_run。用户说“整理一下桌面”模型只需要把“桌面”转成路径用户说“先看看会怎么样”模型就知道把dry_run设为true。参数越接近自然语言模型的填充准确率越高。反过来如果参数是operation_mode1这种模型只能靠猜猜错就出错。4.2 权限设计给技能最小触碰范围给技能授权时我遵循一个原则能碰目录就不碰全盘能读取就不给写入能移动就不给删除。很多人觉得授权越多越好但智能体最怕的就是不可控。一个文件移动脚本如果同时拥有删除权限一次路径写错后果可能是灾难性的。所以我会刻意在manifest.yaml里把allowed_actions写得尽量少。在后续技能设计里我也建议你把“最小权限”写成一条硬性检查项。每次新增一个动作前先问自己这个动作真的是这个技能必须的吗4.3 粒度设计一个技能只解决一个场景刚开始写技能时我总想做一个“万能文件管理技能”把所有文件操作全部塞进去。结果发现模型要在几十个参数里做选择光解释清楚就很费劲效果也很差。现在我更倾向于“一个技能只做一件事”。下载目录整理是一个技能日志快速检索是另一个技能项目文件打包又是一个技能。每个技能边界清晰、描述简短模型自然更容易在合适的时机调用。更重要的是这些技能可以被组合先整理文件再生成归档清单最后调用消息通知技能把结果发给自己。4.4 错误设计把异常返回给“大脑”而不是自己崩溃脚本报错时很多人的第一反应是让脚本崩溃然后看日志找原因。但在智能体场景里最好的做法是让异常信息按可读方式返回给大模型让模型自己调整方案。我在sorter.py里专门写了一个try-except大块捕获目录不存在、权限不足、文件正在被占用等常见异常然后返回类似“目录不存在请检查路径是否正确”的中文提示。这样大模型拿到提示后会主动猜测用户是否说了另一个路径或者让用户重新给出一个有效目录。用户友好度提升很大。4.5 可观测设计每次调用留痕最后一个决策点也是所有进阶技能都要做的每次调用必须留痕。日志、结构化输出、调用记录属于智能体时代的“工程素养”。没有日志你永远不知道模型到底怎么使用你的技能也无法判断是描述文件写得不够好还是脚本逻辑有问题。我的做法是每个技能至少输出一份结构化摘要。比如整理完目录后脚本除了移动文件还会打印一份聚合统计一共找到多少文件、分成几类、移动到哪、跳过多少同名文件。这份摘要既给用户看也作为日志的一部分留存。下次你发现自己设计的技能变得不好用时翻一翻日志问题往往一目了然。5. 从“单个技能”到“技能体系”给OpenClaw装上自己的工具箱5.1 为工作流建技能库而不是为功能建技能做完下载目录整理技能后我又陆续写了几个技能会议纪要转结构化待办、图片批量压缩、项目文件备份、网络文章转本地 Markdown。它们表面上是不同功能实际上都围绕我真实的工作流。专门给某个功能做一个技能往往做完了就吃灰围绕工作流做的技能才会天天被调用。所以我的建议是建技能库之前先记录自己一周内的高频重复动作。把那些“每周都要做、又不难做、只是麻烦”的事情挑出来这才是技能库的第一批成员。技能数量不必多只要每个都能稳定解决一个问题价值就非常大。5.2 技能间的调用与协作让OpenClaw自己串联多个技能单个技能只能解决一个小步骤真正让人省心的是多个技能的串联。比如我现在可以告诉 OpenClaw“整理我的下载目录然后生成一份归档报告最后把报告路径发给我。”它会依次调用download_sorter、generate_report和send_notice三个技能。要让这种串联更稳定我通常会把上一个技能的输出设计成下一个技能需要的输入格式。比如download_sorter输出的统计摘要我会让它额外保存为一个 JSON 文件generate_report技能只需要读取这个 JSON 即可。这样技能之间通过文件解耦谁也不依赖谁模型也可以更灵活地组装调用顺序。5.3 技能的版本维护一次好的更新来自真实使用记录技能写出来不是终点它会随你的使用习惯慢慢演化。我每周都会翻一下各技能的调用日志重点关注两个指标触发频率和失败率。如果某个技能两周都没被触发过那不是模型不聪明而是描述文件没写清楚或者这个技能根本不在用户场景里。此时我会改写SKILL.md里的触发示例。曾经有一个技能总是把用户的话理解成另一个相近技能排查发现是我的两个技能 description 用了一大堆重叠词。把它们改成互相区隔的边界描述后调用准确率立刻上来了。这就是版本维护的价值不是改代码而是持续校准大模型对技能的理解。5.4 我的真实体验从玩具到工具也许只是一个网关的距离整个 Skills 专项做完之后我最大的感受不是“多了一堆自动化脚本”而是 OpenClaw 终于成了我知道它下一步会怎么做的工具。它知道我的下载目录规则知道我的归档命名习惯知道哪些文件可以自动处理、哪些必须问过我之后才能动。这种确定感是任何内置技能都给不了的。如果你也想摆脱“玩具”状态我的建议很简单先挑一个你自己最头疼的重复性小任务照着上面的结构写一个最简单的 Skills跑通它。不用追求复杂先把“整理下载目录”或“自动改文件名”这类小事做扎实。一旦你经历了从写规则到配置参数、再到模型自主调用并完成任务的全过程你就会真正理解题目里那句话的分量——没有自己的 SkillsOpenClaw 只是一个玩具而当你为它亲手装上第一个技能它就迈出了变成工具的第一步。