前言:作为 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("
成功")
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