Unmute是能赋予文本大语言模型(LLM)实时语音交互能力,通过Kyutai的语音转文本(STT)和文本转语音(TTS)模型将LLM包裹起来,实现用户通过语音输入,LLM生成文本响应,然后将文本响应朗读出来的全流程。Unmute强调低延迟,STT和TTS服务均经过优化,兼容任何文本LLM,用户可选择自托管LLM(如基于VLLM)或使用外部API。Unmute的架构由前端、后端、独立的STT、LLM和TTS服务以及Traefik路由组成,通过WebSocket实现浏览器与后端之间的实时音频和数据传输。项目支持多种部署方式,其中Docker Compose因其能简化多服务同时运行的复杂性而被推荐,支持多GPU部署以进一步优化语音处理延迟。Unmute提供了高度的可配置性,允许用户自定义角色语音和系统提示,通过基于OpenAI实时API的协议实现了前端的可替换性,为未来集成工具调用等高级功能奠定了基础。
Unmute工作流程如下:
用户浏览器 → Traefik → 后端/前端;后端分别连接语音转文本(STT)、LLM、文本转语音(TTS)
具体步骤:
1、用户打开由前端提供服务的Unmute网站。
2、点击“连接”后,用户与后端建立websocket连接,实时来回传输音频和其他元数据。
3、后端通过websocket连接到语音转文本服务器,将用户的音频发送过去,并实时接收转录文本。
4、当语音转文本检测到用户停止说话,需要生成响应时,后端会连接到LLM服务器获取响应。我们使用VLLM托管自己的LLM,不过你也可以使用OpenAI或Mistral等外部API。
5、在生成响应的过程中,后端会将响应传输到文本转语音服务器进行语音合成,并将生成的语音转发给用户。
6、Traefik会将/api
路径下的请求路由到后端,其余请求路由到前端。
名称 | GPU数量 | 机器数量 | 难度 | 有文档 | Kyutai支持 |
---|---|---|---|---|---|
Docker compose | 1+ | 1 | 非常简单 | ✅ | ✅ |
无Docker | 1-3 | 1-5 | 简单 | ✅ | ✅ |
Docker swarm | 1-约100 | 1-约100 | 中等 | ✅ | ❌ |
由于Unmute是一个复杂系统,需要同时运行多个服务,因此建议使用Docker Compose来运行Unmute。它能让你用一个命令启动或停止所有服务。因为这些服务都是Docker容器,所以你能获得可复现的环境,无需担心依赖问题。
你可以使用任何LLM。默认情况下,Unmute使用Mistral Small 3.2 24B作为LLM(Gemma 3 12B也是个不错的选择)。这个模型可以免费使用,但需要你接受相关条款:
1、创建一个Hugging Face账号。
2、在Mistral Small 3.2 24B模型页面接受条款。
3、创建一个访问令牌。你可以使用细粒度令牌,只需授予“对所有你能访问的公共 gated 仓库内容的读取权限”即可。公开部署时不要使用具有写入权限的令牌。万一服务器被攻破,攻击者可能会获得你在Hugging Face上所有模型、数据集等的写入权限。
4、将令牌添加到~/.bashrc
或类似文件中,格式为export HUGGING_FACE_HUB_TOKEN=hf_...your token here...
确保你已经安装了Docker Compose。你还需要NVIDIA Container Toolkit,让Docker能访问你的GPU。要确认NVIDIA Container Toolkit安装正确,可以运行:
sudo docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi
如果你使用meta-llama/Llama-3.2-1B(docker-compose.yml中的默认模型),16GB的GPU内存就足够了。如果遇到内存问题,打开docker-compose.yml,查找带有NOTE:
的注释,那里有你可能需要调整的地方。
在有GPU的机器上运行:
# 确保环境变量中包含令牌:
echo $HUGGING_FACE_HUB_TOKEN # 这里应该会输出hf_...之类的内容
docker compose up --build
在Unmute.sh中,我们将语音转文本、文本转语音和VLLM服务器分别运行在不同的GPU上,与单GPU设置相比,这样能降低延迟。在单个L40S GPU上运行所有组件时,TTS延迟约为750ms,而在Unmute.sh上,延迟能降至约450ms。
如果有至少3个GPU可用,可以在stt
、tts
和llm
服务中添加以下代码片段,确保它们在不同的GPU上运行:
stt: # tts和llm也类似
# ...其他配置
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
以下是一些关于如何对Unmute进行特定修改的大致提示。
角色的声音和提示定义在voices.yaml
中。这个配置文件的格式很直观。某些系统提示包含动态生成的元素。例如,“问答节目”的5个问题是从固定列表中随机预先选择的。这样的系统提示定义在unmute/llm/system_prompt.py
中。
需要注意的是,该文件只在后端启动时加载,然后会被缓存,所以如果修改了voices.yaml
中的内容,需要重启后端。
后端和前端通过基于OpenAI实时API(“ORA”)的协议通过websocket通信。在可能的情况下,我们尽量匹配ORA格式,但也需要添加一些额外的消息,还有一些消息的参数经过了简化。我们会明确指出与ORA格式的不同之处,详见unmute/openai_realtime_api_events.py
。
关于WebSocket通信协议、消息类型和音频处理流程的详细信息,可参考浏览器-后端通信文档。
理想情况下,编写一个能同时与Unmute后端或OpenAI实时API通信的前端应该不难,但目前我们还未完全兼容。欢迎贡献代码!
前端是一个定义在frontend/
中的Next.js应用。如果你想对比不同的前端实现,unmute/loadtest/loadtest_client.py
中有一个Python客户端,这是我们用来测试Unmute延迟和吞吐量的脚本。
这是一个常见需求,我们欢迎为Unmute添加工具调用功能的贡献!
可以研究vLLM如何实现工具调用,并修改docker-compose.yml中的vLLM调用,使用适当的参数。
在Unmute方面,修改unmute/unmute_handler.py
中的_generate_response_task()
。目前,llm.chat_completion()
会逐个生成单词。这需要修改为还能生成工具调用并进行处理。
如果你想了解我们如何部署和扩展unmute.sh,可以查看关于Swarm部署的文档。
首先安装pre-commit
本身——你可能希望用pip install pre-commit
在全局安装,而不是在虚拟环境或uv
中,因为需要pre-commit
可执行文件始终可用。然后运行:
pre-commit install --hook-type pre-commit
我们建议使用uv来管理Python依赖。以下命令假设你正在使用uv。
uv run fastapi dev unmute/main_websocket.py
uv run fastapi run unmute/main_websocket.py
loadtest_client.py
是一个连接到Unmute并模拟对话的脚本,用于测量延迟和吞吐量。
uv run unmute/loadtest/loadtest_client.py --server-url ws://localhost:8000 --n-workers 16