一个基于 MLX 的本地实时语音助手示例。
当前版本提供:
- 浏览器端麦克风采集与流式上传
- Silero VAD 语音起止检测
- MLX ASR 实时转写
- MLX VLM 流式回复
- MLX TTS 流式语音合成
- 用户插话时的后端打断与前端立即停播
- 自定义参考音色
custom_voice.wav
- Python 3.11 及以上
- 建议使用 Apple Silicon Mac,因为项目依赖 MLX 生态,主要面向本地 macOS / Apple 芯片环境
- 一个支持麦克风权限和
AudioWorklet的现代浏览器
推荐使用 uv:
uv sync如果你还没有安装 uv,也可以先安装后再执行上面的命令。
uv run main.py如果你想写得更显式一些,也可以使用:
uv run python main.py服务默认监听:
http://127.0.0.1:8000
首次启动会自动加载以下模型:
- TTS:
mlx-community/Qwen3-TTS-12Hz-0.6B-Base-8bit - ASR:
mlx-community/Qwen3-ASR-0.6B-4bit - VLM:
mlx-community/gemma-4-e2b-it-4bit
- 启动服务。
- 打开浏览器访问
http://127.0.0.1:8000。 - 允许麦克风权限。
- 点击页面下方按钮开始说话。
- 说完后,系统会自动转写、生成回复并播报。
- 如果模型正在说话,你继续开口,当前回复会被打断并切换到新的输入。
.
├── main.py # FastAPI 服务、WebSocket、VAD / ASR / VLM / TTS 主流程
├── custom_voice.wav # 当前使用的参考音色
├── web/
│ ├── index.html # 浏览器端界面与播放/录音逻辑
│ └── pcm-recorder-worklet.js # 录音重采样与 PCM 输出
├── pyproject.toml # Python 项目依赖
└── uv.lock # uv 锁文件
整体链路如下:
- 浏览器录制麦克风音频并重采样到 16k PCM。
- 音频通过 WebSocket 发送到后端。
- 后端用 Silero VAD 判断用户是否开始/结束说话。
- 说话结束后,调用 ASR 做最终识别。
- 识别结果进入对话历史,再送入 VLM 生成文本回复。
- 回复文本按句切分后进入 TTS,边生成边回传到前端播放。
- 如果播放期间再次检测到用户说话,后端取消当前回复任务,前端立即停播。
默认参考音频文件是仓库根目录下的 custom_voice.wav。
如果你想替换音色:
- 直接替换
custom_voice.wav - 或修改
main.py中的REF_AUDIO_PATH和REF_TEXT
参考文本 REF_TEXT 最好和参考音频内容一致,否则 TTS 音色稳定性会下降。
可以直接在 main.py 里修改这些常量:
TTS_MODEL_PATHASR_MODEL_PATHVLM_MODEL_PATHREF_AUDIO_PATHREF_TEXTMAX_CHAT_MESSAGES
- 当前是单进程 FastAPI 示例,重点在本地实时交互,不是生产部署模板。
- 浏览器端依赖
AudioWorklet。如果你更新了前端代码但浏览器还在跑旧版本,先强制刷新页面。 - VAD 打断已经接入,但实际效果仍然会受麦克风、扬声器回声消除和环境噪声影响。
- 仓库目前没有自动化测试,主要依赖本地手工联调。
- 确认浏览器已经允许自动播放或用户手势已触发音频上下文
- 确认模型已加载完成
- 确认系统输出设备正常
- 检查当前站点是否被浏览器阻止了麦克风访问
- 确认是通过
http://127.0.0.1:8000或http://localhost:8000打开的
浏览器可能缓存了 pcm-recorder-worklet.js。请强制刷新页面后再试。
首次运行可能需要下载和初始化模型,属于正常现象。
这个项目现在更适合拿来做:
- 本地语音助手原型
- MLX 语音交互链路实验
- WebSocket 全双工语音交互样例
- 带插话打断能力的本地语音 UI 验证