
1. 为什么要在 OpenClaw 里塞一套离线语音控制OpenClaw 语音控制这件事我最早是在一台不联网的工控机上折腾的。那台机器跑着 OpenClaw 做本地自动化键盘鼠标都在但操作员戴着手套敲键盘不方便于是想加一套离线语音指令。云端语音识别方案第一时间就被排除了现场没有外网数据也不允许出内网。这时候 Vosk 就成了很自然的选择——它是一个基于 Kaldi 的离线开源语音识别工具包模型下载到本地后识别过程完全在本机完成不依赖任何网络请求。Vosk 能做什么简单说它把麦克风采集到的音频流实时转成文本。它支持中文、英文等二十多种语言小模型只有 40MB 左右在树莓派、嵌入式 Linux 上都能跑。适合谁适合需要在无网络、低延迟、数据不出本地的场景里做语音控制的开发者比如智能家居中控、工业设备语音操作、离线语音助手。OpenClaw 本身是一个模块化的智能助手框架功能以插件形式存在支持 Linux、macOS、Windows配置灵活敏感操作在本地完成。把 Vosk 作为语音输入层OpenClaw 作为指令执行层两者拼起来就是一个完整的离线语音控制闭环。我试过在 16kHz 单声道、blocksize 8000 的配置下中文小模型从说完一句话到出识别结果端到端延迟大概在 300 到 600 毫秒之间具体取决于句子长度和 CPU。这个延迟对于“打开微信”“截图”这类短指令是完全够用的。下面我会从模型选型、本地服务启动、OpenClaw 侧指令映射一路写到延迟和准确率的验证动作你可以直接照着做。2. TaoToken 前置准备模型与 API 通道怎么配在正式接 Vosk 之前先把两件事理清楚一是 Vosk 模型从哪来、放哪二是 OpenClaw 如果需要调用大模型做语义兜底走哪条 API 通道。Vosk 模型本身是离线文件下载一次就行但 OpenClaw 的很多插件能力比如把模糊语音转成结构化指令、做意图理解会依赖大模型接口。这时候可以用 TaoToken 提供的统一 API 通道把模型调用集中管理省得每个插件各配一套 Key。TaoToken 的官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。注意 API 地址后面不加 UTM 参数直接用它作为 Base URL 就行。你需要先在控制台创建一个 API Key然后把它写进 OpenClaw 的配置里。模型对话的入口在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 这两个页面建议先过一遍尤其是文档里的请求格式和错误码说明。Vosk 模型这边推荐用中文小模型vosk-model-small-cn-0.22官方大小 41.9MB运行时内存占用约 300MB适合边缘设备。如果你对准确率要求更高、机器内存也够可以换vosk-model-cn-0.221.3GB但注意大模型不支持运行时动态修改词汇表Grammar而小模型支持。对于语音控制这种指令集有限的场景小模型加 Grammar 限制准确率反而更稳。模型下载有两种方式。第一种是让 Vosk 自动下载代码里写Model(langzh-cn)首次运行会从官方源拉取并缓存到~/.cache/voskLinux/macOS或~/AppData/Local/voskWindows。第二种是手动下载访问 Vosk 模型页面把 zip 解压到~/.cache/vosk目录下。手动下载的好处是可以在内网机器上离线部署先把模型文件拷进去再跑代码。OpenClaw 侧的配置核心是三个东西Base URL、API Key、Model ID。Base URL 填https://taotoken.net/apiAPI Key 填你在控制台生成的那串Model ID 按你实际要用的模型填。这三件套在后面的 JSON 配置里会具体出现。如果你用的是 Claude Code 这类编码工具做插件开发也可以在 settings 里把这三件套配好让代码补全和调试更顺。3. 可复制配置Vosk 服务 OpenClaw 指令映射这一节直接给可复制的配置片段。先装依赖pip3 install vosk sounddevice fuzzywuzzy python-LevenshteinVosk 的模型路径可以通过环境变量指定避免硬编码export VOSK_MODEL_PATH/opt/models/vosk-model-small-cn-0.22然后是 OpenClaw 语音插件的配置文件voice_config.json这个文件放在插件同目录下OpenClaw 启动时会读取{ model_path: /opt/models/vosk-model-small-cn-0.22, language: zh-cn, sample_rate: 16000, blocksize: 8000, wake_word: 小助手, api_base: https://taotoken.net/api, api_key: sk-你的Key, model_id: 你的模型ID, commands: [ { patterns: [打开微信, 启动微信, 开微信], action: launch_app, params: {app: wechat}, confirm: false }, { patterns: [打开钉钉, 启动钉钉], action: launch_app, params: {app: dingtalk}, confirm: false }, { patterns: [截图, 截屏, 屏幕截图], action: system_command, params: {cmd: gnome-screenshot -i}, confirm: false }, { patterns: [退出, 停止, 关闭], action: stop_plugin, confirm: false } ] }注意api_base、api_key、model_id这三项就是前面说的三件套Base URL 用https://taotoken.net/api不要带 UTM。如果你的 OpenClaw 插件不需要大模型兜底这三项可以留空但建议保留后面做模糊指令纠错时会用到。Vosk 识别器的初始化代码关键是SetGrammar那一步把识别范围限制在指令词汇内import json from vosk import Model, KaldiRecognizer model Model(/opt/models/vosk-model-small-cn-0.22) recognizer KaldiRecognizer(model, 16000) grammar json.dumps([ 小助手 打开微信, 小助手 打开钉钉, 小助手 截图, 小助手 退出 ]) recognizer.SetGrammar(grammar)Grammar 只对小模型生效大模型不支持。设置之后识别器只会输出词汇表里的短语环境噪音和非目标词汇会被过滤掉这对语音控制场景非常关键。OpenClaw 侧的指令映射用模糊匹配把识别文本对齐到配置里的 patternsfrom fuzzywuzzy import fuzz def match_command(text, commands, threshold70): best None best_score 0 for cmd in commands: for pattern in cmd[patterns]: score fuzz.ratio(text, pattern) if score best_score and score threshold: best cmd best_score score return best阈值 70 是个经验值中文短指令下识别文本和 pattern 差一两个字fuzz.ratio 通常还能到 70 以上。如果误匹配多把阈值提到 80如果漏匹配多降到 60。4. 验证请求与成功结果从麦克风到指令执行配置写完后先单独验证 Vosk 能不能正常识别再验证 OpenClaw 能不能正确执行。第一步跑一个最小识别脚本import queue, json, sounddevice as sd from vosk import Model, KaldiRecognizer q queue.Queue() def callback(indata, frames, time, status): q.put(bytes(indata)) model Model(/opt/models/vosk-model-small-cn-0.22) rec KaldiRecognizer(model, 16000) with sd.RawInputStream(samplerate16000, blocksize8000, dtypeint16, channels1, callbackcallback): print(开始说话...) while True: data q.get() if rec.AcceptWaveform(data): result json.loads(rec.Result()) text result.get(text, ).strip() if text: print(识别结果:, text)运行后对着麦克风说“小助手 打开微信”终端应该输出识别结果: 小助手 打开微信。如果输出为空或者乱码先检查麦克风设备索引用sd.query_devices()列出所有输入设备然后在RawInputStream里加device索引。第二步把识别结果接到 OpenClaw 的指令执行函数。成功的结果是你说“小助手 截图”终端打印识别文本紧接着系统截图工具被拉起。这个过程在无网络环境下应该完全正常因为 Vosk 识别和 OpenClaw 执行都在本地。第三步验证延迟。在识别循环里加时间戳import time start time.time() if rec.AcceptWaveform(data): result json.loads(rec.Result()) text result.get(text, ).strip() if text: latency time.time() - start print(f识别结果: {text}, 延迟: {latency:.3f}s) start time.time()实测下来中文小模型在 4 核 CPU 上短指令延迟通常在 0.3 到 0.6 秒。如果超过 1 秒把 blocksize 从 8000 降到 4000延迟会明显下降但 CPU 占用会上升。第四步验证准确率。准备 20 条指令每条说 5 遍记录正确识别次数。Grammar 限制下目标指令的识别率通常能到 90% 以上。如果某条指令总是识别错把它加到 Grammar 里或者换一个发音更清晰的同义说法。5. 本篇常见错排查401、local proxy failed、reading choices接入过程中最容易撞上的几个报错我按实际遇到的频率排一下。第一个是401 Unauthorized。这个通常出现在 OpenClaw 插件调用大模型接口做语义兜底的时候。原因就一个API Key 不对或者没带。检查voice_config.json里的api_key字段确认它和你在 TaoToken 控制台创建的一致。另外确认请求头里带了Authorization: Bearer sk-xxx。如果 Key 是对的还报 401看看是不是把 Base URL 写成了带 UTM 的地址API 地址应该用https://taotoken.net/api不带任何查询参数。第二个是local proxy failed。这个报错一般不是 Vosk 本身的问题而是 OpenClaw 插件在请求外部接口时系统里配了不可用的网络代理。离线语音控制场景下Vosk 识别不需要网络但如果插件里混入了需要联网的调用而环境变量里又有HTTP_PROXY之类的设置就会报这个。解决办法是把插件里非必要的联网调用去掉或者确认网络环境本身是通的。注意这里说的是正常的网络配置问题不涉及任何特殊网络工具。第三个是reading choices相关的报错。这个通常出现在解析大模型返回的 JSON 时返回体里没有choices字段代码却直接去读response[choices][0]。原因可能是接口返回了错误信息比如模型 ID 填错、请求体格式不对。排查方法先把原始返回打印出来看error字段说了什么。如果是模型 ID 问题回到 TaoToken 控制台确认可用的模型列表把model_id改成正确的值。第四个是 Vosk 侧的Model not found。检查model_path指向的目录是否存在目录下应该有am、conf、graph等子目录。如果是自动下载模式确认~/.cache/vosk有写权限。第五个是麦克风没声音。用sd.query_devices()确认输入设备存在然后在RawInputStream里显式指定device参数。Linux 下还要确认当前用户在audio组里。如果你用的是 Claude Code 做插件开发OAuth 相关的报错也偶尔会出现。这类问题一般是本地凭证过期重新走一遍授权流程即可。CC Switch 或 Cline MCP 的配置里同样要保证 Base URL、Key、Model ID 三件套完整缺一个都会导致调用失败。6. 语义一致 CTA把离线语音控制跑通之后语音识别跑通、指令能执行之后下一步通常是让 OpenClaw 理解更自然的说法。比如用户说“帮我把微信打开”而不是标准的“打开微信”这时候纯 Grammar 匹配就不够了需要大模型做意图归一化。这部分能力可以走 TaoToken 的模型对话接口把识别文本发过去让它输出标准指令再交给 OpenClaw 执行。模型对话入口在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 里面有请求示例和参数说明。如果你打算长期做编码和 Agent 相关的开发比如给 OpenClaw 写更多语音插件、做多轮语音对话可以看看 Coding Plan入口在 https://taotoken.net/coding-plan 。它适合需要持续调用模型、做代码生成和调试的场景。控制台在 https://taotoken.net/console API Key 管理在 https://taotoken.net/api-keys 文档在 https://taotoken.net/doc 。Claude Code 相关的接入说明在 https://taotoken.net/claude-code-anthropic 如果你用 Claude Code 写插件可以对照着配。最后说一个实际经验离线语音控制最容易被忽略的是唤醒词和指令之间的停顿。Vosk 的流式识别对连续语音友好但如果你说完唤醒词马上接指令中间没有停顿识别器可能把两段拼在一起。解决办法是在唤醒词后面加一个短静音检测或者干脆把唤醒词和指令一起写进 Grammar让识别器一次性输出完整短语。这个细节调好之后整套离线语音控制的体验会顺很多。