把OpenClaw从“只会聊天”变成“能干活”,是我最近折腾得最有成就感的一件事。以前我让AI帮忙查路线、看商品、放首歌,它只能给我一段文字建议,然后我自己开App操作。现在我直接让它调用高德导航、京东商品搜索和QQ音乐播放控制,一句话就能把“行、购、娱”三件事串起来。这篇内容适合已经能跑起OpenClaw、但还停留在纯对话阶段的朋友,也适合正准备部署OpenClaw、想在初始阶段就把实用技能装进去的人。我会把三个技能从API申请、脚本开发到最终接入Skill的完整过程都写出来,包括我踩过的坑。
先说清楚,我这里没有用什么特殊框架,就是OpenClaw自带的Skill机制加上Python和PowerShell脚本。OpenClaw说到底是一个让大模型能调用外部工具的智能体框架,Skill就是给它配的“工具箱说明书”。只要说明书写得清楚,模型就知道什么时候该掏出哪个工具、怎么用、参数怎么填。
1. 为什么先把高德、京东、QQ音乐做成Skill
1.1 先搞清楚OpenClaw的Skill到底在做什么
很多人在OpenClaw里加技能,第一反应是“写死一段命令”,让AI收到特定关键词就执行固定脚本。这其实不是Skill的正确打开方式。Skill的本质是给大模型一份结构化的“使用手册”,里面说明了这个技能能干什么、什么时候调用、需要哪些参数、执行脚本的方式。模型会根据用户对话的意图,自己判断该不该调用。
打个比方,你给一个新同事一个工具箱,里面每个工具旁边贴了一张标签,写着“适合拧螺丝”“适合裁纸”。你不用每次告诉他“现在用螺丝刀”,他看到螺丝自然就会去拿。Skill就是那个标签,OpenClaw模型就是那个新同事。
所以我在配置这三个技能时,重点不是把功能代码写多复杂,而是把SKILL.md里的描述写得足够准。比如高德导航技能,我会写清楚“当用户需要从A地到B地的驾车路线、距离、预计时间时使用”。如果描述模糊,模型可能该调用时不调用,不该调用时反而执行了。
1.2 为什么选高德、京东、QQ音乐这三个
高德导航、京东商品搜索、QQ音乐播放控制,分别代表了三种不同的集成难度,正好把常见场景都覆盖了。
高德是典型的“官方开放API + 标准REST接口”,申请Key、调接口、返回JSON,路径非常清晰。京东则是“有官方接口但很多人不知道入口”,它没有直接给个人开发者开放网页搜索的API,但京东联盟开放平台提供了商品查询接口,能搜索商品、拿到价格和推广链接,合规又稳定。QQ音乐最难,因为官方没有面向个人的播放控制API,也不可能让你随意远程控制PC端。我最后采用的方案是模拟系统媒体键,让OpenClaw调用本机PowerShell脚本控制播放状态。
这三个技能从易到难,配置完基本就能理解OpenClaw集成外部服务的通用套路:注册对应平台账号、获取凭证、写脚本、把脚本封装成Skill、测试、调优。掌握了这套流程,以后接任何API都轻车熟路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 高德导航技能:Key申请、地理编码和路线API的完整配置
2.1 申请Key并选对接口类型
高德开放平台的接入流程不复杂,但第一次容易搞混。登录高德开放平台后,进入“应用管理”创建一个应用,然后在应用下添加Key,类型务必选择“Web服务”。很多人选了“Web端(JS API)”或“Android”,后面请求REST接口就会一直报错。我一开始就踩了这个坑,申请了好几个Key才发现是Key类型选错了。
Web服务Key不像其他类型需要绑定域名白名单,但建议每个独立用途单独建一个Key。我把OpenClaw用的Key和日常小程序用的Key分开,这样即使其中一个Key触发配额告警,也方便定位是哪个服务在用。高德个人开发者每日调用量有限,地理编码接口一般每天几千次,个人使用完全够,但如果你的OpenClaw被频繁触发导航查询,还是要注意控制频率。
此外,高德的REST接口返回的是JSON,包含status字段。看到status等于1表示成功,等于0说明失败,同时会有一段info告诉你原因。这个字段在排查问题时很有用,我习惯在脚本里先判断status,再处理数据,避免解析一个空对象导致后续操作崩溃。
2.2 最小可用脚本:地理编码加驾车路线规划
高德导航最常用的链路是“输入地址文本 -> 转成经纬度 -> 再请求路线规划”。我用Python写了一个简单版本,放在Skill的scripts目录下。
python复制import requests
import urllib.parse
AMAP_KEY = "你的Web服务Key"
def geocode(address: str):
url = "https://restapi.amap.com/v3/geocode/geo"
params = {
"key": AMAP_KEY,
"address": address
}
r = requests.get(url, params=params, timeout=10)
data = r.json()
if data["status"] == "1" and data["geocodes"]:
location = data["geocodes"][0]["location"]
return location
return None
def driving_route(origin: str, destination: str):
url = "https://restapi.amap.com/v3/direction/driving"
params = {
"key": AMAP_KEY,
"origin": origin,
"destination": destination
}
r = requests.get(url, params=params, timeout=10)
data = r.json()
if data["status"] == "1":
route = data["route"]
paths = route["paths"][0]
distance = int(paths["distance"])
duration = int(paths["duration"])
return {
"distance_km": round(distance / 1000, 1),
"duration_min": round(duration / 60, 0)
}
return None
脚本的逻辑很简单:geocode函数把“北京市朝阳站”这样的模糊地址解析成“116.437,39.921”格式的经纬度,driving_route函数再拿着起终点经纬度请求驾车路线。高德的坐标顺序是“经度,纬度”,这个一定不要搞反。我早期直接把维度放前面,结果每次导航都偏到离谱的位置。
2.3 返回导航链接比返回经纬度更实用
虽然路线规划接口能返回距离和预计时间,但如果你希望用户一键跳转到高德App进行导航,更推荐生成一条高德URI链接。
code复制https://uri.amap.com/navigation?to=经度,纬度,目的地名称
这个链接可以直接在手机浏览器打开并唤起高德导航,不需要自己处理复杂的路线渲染。我的Skill最终实现的效果是:OpenClaw识别到用户想去某地后,先调用地理编码拿到坐标,然后返回“从当前位置出发,大约需要XX分钟,全程XX公里”这样的信息,同时附上导航链接。实测下来体验最好,用户点一下就能开始导航。
不过这里有一个问题:uri.amap.com链接里的to参数,经纬度要用英文逗号分隔,目的地名称要URL编码。我的Python脚本里用urllib.parse.quote处理了中文名称,否则中文会乱码,跳转后很可能定位失败。
2.4 高德配置中的几个坑
- 地址解析失败:如果用户只说“去超市”,地理编码接口基本会返回空。我让LLM在调用脚本前先尝试补充上下文,比如根据对话历史判断用户常去的超市名,再传给脚本。如果实在解析不到,就让AI反问用户要具体地址,而不是硬拼一个坐标出来。
- 配额告警:高德控制台能看到每日调用量,技能部署早期最好每天扫一眼。我之前遇到过一次
status为0且info提示“USER_DAILY_QUERY_OVER_LIMIT”,就是配额超了,换一个Key直接解决。 - Key安全问题:如果OpenClaw部署在云服务器上,并且你后续开放给其他人使用,一定不要把Key写在对话输出里。请求失败时也不要打印完整Key,只打印后四位用于排查就好。
3. 京东商品搜索技能:用联盟API解决“没有开放平台权限”的尴尬
3.1 为什么不用爬虫而用联盟API
很多人在做京东商品搜索时,第一反应是爬网页或用非官方接口。我不推荐这么做,原因很简单:https://item.jd.com页面的DOM结构经常改,昨天还能解析的字段今天可能就没了;而且频繁请求有风控风险。最稳妥的路径是去京东联盟开放平台注册账号,用它的官方商品查询接口。
京东联盟是京东官方的CPS推广平台,个人可以注册。注册通过后,在“推广管理”里创建一个推广位,拿到推广位ID(PID),然后在“开放平台”应用管理里创建一个应用,就能获得app_key和secret_key。这个过程大概只需要一两天审核,比京东开放平台那套面向企业商的流程要友好得多。
有人会问:联盟API不是做返利用的吗?能用来做普通的商品搜索吗?完全可以。它的jd.union.open.goods.query接口支持按关键词搜索商品,返回商品名称、价格、店铺、优惠券等信息。即使不做返利,只是当商品搜索接口用也非常合适。
3.2 签名规则与请求示例
京东联盟API用的是签名校验。规则大致是:把请求参数按字典序排序,拼成字符串,用secret_key做MD5签名。不同版本的官方SDK签名实现略有差异,所以我当时直接复制了官方Python示例里的签名函数,保险起见大家也这么干。
下面是可以跑通的请求示例:
python复制import hashlib
import requests
import time
import json
APP_KEY = "你的app_key"
SECRET_KEY = "你的secret_key"
def sign(params, secret):
src = secret + "".join(f"{k}{v}" for k, v in sorted(params.items())) + secret
return hashlib.md5(src.encode("utf-8")).hexdigest().upper()
def search_goods(keyword: str, page: int = 1, page_size: int = 5):
params = {
"method": "jd.union.open.goods.query",
"app_key": APP_KEY,
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
"v": "1.0",
"param_json": json.dumps({
"goodsReqDTO": {
"keyword": keyword,
"pageIndex": page,
"pageSize": page_size
}
}, ensure_ascii=False)
}
params["sign"] = sign(params, SECRET_KEY)
r = requests.get("https://api.jd.com/routerjson", params=params, timeout=10)
data = r.json()
return data
这里有个小细节:param_json是JSON字符串,字符串里的中文不需要转义吗?我测试时保留中文,签名也能对上。但如果你的签名函数是纯拼接方式,中文编码不同可能会导致sign不一致。怀疑签名错误时,先用官方“签名验证工具”或官方SDK跑一遍,排除是参数顺序、编码字符集还是接口地址的问题。
3.3 返回结果里的“有效信息”处理
商品查询接口返回的字段非常多,有几十个,但OpenClaw只需要把最关键的几个给用户看:商品名称、价格、店铺、券后价、是否自营。我在脚本里过滤了这些字段,避免大模型把一堆无用数据塞回给用户。
python复制def format_goods(data):
if "jd_union_open_goods_query_response" not in data:
return []
result = data["jd_union_open_goods_query_response"]["result"]
goods_list = json.loads(result).get("data", [])
formatted = []
for g in goods_list:
formatted.append({
"title": g.get("skuName"),
"price": g.get("priceInfo", {}).get("price"),
"shop": g.get("shopInfo", {}).get("shopName"),
"url": g.get("materialInfo", {}).get("jumpUrl", "")
})
return formatted
京东接口的坑在于result字段本身是一个JSON字符串,不是字典。我第一次处理时直接当字典取data,结果一直报TypeError。后来打印了原始返回才看清结构。这个问题在Stack Overflow上也不少人问,但官方文档写得确实不够醒目。
3.4 让商品搜索更实用的小增强
基础搜索能做起来后,我加了一个“转链”功能。京东联盟有单独的转链接口,可以把商品链接、推广位ID和PID绑在一起,生成带推广位的短链接。这个对个人做返利助手比较有用,但对大部分人可能用不上,我更推荐在搜索参数里加强筛选能力。
jd.union.open.goods.query的goodsReqDTO里可以传很多条件,比如priceLow、priceHigh、sortName(排序字段)、isCoupon(只看有券商品)。我把这些参数做成了Python脚本的可选参数。OpenClaw的模型在调用时,会根据用户的问题自动填。比如用户说“找200到300元的机械键盘,按销量排序”,脚本就会拼出priceLow=200、priceHigh=300、sortName=inOrderCount30Days。如果没有这些可选参数,模型只能把关键词传进去,返回结果就容易跑偏。
4. QQ音乐播放控制技能:不依赖官方API的本地控制方案
4.1 方案选型:为什么最终选择模拟系统媒体键
QQ音乐官方没有给个人开发者提供播放控制API,网上流传的很多所谓“网页版接口”也存在随时失效的风险。对于OpenClaw跑在个人电脑上的场景,模拟系统媒体键是最稳的方案。
系统媒体键就是键盘上控制多媒体播放的那几个键:播放/暂停、上一曲、下一曲。Windows系统里,它们对应虚拟键码0xB3(媒体播放/暂停)、0xB1(上一曲)、0xB0(下一曲)。PowerShell可以通过keybd_event这个Windows API发送这些虚拟键,QQ音乐接收到后会像你按了键盘媒体键一样执行对应操作。这个方法不仅对QQ音乐有效,网易云音乐、酷狗音乐也都能识别。
要说明的是,这个方案只适用于OpenClaw和QQ音乐跑在同一台电脑上的场景。如果OpenClaw部署在云服务器,就没办法直接控制本地的QQ音乐了。我自己的用法是Mac Mini和Windows主机都跑了OpenClaw,QQ音乐主要装在有扬声器的那台Windows机器上,所以这个限制对我影响不大。
4.2 实现一个可被OpenClaw调用的播放控制脚本
我用PowerShell写了一个控制脚本,放在Skill的scripts/music_control.ps1里。
powershell复制param(
[string]$Command = "playpause"
)
Add-Type -TypeDefinition '
using System;
using System.Runtime.InteropServices;
public class MediaKey {
[DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
}';
$keyMap = @{
"playpause" = 0xB3
"next" = 0xB0
"prev" = 0xB1
}
if ($keyMap.ContainsKey($Command)) {
$vk = $keyMap[$Command]
[MediaKey]::keybd_event($vk, 0, 0, [UIntPtr]::Zero)
Start-Sleep -Milliseconds 50
[MediaKey]::keybd_event($vk, 0, 2, [UIntPtr]::Zero)
}
keybd_event执行一次完整的按键操作要分两步:先发送按下,再发送抬起。中间最好间隔几十毫秒。我之前只发了一次按下事件,结果播放状态一切正常,但偶尔会触发“长按”效果,导致QQ音乐弹出额外菜单。加上Start-Sleep之后就没再遇到过。
在OpenClaw的Skill配置里,我会让它这样执行:
bash复制powershell -ExecutionPolicy Bypass -File scripts/music_control.ps1 -Command playpause
注意-ExecutionPolicy Bypass不能省,否则脚本可能被PowerShell执行策略拦截。
4.3 让AI理解“播放周杰伦的歌”这类指令
只支持播放暂停显然不够,用户更自然的指令是“放一首周杰伦的歌”。这里我做了个妥协:本地控制脚本只管播放状态,同时给OpenClaw配置一个“搜索链接生成”能力。当用户提出想听特定歌曲时,OpenClaw会找出一首匹配的歌名,然后生成QQ音乐的网页搜索链接,比如:
code复制https://y.qq.com/n/ryqq/search?w=晴天
这个方案虽然不能一键让QQ音乐开始播放指定歌曲,但能帮用户省掉“打开App再手动搜索”的步骤。如果你实在需要自动点歌,可以再用桌面自动化工具(比如AutoHotkey)模拟键盘快捷键呼出QQ音乐搜索框,再输入歌名回车。我试过一次,能成功,但界面稍微一变就可能失效,维护成本比较高。为了稳定,我最终决定只做媒体键控制和搜索链接生成,把“完美点歌”留给后续二次开发。
4.4 播放控制里的几个坑
- 没有管理员权限时,PowerShell调用Windows API可能被拦截。如果脚本没有反应,先手动打开一个管理员PowerShell窗口执行一次,看看是否报错。
- QQ音乐运行在后台时,媒体键控制是有效的;但如果有多个播放器同时运行,媒体键会被系统默认路由到最近活跃的媒体会话,可能出现控制错对象的情况。我建议在SKILL.md里提醒用户“先激活QQ音乐窗口”,或者在调用脚本前用
Start-Process把QQ音乐带到前台。 - 在Mac上,
keybd_event不可用,需要换AppleScript。比如控制“音乐”App可以用osascript -e 'tell application "Music" to playpause'。但QQ音乐Mac版的AppleScript支持不完整,所以我的技能脚本是根据操作系统分支处理的。Windows主用PowerShell,Mac就退化为打开QQ音乐App并提示用户手动播放。
5. 把三个Skill组合进OpenClaw:配置结构、测试与协同场景
5.1 Skill目录结构与注册方式
OpenClaw的Skill目录一般在用户目录下的.openclaw/skills/,每个技能一个文件夹。我的习惯是这样一个结构:
code复制~/.openclaw/skills/
├── amap_navigation/
│ ├── SKILL.md
│ └── scripts/amap.py
├── jd_goods_search/
│ ├── SKILL.md
│ └── scripts/jd_search.py
└── qqmusic_control/
├── SKILL.md
└── scripts/music_control.ps1
SKILL.md就是给LLM看的使用说明。以高德导航为例,我写的内容大致是:
markdown复制---
name: amap_navigation
description: 当用户需要查询驾车路线、导航链接、两地距离或预计时间时使用。
---
用户在对话中提到“导航”“路线”“去某地”时,调用 scripts/amap.py。
输入参数:
- destination: 目的地名称或地址
- origin: 可选,默认为“当前位置”
输出:
- 从起点到终点的距离、预计驾驶时间
- 高德导航跳转链接
关键就是description字段。OpenClaw的模型会根据这个描述判断什么时候该调用。如果你把描述写得太窄,比如只写“导航”,模型在听到“从公司回家要多久”时可能就不知道该用这个技能。我后来改成“驾车路线、目的地、距离、预计时间”等更宽泛的语义,识别率提升了很多。
写完SKILL.md后需要重启OpenClaw让它重新加载技能。如果日志里没有加载到技能,先检查文件夹路径和YAML格式。YAML里不要用Tab缩进,必须用空格。
5.2 实测:三个技能一起跑的表现
把三个技能都配好后,我试了一句综合指令:“帮我查一下下班以后从公司到附近的永辉超市怎么走,顺便看看京东上有哪些降噪耳机,再放点轻音乐。”
OpenClaw的实际表现是:先调用高德导航脚本,返回了距离和导航链接;然后调用京东商品搜索脚本,列了三个降噪耳机的价格;最后调用QQ音乐播放脚本,并且因为用户说“放点轻音乐”,模型还主动补充了一句“我帮你播放了,接下来可以用上一曲下一曲切换”。
整个过程我比较满意,但暴露了一个问题:三个技能串行执行,总耗时接近15秒。尤其是京东接口响应本身就慢,容易让用户觉得卡顿。后来我在SKILL.md里注明:“如果用户同时提出多个请求,按照导航、购物、音乐的顺序逐个执行,并在每个结果前加上明确的前缀。”这样模型输出会更结构化,用户体验也好一些。
5.3 排查错误的方法论
配置Skill最烦的就是“AI没有调用你的技能”。我的排查顺序是:
先看OpenClaw的日志,通常位于~/.openclaw/logs。日志里会记录模型最终生成的工具调用参数,如果模型压根没提到技能名,那就是SKILL.md描述问题;如果调用了但脚本报错,那就是脚本问题;如果脚本正常但结果不正确,很大概率是API参数问题。
我整理了一个常见问题表:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 高德返回status=0 | Key类型不对或配额超限 | 检查Key类型,换Web服务Key |
| 高德地理编码返回空 | 地址太模糊 | 提示用户提供详细地址 |
| 京东签名验证失败 | secret_key或参数字段顺序错误 | 用官方SDK对照生成签名 |
| 京东没有返回数据 | 关键词太冷门或页码太大 | 放宽关键词,调整pageSize |
| QQ音乐没反应 | 当前用户无权限或QQ音乐未激活 | 管理员运行测试,先激活窗口 |
| OpenClaw未调用技能 | SKILL.md描述不准确 | 扩展description触发词 |
这块是最有价值的排查经验。每当你觉得“AI变笨了”,先别急着怀疑模型,90%的情况是技能描述或脚本本身出了问题。
5.4 扩展思路:把三个技能串成“场景”
技能独立配置只是第一步,真正好用的是组合。我现在在OpenClaw里加了几个简单的场景规则,比如当用户说“下班回家”时,会同时触发高德导航和QQ音乐播放,默认播放我收藏的“通勤歌单”。京东搜索则独立保留,因为购物和通勤通常不是同时发生的。
这种场景编排不一定要写在代码里,直接写在SKILL.md的正文里也行。OpenClaw模型具备多步规划能力,你只要给它一个足够清晰的上下文,它就会自己拆解任务。我甚至试过把“通勤回家”定义成一句话:“当用户提到下班回家,请先查询高德路线,再播放QQ音乐,但不要做商品搜索。”效果很好。
如果你想让场景更动态,可以利用OpenClaw的记忆机制,把用户常用的家和公司地址存下来。下次用户说“导航回家”,模型会自动调用记忆里的家庭地址,不再需要重复输入。这个体验已经非常接近真正的个人助理了。
最后再说一个实际体验:这三个技能配好之后,使用频率最高的其实是高德导航和QQ音乐,京东搜索反而用得少。原因很简单,购物决策链路长,用户更愿意自己打开App慢慢挑。但作为OpenClaw能力的展示,京东商品搜索反而最能体现“AI主动帮你聚合信息”的价值。所以我建议你即使不需要购物功能,也把京东这个Skill装上,它能帮你理解“如何把一个带签名校验的API稳定封装进OpenClaw”,这套经验以后接任何付费接口都用得上。
