资讯动态

OpenClaw自定义SKILL实战:从技能封装到智能体自动化落地

发布时间:2026/10/22 8:24:28 来源:尧图企业网站定制
我一直觉得这类自动化智能体框架最灵魂的部分不是那套花哨的对话界面也不是默认带的那几个工具而是你能不能把“自己想让它干的事”教给它。OpenClaw里这个“教”的动作本质上就是自定义SKILL技能。很多人把SKILL理解成“写一段代码让程序执行”这没错但格局小了。更准确地说SKILL是一套“让智能体理解任务、拆解动作、调用工具、拿到结果并自我纠错”的封装单元。这篇东西不打算给你念文档我就结合自己的实际落地经验把OpenClaw自定义SKILL从思路到细节从“能跑”到“跑得稳”的全过程拆开讲清楚。1. 为什么非要自定义SKILL理解了设计哲学才好动手1.1 SKILL在OpenClaw里到底扮演什么角色先聊点虚的但重要的。OpenClaw的架构里Agent本身是一个“决策大脑”它自己并不直接干脏活累活而是通过调用各种能力去完成任务。这些能力被分成了几层最底层叫TOOL工具是单个动作比如“发一个HTTP请求”、“创建一个文件夹”往上一层是PROTOCOL协议是完成某个业务目标的最小动作序列再往上一层就是我们说的SKILL技能。如果说TOOL是“指头”PROTOCOL是“手”那SKILL就是“一套完整的舞步”。它的粒度足够大大到能让智能体在听到“帮我整理一下这堆网页里的产品价格”时不用你去临时编排“先打开链接、再提取文本、再解析HTML、再写入表格”而是直接激活一个名为web_deal_scraper的技能按部就班地把活干完。自定义SKILL的核心价值就是把“你脑子里的经验”变成“智能体肌肉里的记忆”。比如你用Python写了三年代码你拿到一堆脏数据时的处理套路是先看前五行、判断编码、用正则清洗、再入库。这个套路以前是你手动执行的现在你可以把它固化为一个SKILL。以后你只需要说“用老套路处理这批数据”剩下的交给智能体。1.2 不定义SKILL的代价是什么有人喜欢偷懒觉得直接在对话里描述指令让模型自己发挥就行。短期看没问题但长期看有四个致命伤。第一是不可复用。你今天花三十分钟描述清楚“怎么处理这个报表”明天再来一批新报表你又得从头描述一遍模型还未必听得懂你的新语境。第二是质量不稳定。大模型自由发挥容易飘今天给你用Pandas清洗明天可能就换成了OpenPyXL实现方式一变出错概率和结果风格都会跟着变。第三是无法调试优化。过程不可控你就没法针对“翻车”的环节去定向补强。第四是工具链割裂。你没法在技能里封装复杂的命令行调用、多步骤API交互和特定的异常重试逻辑只能任由模型发挥。所以你看自定义SKILL不是在“增加工作量”它是在“给智能体上保险给使用者省心”。想明白这件事你才能理解为什么值得花心思把每一个SKILL都写得到位、写得专业。2. 自定义SKILL的总体设计与文件结构2.1 从零开始一个SKILL的本质是一份带执行逻辑的说明书OpenClaw的SKILL机制和很多同类框架比如早期的CogAgent、Claude Skills这类概念的设计有共通之处。它的核心逻辑是智能体在需要完成任务时会根据你的自然语言描述去匹配一段预先定义好的“结构化技能包”然后按技能包里的指令路径去调用脚本、工具或API。所以一个SKILL包不是单一文件它通常包含以下三大件技能说明文件通常叫SKILL.md给智能体“看”的说明书写明这个技能是干嘛的、在什么场景下触发、需要哪些输入、输出什么结果、注意什么坑。参数定义文件通常是JSON Schema或YAML声明这个技能需要哪些参数、参数类型和约束智能体看到这个文件才知道“调用时要填哪些东西”。执行脚本Python、Shell、Node.js等真正干活的代码负责把智能体传来的参数转化成实际动作写文件、调API、跑数据分析。有人会把TOOL也混进来。我再打个比方TOOL就像一把螺丝刀SKILL则是一整个“用螺丝刀把这块面板拆下来再装上”的操作规程。你可以用螺丝刀做很多事但SKILL规定了“什么顺序拧、拧几圈、拧完怎么检查”。2.2 技能仓库与发现机制智能体怎么找到你的技能OpenClaw在运行时会维护一个SKILL集合默认存放在技能目录下常见的是~/.openclaw/skills/或项目根目录的skills/。你可以创建子目录但每个技能必须有一个独立目录目录名就是技能名也就是触发词。举个例子你想做一个“从商品详情页抓取价格并生成对比表”的技能。你可以建目录~/.openclaw/skills/ └── product_price_tracker/ ├── SKILL.md ├── schema.json └── run.py智能体匹配技能的时候会扫描所有子目录中的SKILL.md解析里面标注的“技能名称”“技能描述”“适用场景”然后和用户当前的指令做语义匹配。这里有个设计细节非常关键SKILL.md里的描述写得越贴近真实任务场景匹配准确率越高。你写“处理价格”就不如写“从跨境电商或B2B网站的商品详情页中提取价格、折扣、库存状态生成对比表格”效果好。前半段前言我用了不少篇幅讲“为什么”现在开始动真格的把文件内容逐一拆解。3. 手把手拆解SKILL的三大核心文件3.1 技能说明书SKILL.md这部分决定智能体懂不懂你SKILL.md通常是Markdown格式但它的作用不是给人类看的文档而是给大模型做“系统级提示注入”的文本。它一般由三部分组成YAML格式的Frontmatter元信息、一段详细的自然语言说明、以及若干示例。我给一个可以作为模板的写法你替换成自己的场景即可--- name: product_price_tracker description: 用于从指定的商品详情页URL中提取价格、原价、折扣、库存状态和SKU信息并输出为CSV对比表。适合电商比价、竞品监控、采购核价等场景。 version: 1.0.0 author: demo_developer parameters_schema: schema.json execution_command: python run.py --- # Product Price Tracker ## 功能概述 本技能能够批量抓取用户提供的一组商品链接自动识别页面中的价格结构和促销信息最后生成一份格式统一的CSV文件。 ## 使用场景 - 用户在对话中提出“比较这几个链接的商品价格” - 用户需要定时监控竞品价格变动 - 用户提供了多个电商平台的商品链接希望统一汇总 ## 执行规则 1. 接收参数urls字符串数组和output_path输出文件路径。 2. 对每个URL发送HTTP请求使用内置解析器识别结构化数据。 3. 如果页面结构发生变化自动尝试备用CSS选择器均失败则记录为空值。 4. 最终结果通过标准JSON输出到指定路径。 ## 注意事项 - 所有请求必须在10秒内超时失败后重试最多2次。 - 不得修改原始URL不得访问链接以外的页面。 - 如果参数中没有提供output_path默认写入当前工作目录下的result.csv。这里面的“注意事项”容易被忽略但其实是精髓。智能体读到“所有请求必须在10秒内超时”它在写代码或调用脚本时就会带上超时控制读到“不得修改原始URL”它就会避免因为拼接参数而导致的越权访问或爬取错误。你写得越细模型在执行时就越少自由发挥。3.2 参数说明书schema.json这部分决定程序稳不稳为什么一定要一个独立的JSON Schema因为大模型虽然聪明但它从自然语言里抽取参数时偶尔会自作主张地改变参数形式。你说“给我处理这几个链接”它可能直接把urls传成逗号分隔的字符串也可能传成JSON数组。如果没有约束你的脚本就得花大量时间去兼容这些脏输入。schema.json就是用来消灭这种歧义的。我们来看具体写法{ name: product_price_tracker, parameters: { type: object, properties: { urls: { type: array, items: { type: string, format: uri }, description: 需要提取价格的商品详情页链接列表, minItems: 1, maxItems: 50 }, output_path: { type: string, description: CSV输出文件的完整路径缺省为result.csv, default: result.csv }, include_metadata: { type: boolean, description: 是否在输出中包含页面标题和抓取时间, default: true } }, required: [urls] } }有几个细节值得注意。一是format: uri要求模型尽量传合法的URL格式减少字符串解析出错的风险。二是minItems和maxItems的约束避免用户一次丢给你几百个链接导致脚本跑半天甚至超时崩溃。三是default字段最好把常见的缺省值都写上模型在调用时就不会反复追问用户“输出路径是什么”交互体验会顺畅很多。3.3 执行脚本run.py这部分决定活儿干得漂不漂亮接着写执行脚本。尽量把脚本写得“像命令行工具一样干净”通过标准输入或参数接收JSON再输出JSON。这样好处是方便调试也方便以后做异步任务。先看一个基础版本的例子基于Python#!/usr/bin/env python3 import sys import json import csv import requests from bs4 import BeautifulSoup from urllib.parse import urlparse import time def parse_page(url, timeout10): try: resp requests.get(url, headers{ User-Agent: Mozilla/5.0 ..., Accept-Language: zh-CN,zh;q0.9 }, timeouttimeout) resp.raise_for_status() except Exception as e: return {url: url, error: str(e)} soup BeautifulSoup(resp.text, html.parser) title soup.title.get_text(stripTrue) if soup.title else price None # 主干逻辑依次尝试多组选择器 for selector in [.price-now, #price, [data-price], .price]: el soup.select_one(selector) if el: raw el.get_text(stripTrue) try: price float(raw.replace(¥, ).replace(,, )) except ValueError: pass if price is not None: break stock None stock_el soup.select_one(.stock-status, #stock) if stock_el: stock stock_el.get_text(stripTrue) return {url: url, title: title, price: price, stock: stock} def main(): payload json.loads(sys.stdin.read()) urls payload.get(urls, []) output_path payload.get(output_path, result.csv) include_metadata payload.get(include_metadata, True) results [] for url in urls: results.append(parse_page(url)) time.sleep(0.5) # 礼貌抓取降低被封风险 # 输出CSV with open(output_path, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[url, title, price, stock]) writer.writeheader() for row in results: if include_metadata: writer.writerow(row) else: writer.writerow({url: row[url], title: , price: row.get(price), stock: row.get(stock)}) print(json.dumps({status: ok, output: output_path, count: len(results)}, ensure_asciiFalse)) if __name__ __main__: main()说点真心话这个脚本我故意写得简单但核心的健壮性设计都已经在里面了。异常捕获覆盖了网络错误和解析错误价格解析时用多种选择器去试CSV写入用了utf-8-sig编码这样Excel打开不会乱码。你在这个框架上扩展最需要关注的反而不是业务逻辑多复杂而是边界情况处理得好不好。4. 实操过程与核心环节实现让OpenClaw跑起你的技能4.1 技能注册与目录规整的实操文件写完之后需要让OpenClaw“看到”它们。大致流程如下创建目录在OpenClaw的技能根目录下新建一个以技能名命名的文件夹。放置文件将SKILL.md、schema.json、run.py放进去如果技能单纯是“不需要脚本的提示词指令”也可以不写run.py但那样扩展性差我不推荐。静态校验打开OpenClaw的调试界面输入“列出所有可用技能”或检查启动日志确认新技能被加载。独立测试先在系统终端里手动跑一遍脚本避免把OpenClaw的报错和脚本自身的报错混在一起。这一步有个小经验目录名千万不要用空格或大写字母最好用全小写加下划线。否则在Linux环境下大小写敏感OpenClaw的自动加载器偶尔会误判目录名和技能名不一致。我自己最早建过一个Web_Scraper目录结果日志里一直报告找不到技能换成web_scraper之后立刻正常。4.2 通过OpenClaw控制台触发技能的正确姿势技能注册好之后你可以直接在OpenClaw的聊天界面里输入指令。但要注意指令里最好包含“触发场景关键参数”要素。比如请使用商品价格追踪技能帮我抓取以下三个链接的价格https://example-a.com/item/123https://example-b.com/p/456输出目录设为/home/myuser/reports/。当然更好的做法是配合参数Schema直接让客户端结构化传参。OpenClaw支持在调用技能时通过JSON传递参数类似于{ command: product_price_tracker, parameters: { urls: [https://example-a.com/item/123, https://example-b.com/p/456], output_path: /home/myuser/reports/price.csv } }有些版本还支持在输入框内输入斜杠命令直接触发技能比如/product_price_tracker {urls: [...]}。这类快捷方式本质上是绕开了“语义匹配”环节直接强制调用指定技能适合在明确知道要用哪个技能时使用。4.3 联动多个SKILL从单技能到工作流的进阶玩法单独一个技能能做的事终归有限。你在实际项目中大概率会需要让多个SKILL协同工作。比如“做一份竞品监控日报”可能需要三个技能配合url_collector根据关键词搜索和收集竞品商品链接。product_price_tracker逐个抓取价格入库。report_generator根据上一阶段产出的CSV生成带图表的PDF或HTML报告。如何编排这些技能OpenClaw本身的Agent编排能力允许对话中指定连续动作。比如你可以说先用链接收集技能找出“无线蓝牙耳机”这个关键词下的前10个商品链接再用商品价格追踪技能抓取价格最后用报告生成技能把结果做成简版日报。智能体这时候的行为是先激活url_collector拿到结果后把url_collector输出的结构化JSON传入product_price_tracker的参数槽再拿最终结果调report_generator。这种“技能流水线”思想非常重要它让每个技能都保持单一职责又能互相喂数据。但这里有个安全问题传递过程中参数很容易“变质”。当你把A技能的输出作为B技能的输入时B技能往往拿不到A输出的原始对象而只能拿到一段渲染过的人类可读文本。解决方式有两种规范中间的“数据契约”比如约定A输出永远是JSON且字段名固定B脚本里直接解析标准JSON。使用内存中的上下文暂存区A技能把结果写入一个临时文件或共享变量B技能从那里读取原始数据。我自己的经验是尽量不要依赖智能体“带话”。大模型在转述时是可能丢信息的比如把数字四舍五入、把布尔值改成字符串。宁可让技能之间通过共享文件或数据库中转也不要直接让它们在对话里传参。4.4 日志与错误反馈定位问题的基础设施几乎没有哪次开发是不出错的。当技能跑挂的时候OpenClaw一般会返回一个笼统的错误描述比如“技能执行失败脚本返回非零退出码”。指望它替你精确定位到哪一行代码出错那是想多了。所以我强烈建议在脚本里做三件事打印结构化进度日志每完成一个URL输出一条{event: fetch_ok, url: ...}。捕获顶层异常并输出JSON错误让OpenClaw收到的是“友好信息”而不是一段Traceback。记录退出码脚本正常返回0异常返回非0并在日志里说明错误类别。举个例子parse_page里如果连续五个URL超时就可以直接输出一条汇总错误而不是逐条吞掉然后令脚本退出码为2。这样OpenClaw的调度器能更快判断“这个技能现在不健康不要再傻傻地循环调用”。5. 常见问题与排查技巧实录5.1 技能匹配不到为什么我说“抓价格”它却用起了普通浏览器操作这是个高频问题。智能体有时候不会调用你的技能而是自作主张用内置的浏览器工具去访问页面。原因往往是SKILL.md里的描述写得太泛或者和现有TOOL的描述撞车了。比如你写“抓取页面内容”内置工具也叫“页面抓取”模型当然优先选内置工具因为内置工具的调用成本更低、不涉及参数Schema校验。排查方向确认技能描述里有明确的“触发词”和“非适用场景”比如“仅适用于电商详情页价格提取不适用于正文提取”。把内置TOOL和自定义SKILL的场景区分开尽量在描述里强调“必须使用本技能”。再次设置schema的时候把required字段里的参数都设为显式必填模型识别为“专用技能”的倾向会提高。5.2 参数总是传错模型把urls传成一个字符串怎么办我在前面已经提过Schema的重要性但还有更绝的一招在SKILL.md的“执行规则”里用示例明确告诉模型“参数必须是JSON数组”。比如正确的调用示例product_price_tracker.urls [https://a.com/1, https://b.com/2]而不是product_price_tracker.urls https://a.com/1,https://b.com/2。大模型对“示例范本”的跟随能力比对抽象描述强得多。这有点像教人做事时说一百句规则都不如直接演示一遍。同时在执行脚本里也做一次防御性清洗如果收到的是字符串就尝试用JSON解析或正则切分转成列表。两头都堵住才能最大程度降低翻车概率。5.3 技能超时三分钟跑不完怎么办OpenClaw的对话型任务对技能执行时长是有容忍区间的但如果你的脚本里没有设置超时控制一个请求挂住就会拖垮整次调用。我自己就把过一次坑某个数据源的服务器响应速度极慢脚本默认请求无超时结果OpenClaw等了五分钟之后直接判定技能调用失败而代码本身逻辑是好的。解法思路在每个外部请求上加timeout10。批量任务里加“最大执行数”限制比如最多处理20个URL。长任务最好拆成“分批进度汇报”的方式每次只处理一小部分把中间结果写入持久化存储再由后续技能续跑。5.4 技能加载了但执行报“找不到命令”或“缺依赖”很多人写完Python脚本放到技能目录就完事了结果OpenClaw环境里压根没装requests和bs4。你本地跑得好好的不代表执行环境里有同样的依赖。这个问题也很常见。检查清单在技能目录下放一个requirements.txt并在文档里写明安装方式。在SKILL.md里增加“环境依赖”一项告诉智能体“如果运行失败先尝试安装以下包”。尽量用Python标准库实现核心逻辑减少对第三方库的依赖。比如抓网页可以用urllib.request解析HTML可以用html.parser虽然写起来费劲点但部署环境兼容性会好很多。5.5 技能返回结果被截断数据太多了智能体只回一句话这是另一个非常隐蔽的坑。脚本输出了一大坨JSON但智能体在回复用户时可能只提炼出一句话“我已经抓取并保存了结果”用户却看不到明细。这个问题本质上不是技能不好而是“人机交互的呈现问题”。我的办法是脚本除了输出结构化数据外还强制生成一份人类可读的摘要文件比如Markdown或CSV并在技能返回信息里把“摘要文件路径”和“首条数据预览”一并输出。这样用户既能在对话里看到大体内容又能拿到完整文件去细看。6. 进阶技巧让SKILL具备自我纠错与自适应能力6.1 假如页面结构变了技能该怎么“自救”爬虫和页面解析最大的痛点就是“页面改版”。你的CSS选择器今天还能选中价格明天可能就失效了。传统爬虫程序只能人工去改代码但OpenClaw的SKILL因为有Agent的加持是可以实现“半自动修复”的。操作思路解析脚本里不把选择器写死在代码中而是作为可配置参数。当主选择器匹配不到元素时脚本返回{status: missing_selector, page_snapshot: ...}这时候智能体可以根据快照自己去尝试用自然语言理解页面结构再生成新的选择器注入配置。这就等于把“反馈-调整-重试”的闭环放在了Agent层面而不仅仅是代码层面。不过这里要注意安全边界。不要让智能体无限重试否则它可能会对同一样本发起几十次请求看起来像一小段时间内的突发流量容易触发反爬。建议最多重试一次并把失败详情记录到日志库中供人工复查。6.2 如何给SKILL写“使用护栏”和“安全限制”自定义技能不是越强大越好有时候也需要设置限制防止Agent误用。比如某个技能专门读取本地文件你肯定不希望它被用来读取/etc/passwd再比如某个技能能发送HTTP请求你肯定不想让它随意访问内网地址。所以在SKILL.md里要明确写入“禁止操作”和“允许范围”。例如“仅允许处理/data/ftp/目录下的文件禁止访问其他路径下的绝对路径。”“只允许访问用户提供的公共互联网URL禁止访问内网IP段如192.168.0.0/16、10.0.0.0/8、127.0.0.1。”“不得在脚本中直接执行用户输入的命令字符串。”在OpenClaw里这层约束需要双向起作用一方面靠Agent理解文档后自觉遵守另一方面靠脚本内部的权威校验把非法输入挡在门外。安全设计永远不要只依赖模型自律代码层面的硬校验才是底线。6.3 技能版本管理改了旧技能怕影响线上怎么办自定义SKILL会像软件代码一样迭代。你可能会今天觉得“描述不够准确”明天觉得“脚本太慢”后天又要“加一个新参数”。如果直接动手改文件改崩了很难回滚。我的习惯是为每个技能目录初始化一个git仓库每次修改前先提交。同时在SKILL.md的Frontmatter里维护一个version字段。这样有两个好处随时回滚到上一个稳定版本。智能体在遇到问题时可以附带“当前技能版本号”方便排查。如果你想更稳妥可以做一个“技能影子目录”。把当前稳定版复制的副本放在safe_versions/下当新版有问题时直接在加载路径上指向旧版。但那套方案更适合团队协作个人使用的话Git就完全够了。6.4 多模态技能的实践示范从一个纯文本技能开始有人总觉得SKILL只能处理文本或API其实不是。我在某个模拟项目里做过一个“票务系统OCR技能”用户上传一张演出票的照片技能需要提取票价、座位号、日期。这个技能的内部实现是脚本启动时调用一个OCR服务本地模型或云端API识别出文本后再用正则和关键词匹配提炼关键字段最后输出结构化JSON。整个过程依然遵循着我们已经讲过的结构——SKILL.md描述任务、schema.json接收图片路径、run.py执行处理。这类多模态技能和纯文本技能的核心差异在于参数里多了一个media_path或image_url字段。你的SKILL.md描述要更明确地告诉智能体“当用户发送图片时先保存图片到临时目录再将图片路径传入技能”否则模型很可能会直接把图片内容塞进JSON参数里导致脚本完全无法解析。7. 我的个人体会写自定义SKILL这件事本质上是“把行为经验转化成数据资产”。一开始你会觉得麻烦写Schema、写说明、写脚本、测试、调优一整套下来比手动处理慢得多。但一旦你手上积累了十几个调教成熟的技能后续的自动化流程就会指数级地顺滑起来你的智能体也会从“偶尔聪明的实习生”进化成“深谙你业务逻辑的老搭档”。最后分享一个小技巧一个新技能上线后不要急着做复杂的编排先单独裸测它20次。用不同风格的指令去调它、故意给它传错参数、把数据源弄成超时逼着它暴露问题。等这20次跑稳了再上复杂流程你会发现翻车率低到惊人。技能这种东西和人一样——前期多塔台后期少返工。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑