音控制:用 Vosk 實(shí)現(xiàn)離線語(yǔ)音指令的完整配置)
1. 為什么要在 OpenClaw 里塞一套離線語(yǔ)音控制OpenClaw 語(yǔ)音控制這件事我最早是在一臺(tái)不聯(lián)網(wǎng)的工控機(jī)上折騰的。那臺(tái)機(jī)器跑著 OpenClaw 做本地自動(dòng)化鍵盤(pán)鼠標(biāo)都在但操作員戴著手套敲鍵盤(pán)不方便于是想加一套離線語(yǔ)音指令。云端語(yǔ)音識(shí)別方案第一時(shí)間就被排除了現(xiàn)場(chǎng)沒(méi)有外網(wǎng)數(shù)據(jù)也不允許出內(nèi)網(wǎng)。這時(shí)候 Vosk 就成了很自然的選擇——它是一個(gè)基于 Kaldi 的離線開(kāi)源語(yǔ)音識(shí)別工具包模型下載到本地后識(shí)別過(guò)程完全在本機(jī)完成不依賴(lài)任何網(wǎng)絡(luò)請(qǐng)求。Vosk 能做什么簡(jiǎn)單說(shuō)它把麥克風(fēng)采集到的音頻流實(shí)時(shí)轉(zhuǎn)成文本。它支持中文、英文等二十多種語(yǔ)言小模型只有 40MB 左右在樹(shù)莓派、嵌入式 Linux 上都能跑。適合誰(shuí)適合需要在無(wú)網(wǎng)絡(luò)、低延遲、數(shù)據(jù)不出本地的場(chǎng)景里做語(yǔ)音控制的開(kāi)發(fā)者比如智能家居中控、工業(yè)設(shè)備語(yǔ)音操作、離線語(yǔ)音助手。OpenClaw 本身是一個(gè)模塊化的智能助手框架功能以插件形式存在支持 Linux、macOS、Windows配置靈活敏感操作在本地完成。把 Vosk 作為語(yǔ)音輸入層OpenClaw 作為指令執(zhí)行層兩者拼起來(lái)就是一個(gè)完整的離線語(yǔ)音控制閉環(huán)。我試過(guò)在 16kHz 單聲道、blocksize 8000 的配置下中文小模型從說(shuō)完一句話到出識(shí)別結(jié)果端到端延遲大概在 300 到 600 毫秒之間具體取決于句子長(zhǎng)度和 CPU。這個(gè)延遲對(duì)于“打開(kāi)微信”“截圖”這類(lèi)短指令是完全夠用的。下面我會(huì)從模型選型、本地服務(wù)啟動(dòng)、OpenClaw 側(cè)指令映射一路寫(xiě)到延遲和準(zhǔn)確率的驗(yàn)證動(dòng)作你可以直接照著做。2. TaoToken 前置準(zhǔn)備模型與 API 通道怎么配在正式接 Vosk 之前先把兩件事理清楚一是 Vosk 模型從哪來(lái)、放哪二是 OpenClaw 如果需要調(diào)用大模型做語(yǔ)義兜底走哪條 API 通道。Vosk 模型本身是離線文件下載一次就行但 OpenClaw 的很多插件能力比如把模糊語(yǔ)音轉(zhuǎn)成結(jié)構(gòu)化指令、做意圖理解會(huì)依賴(lài)大模型接口。這時(shí)候可以用 TaoToken 提供的統(tǒng)一 API 通道把模型調(diào)用集中管理省得每個(gè)插件各配一套 Key。TaoToken 的官網(wǎng)入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。注意 API 地址后面不加 UTM 參數(shù)直接用它作為 Base URL 就行。你需要先在控制臺(tái)創(chuàng)建一個(gè) API Key然后把它寫(xiě)進(jìn) OpenClaw 的配置里。模型對(duì)話的入口在 https://taotoken.net/api-keys 接入文檔在 https://taotoken.net/doc 這兩個(gè)頁(yè)面建議先過(guò)一遍尤其是文檔里的請(qǐng)求格式和錯(cuò)誤碼說(shuō)明。Vosk 模型這邊推薦用中文小模型vosk-model-small-cn-0.22官方大小 41.9MB運(yùn)行時(shí)內(nèi)存占用約 300MB適合邊緣設(shè)備。如果你對(duì)準(zhǔn)確率要求更高、機(jī)器內(nèi)存也夠可以換vosk-model-cn-0.221.3GB但注意大模型不支持運(yùn)行時(shí)動(dòng)態(tài)修改詞匯表Grammar而小模型支持。對(duì)于語(yǔ)音控制這種指令集有限的場(chǎng)景小模型加 Grammar 限制準(zhǔn)確率反而更穩(wěn)。模型下載有兩種方式。第一種是讓 Vosk 自動(dòng)下載代碼里寫(xiě)Model(langzh-cn)首次運(yùn)行會(huì)從官方源拉取并緩存到~/.cache/voskLinux/macOS或~/AppData/Local/voskWindows。第二種是手動(dòng)下載訪問(wèn) Vosk 模型頁(yè)面把 zip 解壓到~/.cache/vosk目錄下。手動(dòng)下載的好處是可以在內(nèi)網(wǎng)機(jī)器上離線部署先把模型文件拷進(jìn)去再跑代碼。OpenClaw 側(cè)的配置核心是三個(gè)東西Base URL、API Key、Model ID。Base URL 填https://taotoken.net/apiAPI Key 填你在控制臺(tái)生成的那串Model ID 按你實(shí)際要用的模型填。這三件套在后面的 JSON 配置里會(huì)具體出現(xiàn)。如果你用的是 Claude Code 這類(lèi)編碼工具做插件開(kāi)發(fā)也可以在 settings 里把這三件套配好讓代碼補(bǔ)全和調(diào)試更順。3. 可復(fù)制配置Vosk 服務(wù) OpenClaw 指令映射這一節(jié)直接給可復(fù)制的配置片段。先裝依賴(lài)pip3 install vosk sounddevice fuzzywuzzy python-LevenshteinVosk 的模型路徑可以通過(guò)環(huán)境變量指定避免硬編碼export VOSK_MODEL_PATH/opt/models/vosk-model-small-cn-0.22然后是 OpenClaw 語(yǔ)音插件的配置文件voice_config.json這個(gè)文件放在插件同目錄下OpenClaw 啟動(dòng)時(shí)會(huì)讀取{ 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: [打開(kāi)微信, 啟動(dòng)微信, 開(kāi)微信], action: launch_app, params: {app: wechat}, confirm: false }, { patterns: [打開(kāi)釘釘, 啟動(dòng)釘釘], action: launch_app, params: {app: dingtalk}, confirm: false }, { patterns: [截圖, 截屏, 屏幕截圖], action: system_command, params: {cmd: gnome-screenshot -i}, confirm: false }, { patterns: [退出, 停止, 關(guān)閉], action: stop_plugin, confirm: false } ] }注意api_base、api_key、model_id這三項(xiàng)就是前面說(shuō)的三件套Base URL 用https://taotoken.net/api不要帶 UTM。如果你的 OpenClaw 插件不需要大模型兜底這三項(xiàng)可以留空但建議保留后面做模糊指令糾錯(cuò)時(shí)會(huì)用到。Vosk 識(shí)別器的初始化代碼關(guān)鍵是SetGrammar那一步把識(shí)別范圍限制在指令詞匯內(nèi)import json from vosk import Model, KaldiRecognizer model Model(/opt/models/vosk-model-small-cn-0.22) recognizer KaldiRecognizer(model, 16000) grammar json.dumps([ 小助手 打開(kāi)微信, 小助手 打開(kāi)釘釘, 小助手 截圖, 小助手 退出 ]) recognizer.SetGrammar(grammar)Grammar 只對(duì)小模型生效大模型不支持。設(shè)置之后識(shí)別器只會(huì)輸出詞匯表里的短語(yǔ)環(huán)境噪音和非目標(biāo)詞匯會(huì)被過(guò)濾掉這對(duì)語(yǔ)音控制場(chǎng)景非常關(guān)鍵。OpenClaw 側(cè)的指令映射用模糊匹配把識(shí)別文本對(duì)齊到配置里的 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 是個(gè)經(jīng)驗(yàn)值中文短指令下識(shí)別文本和 pattern 差一兩個(gè)字fuzz.ratio 通常還能到 70 以上。如果誤匹配多把閾值提到 80如果漏匹配多降到 60。4. 驗(yàn)證請(qǐng)求與成功結(jié)果從麥克風(fēng)到指令執(zhí)行配置寫(xiě)完后先單獨(dú)驗(yàn)證 Vosk 能不能正常識(shí)別再驗(yàn)證 OpenClaw 能不能正確執(zhí)行。第一步跑一個(gè)最小識(shí)別腳本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(開(kāi)始說(shuō)話...) while True: data q.get() if rec.AcceptWaveform(data): result json.loads(rec.Result()) text result.get(text, ).strip() if text: print(識(shí)別結(jié)果:, text)運(yùn)行后對(duì)著麥克風(fēng)說(shuō)“小助手 打開(kāi)微信”終端應(yīng)該輸出識(shí)別結(jié)果: 小助手 打開(kāi)微信。如果輸出為空或者亂碼先檢查麥克風(fēng)設(shè)備索引用sd.query_devices()列出所有輸入設(shè)備然后在RawInputStream里加device索引。第二步把識(shí)別結(jié)果接到 OpenClaw 的指令執(zhí)行函數(shù)。成功的結(jié)果是你說(shuō)“小助手 截圖”終端打印識(shí)別文本緊接著系統(tǒng)截圖工具被拉起。這個(gè)過(guò)程在無(wú)網(wǎng)絡(luò)環(huán)境下應(yīng)該完全正常因?yàn)?Vosk 識(shí)別和 OpenClaw 執(zhí)行都在本地。第三步驗(yàn)證延遲。在識(shí)別循環(huán)里加時(shí)間戳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識(shí)別結(jié)果: {text}, 延遲: {latency:.3f}s) start time.time()實(shí)測(cè)下來(lái)中文小模型在 4 核 CPU 上短指令延遲通常在 0.3 到 0.6 秒。如果超過(guò) 1 秒把 blocksize 從 8000 降到 4000延遲會(huì)明顯下降但 CPU 占用會(huì)上升。第四步驗(yàn)證準(zhǔn)確率。準(zhǔn)備 20 條指令每條說(shuō) 5 遍記錄正確識(shí)別次數(shù)。Grammar 限制下目標(biāo)指令的識(shí)別率通常能到 90% 以上。如果某條指令總是識(shí)別錯(cuò)把它加到 Grammar 里或者換一個(gè)發(fā)音更清晰的同義說(shuō)法。5. 本篇常見(jiàn)錯(cuò)排查401、local proxy failed、reading choices接入過(guò)程中最容易撞上的幾個(gè)報(bào)錯(cuò)我按實(shí)際遇到的頻率排一下。第一個(gè)是401 Unauthorized。這個(gè)通常出現(xiàn)在 OpenClaw 插件調(diào)用大模型接口做語(yǔ)義兜底的時(shí)候。原因就一個(gè)API Key 不對(duì)或者沒(méi)帶。檢查voice_config.json里的api_key字段確認(rèn)它和你在 TaoToken 控制臺(tái)創(chuàng)建的一致。另外確認(rèn)請(qǐng)求頭里帶了Authorization: Bearer sk-xxx。如果 Key 是對(duì)的還報(bào) 401看看是不是把 Base URL 寫(xiě)成了帶 UTM 的地址API 地址應(yīng)該用https://taotoken.net/api不帶任何查詢參數(shù)。第二個(gè)是local proxy failed。這個(gè)報(bào)錯(cuò)一般不是 Vosk 本身的問(wèn)題而是 OpenClaw 插件在請(qǐng)求外部接口時(shí)系統(tǒng)里配了不可用的網(wǎng)絡(luò)代理。離線語(yǔ)音控制場(chǎng)景下Vosk 識(shí)別不需要網(wǎng)絡(luò)但如果插件里混入了需要聯(lián)網(wǎng)的調(diào)用而環(huán)境變量里又有HTTP_PROXY之類(lèi)的設(shè)置就會(huì)報(bào)這個(gè)。解決辦法是把插件里非必要的聯(lián)網(wǎng)調(diào)用去掉或者確認(rèn)網(wǎng)絡(luò)環(huán)境本身是通的。注意這里說(shuō)的是正常的網(wǎng)絡(luò)配置問(wèn)題不涉及任何特殊網(wǎng)絡(luò)工具。第三個(gè)是reading choices相關(guān)的報(bào)錯(cuò)。這個(gè)通常出現(xiàn)在解析大模型返回的 JSON 時(shí)返回體里沒(méi)有choices字段代碼卻直接去讀response[choices][0]。原因可能是接口返回了錯(cuò)誤信息比如模型 ID 填錯(cuò)、請(qǐng)求體格式不對(duì)。排查方法先把原始返回打印出來(lái)看error字段說(shuō)了什么。如果是模型 ID 問(wèn)題回到 TaoToken 控制臺(tái)確認(rèn)可用的模型列表把model_id改成正確的值。第四個(gè)是 Vosk 側(cè)的Model not found。檢查model_path指向的目錄是否存在目錄下應(yīng)該有am、conf、graph等子目錄。如果是自動(dòng)下載模式確認(rèn)~/.cache/vosk有寫(xiě)權(quán)限。第五個(gè)是麥克風(fēng)沒(méi)聲音。用sd.query_devices()確認(rèn)輸入設(shè)備存在然后在RawInputStream里顯式指定device參數(shù)。Linux 下還要確認(rèn)當(dāng)前用戶在audio組里。如果你用的是 Claude Code 做插件開(kāi)發(fā)OAuth 相關(guān)的報(bào)錯(cuò)也偶爾會(huì)出現(xiàn)。這類(lèi)問(wèn)題一般是本地憑證過(guò)期重新走一遍授權(quán)流程即可。CC Switch 或 Cline MCP 的配置里同樣要保證 Base URL、Key、Model ID 三件套完整缺一個(gè)都會(huì)導(dǎo)致調(diào)用失敗。6. 語(yǔ)義一致 CTA把離線語(yǔ)音控制跑通之后語(yǔ)音識(shí)別跑通、指令能執(zhí)行之后下一步通常是讓 OpenClaw 理解更自然的說(shuō)法。比如用戶說(shuō)“幫我把微信打開(kāi)”而不是標(biāo)準(zhǔn)的“打開(kāi)微信”這時(shí)候純 Grammar 匹配就不夠了需要大模型做意圖歸一化。這部分能力可以走 TaoToken 的模型對(duì)話接口把識(shí)別文本發(fā)過(guò)去讓它輸出標(biāo)準(zhǔn)指令再交給 OpenClaw 執(zhí)行。模型對(duì)話入口在 https://taotoken.net/api-keys 接入文檔在 https://taotoken.net/doc 里面有請(qǐng)求示例和參數(shù)說(shuō)明。如果你打算長(zhǎng)期做編碼和 Agent 相關(guān)的開(kāi)發(fā)比如給 OpenClaw 寫(xiě)更多語(yǔ)音插件、做多輪語(yǔ)音對(duì)話可以看看 Coding Plan入口在 https://taotoken.net/coding-plan 。它適合需要持續(xù)調(diào)用模型、做代碼生成和調(diào)試的場(chǎng)景??刂婆_(tái)在 https://taotoken.net/console API Key 管理在 https://taotoken.net/api-keys 文檔在 https://taotoken.net/doc 。Claude Code 相關(guān)的接入說(shuō)明在 https://taotoken.net/claude-code-anthropic 如果你用 Claude Code 寫(xiě)插件可以對(duì)照著配。最后說(shuō)一個(gè)實(shí)際經(jīng)驗(yàn)離線語(yǔ)音控制最容易被忽略的是喚醒詞和指令之間的停頓。Vosk 的流式識(shí)別對(duì)連續(xù)語(yǔ)音友好但如果你說(shuō)完喚醒詞馬上接指令中間沒(méi)有停頓識(shí)別器可能把兩段拼在一起。解決辦法是在喚醒詞后面加一個(gè)短靜音檢測(cè)或者干脆把喚醒詞和指令一起寫(xiě)進(jìn) Grammar讓識(shí)別器一次性輸出完整短語(yǔ)。這個(gè)細(xì)節(jié)調(diào)好之后整套離線語(yǔ)音控制的體驗(yàn)會(huì)順很多。