前言:作为 OpenClaw 项目的开发者,我一直想做一个完全开源、免费的语音对话技能。经历了从 Faster-Whisper + Porcupine 到 Vosk + Edge TTS 的演进,终于完成了 voice-chat-1.0.3。本文将完整记录整个开发过程,包括技术选型、踩过的坑、以及最终的解决方案。 为什么做这个项目 在使用 OpenClaw 的过程中,我发现现有的语音交互方案都有缺陷: - 需要 API Key:大多数方案(如 Porcupine、DeepSpeech)都需要付费或注册 - 网络依赖:在线识别方案需要稳定的网络连接 - 隐私担忧:语音数据上传到第三方服务器 - 成本高:长期使用的 API 调用费用不菲 所以我决定做一个完全开源、离线、免费的语音对话技能。 技术选型演进 方案 1.0.1:Faster-Whisper + Porcupine 优势: - Faster-Whisper 识别准确率高 - Porcupine 唤醒词检测灵敏 问题: - ❌ Porcupine 需要注册获取 API Key(虽然是免费的) - ❌ Faster-Whisper 模型较大(~100MB) - ❌ 唤醒词训练需要额外配置 结果:已废弃 方案 1.0.2:优化 Faster-Whisper 改进: - ✅ 使用 tiny 模型(~100MB) - ✅ 优化内存管理 - ✅ 添加文件监听机制 问题: - ❌ 仍然需要 Porcupine API Key - ❌ 首次运行需要下载大文件 结果:已废弃 方案 1.0.3:Vosk + Edge TTS(最终方案) 优势: - ✅ 完全开源 - Vosk 开源识别,Edge TTS 免费合成 - ✅ 无需 API Key - 零门槛使用 - ✅ 离线支持 - Vosk 可以完全离线运行 - ✅ 模型轻量 - 中文小模型仅 ~50MB - ✅ 自动下载 - 首次运行自动下载模型 结果:✅ 当前版本(推荐) 最终架构设计 工作流程: 用户说话 ↓ Vosk 实时监听(检测唤醒词"小鹏") ↓ 录音(静音 1 秒自动停止) ↓ Vosk 完整识别 → 转换为文本 ↓ 移除唤醒词 → "小鹏,你好" → "你好" ↓ 写入请求文件 → shared/oc_request.txt ↓ OpenClaw Cron(60 秒轮询)→ 检测到新请求 ↓ Chat Agent 处理 → 生成回复 ↓ 写入响应文件 → shared/oc_response.txt ↓ 读取响应(watchdog 监听) ↓ Edge TTS 合成 → 文本转语音 ↓ 播放音频 技术栈: - 语音识别:Vosk(开源、离线、免费) - 语音合成:Edge TTS(微软 Edge 浏览器引擎) - 音频 IO:SoundDevice(麦克风/扬声器) - 音频处理:NumPy + SciPy(数值计算、重采样) - 文件监听:Watchdog(非阻塞轮询) - 通信方式:文件共享(shared/oc_request.txt) - AI 处理:OpenClaw Cron(定时任务触发) 核心功能实现 1. Vosk 自动下载 ```python def initialize_vosk(wake_word: str = "小鹏"): """初始化 Vosk 语音识别(自动下载中文模型)""" model_path = SKILL_DIR / "vosk-model-zh-cn-small" # 自动下载模型(首次运行) if not model_path.exists(): print("[DL] 正在下载 Vosk 中文模型 (~50MB)...") url = "https://github.com/alphacep/vosk-models/releases/download/vosk-model-small-zh-cn-0.22/vosk-model-small-zh-cn-0.22.zip" # 下载、解压、重命名、清理 # 加载模型 vosk_model = vosk.Model(str(model_path)) recognizer = vosk.KaldiRecognizer(vosk_model, 16000) return recognizer, wake_word ``` 2. Edge TTS 重试机制 ```python async def edge_tts_communicate(text: str, output_file: str, retry_times: int = 3): """Edge TTS 语音合成(带重试)""" # 文本预处理 text = re.sub(r'[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]', '', text) # 清理特殊字符 if len(text) > 500: text = text[:497] + "..." # Edge TTS 单次最多 500 字符 for attempt in range(retry_times): try: print(f"[TTS] TTS 合成尝试 {attempt + 1}/{retry_times}...") communicate = edge_tts.Communicate(text, "zh-CN-XiaoxiaoNeural") await communicate.save(output_file) # 使用 save() 更稳定 # 验证文件 if Path(output_file).exists() and Path(output_file).stat().st_size > 0: return True except Exception as e: print(f"[FAIL] TTS 尝试 {attempt + 1} 失败:{e}") if attempt < retry_times - 1: time.sleep(1) continue else: raise return False ``` 3. 唤醒词过滤(关键!) 这是最关键的功能之一,避免大模型误解: ```python if text and target_word in text: print(f"[FOUND] 检测到关键词 '{target_word}': {text}") # 移除唤醒词 cleaned_text = text.replace(target_word, "").strip() # 移除标点符号 cleaned_text = re.sub(r'^[,,:,.。.]+', '', cleaned_text).strip() if cleaned_text: print(f"[INFO] 清理后文本:{cleaned_text}") # 发送清理后的文本 write_request(cleaned_text) ``` 效果: - 用户说:"小鹏,今天天气怎么样" - Vosk 识别:"小鹏,今天天气怎么样" - 清理后:"今天天气怎么样" ✓ 4. 文件监听机制 ```python class ResponseHandler(FileSystemEventHandler): def on_modified(self, event): if event.src_path == str(RESPONSE_FILE) and not self.completed: with open(RESPONSE_FILE, "r", encoding="utf-8") as f: response = json.load(f) if response.get("id") == self.request_id: self.completed = True text = response.get("response") self.callback(text) # 使用 watchdog observer = Observer() observer.schedule(ResponseHandler(request_id, callback), str(SHARED_DIR), recursive=False) observer.start() ``` 遇到的坑与解决方案 坑 1:Edge TTS 返回空音频 问题:流式写入时,网络不稳定可能返回空音频 解决方案:改用 save() 方法 ```python # 新代码(稳定) await communicate.save(output_file) # 内置错误处理 ``` 坑 2:Windows GBK 不支持 Emoji 问题:UnicodeEncodeError: 'gbk' codec can't encode '\U0001f504' 解决方案:移除所有 emoji,使用 ASCII 标记 ```python print("[TTS] TTS 合成中...") print("[OK] 成功") print("[FAIL] 失败") ``` 坑 3:唤醒词被发送给大模型 问题:用户说"小鹏,你好",大模型误解为在提你的名字 解决方案:发送前过滤唤醒词 ```python cleaned_text = text.replace(target_word, "").strip() cleaned_text = re.sub(r'^[,,:,.。.]+', '', cleaned_text).strip() write_request(cleaned_text) ``` 坑 4:Cron 任务不触发 问题:长时间等待后超时 解决方案: 1. 检查 Cron 任务状态:openclaw cron list 2. 确认 OpenClaw Gateway 运行:openclaw status 3. 手动触发测试:openclaw cron run --id <task-id> 性能测试 端到端延迟: - Vosk 实时检测:250ms - 录音(静音检测):1-5 秒 - Vosk 完整识别:0.3-1 秒 - Cron 触发等待:0-60 秒 - Chat 处理:2-10 秒 - TTS 合成:1-3 秒 - 总计:约 4.5-79 秒 优化建议: 1. 缩短 Cron 间隔:当前 60 秒,建议 5-10 秒 2. 更换更大模型:vosk-model-zh-cn-large(~300MB,准确率更高) 3. 本地 TTS 备份:pyttsx3(离线) 总结与展望 项目成果: ✅ 完全开源 - 无需任何 API Key ✅ 离线支持 - Vosk 可完全离线运行 ✅ 自动下载 - 首次运行自动下载模型 ✅ 智能过滤 - 唤醒词自动移除 ✅ 错误重试 - TTS 失败自动重试 ✅ 详细文档 - 完整的快速开始和故障排查 项目地址: - GitHub: https://github.com/openclaw/voice-chat - 文档:https://docs.openclaw.ai - Discord: https://discord.com/invite/clawd 未来计划: - [ ] 支持更多语言(英语、日语等) - [ ] 添加情感识别 - [ ] 优化 Cron 间隔(降低延迟) - [ ] 本地 TTS 备份(pyttsx3) - [ ] 多唤醒词支持 致谢 感谢以下开源项目: - Vosk: https://github.com/alphacep/vosk-api - Edge TTS: https://github.com/rany2/edge-tts - OpenClaw: https://github.com/openclaw/openclaw --- 如果你喜欢这个项目,请给个 Star!🌟 作者:崔小鹏 🐼 最后更新:2026-04-12