内容
# 语音转文本服务
语音转文本和文本转语音合二为一的小型服务,通过 **MCP(可流式HTTP)接口** 和普通的 **REST接口** 公开。
- **语音转文本(STT)** — 使用[faster-whisper](https://github.com/SYSTRAN/faster-whisper)在CPU(int8)上运行。 将语音消息(ogg/opus,mp3,wav,m4a…)转录为文本。
- **文本转语音(TTS)** — 使用[gTTS](https://github.com/pndurette/gTTS)提供越南语语音,并可选地使用Google Cloud TTS作为备用。 服务器端使用ffmpeg将mp3转码为**Ogg/Opus**(`libopus`,48 kbit/s),以便在 **Telegram 语音气泡** 中原生渲染。 返回 **缓存 `.ogg` 的URL**,而非base64编码的音频块。 无需机器人具备ffmpeg能力 — 它已内置于镜像中。
这是原始单文件`whisper-mcp`(仅STT)的封装演进;STT行为和API保持100%向后兼容。
## 关于
voice-mcp 为AI代理(Claude Code、聊天机器人、自动化工具)提供单一的、自主托管的语音端点:发送音频,返回文本;发送文本,返回可分享的语音片段。它运行在 modest CPU VPS上,将音频保留在您自己的基础设施上(除了可选的云TTS备用方案外,不会将任何内容发送到第三方STT/TTS服务);并通过MCP和REST接口公开这两种功能,以便任何工具都可以消费它。 首先为越南语构建(Whisper + gTTS),但可用于Whisper/gTTS支持的任何语言。
## 接口
| 路由 | 认证 | 描述 |
| --- | --- | --- |
| `POST /mcp` (MCP可流式HTTP) | Bearer | 工具:`transcribe`,`speak` |
| `POST /transcribe` `{audio_base64, language}` | Bearer | → `{text, language}` |
| `POST /speak` `{text, lang}` | Bearer | → `{url, cached, engine, format}` |
| `GET /audio/<sha>.ogg` | 无 | 提供缓存的`.ogg`/`.mp3`(不透明的哈希文件名) |
| `GET /healthz` | 无 | 存活检测 / 模型信息 |
MCP端点路径为 `/mcp`,**无尾斜杠**。
`speak`(MCP工具和REST)返回 **`.ogg`(Ogg/Opus)音频片段的短URL字符串**,而非base64编码的音频 — 在工具结果中,base64编码的音频块会占用模型上下文窗口。 音频文件由 `GET /audio/<sha>.ogg` 提供,`Content-Type: audio/ogg`(同一路由也以`audio/mpeg`提供中间`.mp3`文件)。
## 运行
### 预构建镜像(GHCR)
```bash
docker run -d --name voice-mcp \
-p 127.0.0.1:8767:8767 \
-e MCP_TOKEN=your-secret \
-e WHISPER_MODEL=small \
-e PUBLIC_BASE_URL=https://whisper.veasy.vn \
-v voice-models:/models \
-v voice-audio:/audio \
--memory 1200m --cpus 2 \
ghcr.io/<owner>/voice-mcp:latest
```
### 本地构建
```bash
docker build -t voice-mcp .
# 或,不使用Docker:
pip install -e ".[dev]"
MCP_TOKEN=dev PUBLIC_BASE_URL=http://localhost:8767 voice-mcp
```
### 配置(环境变量)
参见 [`.env.example`](.env.example): `MCP_TOKEN`,`WHISPER_MODEL`,`AUDIO_DIR`,`PUBLIC_BASE_URL`,以及可选的 `GOOGLE_APPLICATION_CREDENTIALS`。
## 模型大小
CPU int8,大致占用/精度权衡:
| 模型 | RAM(近似) | 备注 |
| --- | --- | --- |
| `tiny` / `base` | ~0.3–0.5 GB | 最快,精度较低 |
| `small` | ~0.7–1 GB | **默认** — 良好的越南语精度/速度平衡 |
| `medium` | ~2–3 GB | 更高的精度,更重的模型 |
| `large-v3` | ~4–5 GB | 最佳精度,需要大量RAM |
在小型主机上限制容器(例如 `--memory 1200m --cpus 2`),以防止容器耗尽主机资源;`small`模型可以舒适地运行。
## TTS引擎原理
提供商链位于 [`src/voice_mcp/tts.py`](src/voice_mcp/tts.py):
1. **gTTS** (Google Translate TTS,`lang="vi"`)— 主要,免费,无需凭证,输出mp3。
2. **Google Cloud TTS** (语音`vi-VN-Wavenet-A`,女声)— 备用,仅在gTTS引发错误时使用(例如HTTP 429)。 需要 `GOOGLE_APPLICATION_CREDENTIALS`;如果未设置,则完全跳过。
故意 **不** 使用(不要重新添加):
- **Piper** — 语音质量被拒绝。
- **edge-tts** — Microsoft阻止数据中心IP地址(VPS上的403错误)。
然后使用ffmpeg将mp3转码为 **Ogg/Opus** (`ffmpeg -y -loglevel error -i in.mp3 -c:a libopus -b:a 48k out.ogg`),并将其内置到镜像中。 `.mp3`(用于链)和返回的`.ogg`都通过 `sha256(text + lang + engine)` 在 `AUDIO_DIR` 中进行内容寻址缓存,因此重复请求会立即返回`.ogg`,而无需重新运行gTTS或ffmpeg。
## 提供音频
应用程序自行提供 `GET /audio/<file>` (流式传输`.ogg`/`.mp3`并匹配Content-Type,无认证 — 文件名是不透明的哈希),因此不需要额外的Web服务器配置。 为了提高效率,您可以直接让nginx提供 `AUDIO_DIR`:
```nginx
location /audio/ {
alias /path/to/audio/;
add_header Cache-Control "public, max-age=31536000, immutable";
}
```
## 语音到语音助手模式
将两个工具链接起来,实现语音输入 → 语音输出循环:
```python
text = transcribe(audio_base64=clip_b64, language="vi") # STT
reply = my_llm(text) # 您的逻辑
url = speak(text=reply, lang="vi") # TTS -> Ogg/Opus URL
# 下载 `url`(一个.ogg)并将其作为Telegram语音消息发送回去
```
## 开发
```bash
pip install -e ".[dev]"
ruff check .
pytest # 纯逻辑测试(缓存密钥,配置解析)— 无网络/模型
```
## 贡献
欢迎贡献。 请参阅 [CONTRIBUTING.md](./CONTRIBUTING.md),了解开发设置、`ruff` + `pytest`检查以及设计原则(两个API / 绝不以内联方式返回大音频 / 引擎作为备用链)。
## 许可证
[MIT](./LICENSE) © Edward Nguyen