语音功能开发指南

May 14, 2026 · View on GitHub

HotPlex 内建完整的语音交互管线:语音输入(STT) -> AI 处理 -> 语音回复(TTS),让用户通过 Slack/飞书发送语音消息即可与 AI Agent 对话。

整体管线

用户语音消息
    |
    v
平台适配器(Slack/飞书)
    |
    v
STT 引擎 -> 文本
    |
    v
Session -> Worker(Claude Code / OpenCode Server)
    |
    v
AI 回复(长文本)
    |
    v
LLM 摘要(口语化改写,控制在 max_chars 以内)
    |
    v
TTS 引擎 -> 音频(MP3 / WAV)
    |
    v
ffmpeg 转码 -> Opus / MP3
    |
    v
平台音频消息回传

STT(语音转文字)

Provider 类型

Provider引擎依赖说明
feishu飞书语音识别 APIffmpeg云端转写,音频经 ffmpeg 转 PCM 后上传
local本地子进程(stt_server.py)python3 + funasr-onnx + onnxruntime常驻进程,JSON-over-stdio 协议,模型常驻内存
feishu+local飞书优先 + local 兜底上述全部先调飞书 API,失败时回退本地

配置

# 全局默认(三级继承:全局 -> 平台 -> Bot)
HOTPLEX_MESSAGING_STT_PROVIDER=local              # feishu | local | feishu+local
HOTPLEX_MESSAGING_STT_LOCAL_CMD="python3 ~/.hotplex/scripts/stt_server.py"
HOTPLEX_MESSAGING_STT_LOCAL_IDLE_TTL=1h           # 空闲自动关闭

本地 STT 工作原理

local provider 使用 PersistentSTT 管理常驻 Python 子进程:

  1. 首次请求时懒启动子进程(PGID 隔离)
  2. 音频写入临时文件,通过 stdin 发送 JSON 请求:{"audio_path": "/tmp/.../stt_123.opus"}
  3. 从 stdout 读取 JSON 响应:{"text": "转录结果", "error": ""}
  4. 空闲超过 IDLE_TTL 自动关闭,下次请求自动重启
  5. 崩溃自动检测 + 重启,Gateway 关闭时分层终止(close stdin -> SIGTERM -> 5s -> SIGKILL)

子进程部署链路:scripts/*.py -> go:embed -> assets.InstallScripts() -> ~/.hotplex/scripts/

TTS(文字转语音)

Provider 类型

Provider引擎输出格式依赖
edgeMicrosoft Edge TTSMP3无(免费 WebSocket API)
mossMOSS-TTS-NanoWAVpython3 + ONNX 模型 + ffmpeg
edge+mossEdge 优先 + MOSS 兜底MP3 / WAV上述全部

配置

# 全局默认
HOTPLEX_MESSAGING_TTS_ENABLED=true
HOTPLEX_MESSAGING_TTS_PROVIDER=edge+moss
HOTPLEX_MESSAGING_TTS_VOICE=zh-CN-XiaoxiaoNeural    # Edge 音色
HOTPLEX_MESSAGING_TTS_MAX_CHARS=150                   # 摘要最大字符数

# MOSS 专用(仅 moss / edge+moss 时需要)
HOTPLEX_MESSAGING_TTS_MOSS_MODEL_DIR=~/.hotplex/models/moss-tts-nano
HOTPLEX_MESSAGING_TTS_MOSS_VOICE=Xiaoyu
HOTPLEX_MESSAGING_TTS_MOSS_PORT=18083
HOTPLEX_MESSAGING_TTS_MOSS_IDLE_TIMEOUT=30m
HOTPLEX_MESSAGING_TTS_MOSS_CPU_THREADS=2

Edge TTS 工作原理

原生 Go 实现(无第三方依赖),通过 WebSocket 连接 Microsoft Edge TTS 服务:

  1. 生成 Sec-MS-GEC 认证 token(SHA-256 + Windows epoch)
  2. 建立 WebSocket 连接,发送 SSML 语音合成请求
  3. 流式接收二进制音频帧(24kHz mono MP3)
  4. Edge TTS 默认音色 zh-CN-XiaoxiaoNeural,支持所有 Microsoft Neural 音色

MOSS-TTS-Nano 工作原理

本地 CPU 推理引擎,以 FastAPI sidecar 进程运行:

  1. 首次请求时懒启动(python3 app_onnx.py --host 127.0.0.1 --port 18083
  2. 等待 /api/warmup-status 健康检查通过(最长 60s)
  3. 通过 HTTP POST /api/generate 调用合成(返回 base64 编码 WAV)
  4. 空闲超时自动关闭,崩溃自动重启
  5. 支持引用计数共享(多平台复用同一 sidecar)

音频转码

所有 TTS 输出经 ffmpeg 转码为平台所需格式:

  • 飞书:MP3/WAV -> Opus(24kHz mono,ToOpus
  • Slack:WAV -> MP3(24kHz mono,ToMP3),已是 MP3 则跳过

摘要生成

AI 回复通常很长,TTS 前需通过 LLM 改写为口语播报稿:

  1. 输入截断至 SummaryInputCap(2000 字符)
  2. 调用 Brain LLM,使用专用 TTS system prompt(语音播报编辑人设)
  3. 输出经 SanitizeForSpeech 清洗(移除 Markdown、代码块、文件路径、大数字中文化)
  4. 控制在 max_chars 字符以内(默认 150)

各 Provider 安装指南

Edge TTS(推荐,零配置)

无需安装任何依赖。Gateway 内建原生 WebSocket 客户端,开箱即用。

# 最简配置 — 只用 Edge TTS
HOTPLEX_MESSAGING_TTS_ENABLED=true
HOTPLEX_MESSAGING_TTS_PROVIDER=edge

MOSS-TTS-Nano(离线 / 低延迟)

# 1. 安装 Python 依赖
pip3 install numpy sentencepiece onnxruntime fastapi uvicorn python-multipart soundfile huggingface_hub

# 2. 下载模型和脚本到指定目录
mkdir -p ~/.hotplex/models/moss-tts-nano
# 将 MOSS-TTS-Nano 的 app_onnx.py 和模型文件放入该目录

# 3. 配置
HOTPLEX_MESSAGING_TTS_PROVIDER=moss
HOTPLEX_MESSAGING_TTS_MOSS_MODEL_DIR=~/.hotplex/models/moss-tts-nano

本地 STT

本地 STT 使用阿里达摩院开源的 SenseVoice-Small 模型,通过 funasr-onnx 运行 ONNX 推理,支持中英日韩粤五语种。

系统要求

项目要求
Python3.9 或更高版本
磁盘空间~3 GB(模型文件)
内存最低 512 MB 可用(INT8 量化推理约 ~400 MB)
操作系统Linux / macOS
python3 --version   # 确认 Python >= 3.9

Python 依赖安装

pip3 install funasr-onnx modelscope onnx
用途
funasr-onnxSenseVoice ONNX 推理引擎
modelscope模型自动下载(ModelScope Hub)
onnxONNX 模型修补工具

注意funasr-onnx 仅依赖 onnxruntime(CPU 版本)。如果环境中误安装了 torchnvidia-*cuda-* 等 GPU 包,它们不会被使用但会占用大量内存和磁盘。可通过 pip uninstall torch nvidia-cublas nvidia-cufft nvidia-cudnn-cu13 等移除。

验证安装:

python3 -c "from funasr_onnx import SenseVoiceSmall; print('OK')"
# 输出: OK

模型下载

首次运行时,模型会自动从 ModelScope Hub 下载(~3 GB),缓存在 ~/.cache/modelscope/hub/models/iic/SenseVoiceSmall/。手动预下载(可选):

python3 -c "
from modelscope.hub.snapshot_download import snapshot_download
path = snapshot_download('iic/SenseVoiceSmall')
print(f'Model cached at: {path}')
"

ONNX 模型修补

ModelScope 预导出的 ONNX 模型存在已知 bug:Less 算子接收了 float 和 int64 混合输入,ONNX Runtime 1.19+ 会拒绝加载。使用 persistent 模式时修补会自动执行,也可手动运行:

python3 scripts/fix_onnx_model.py

输出示例:

Patched model.onnx
model_quant.onnx: already correct

修补脚本会在原文件旁创建 .bak 备份,不会丢失原始模型。

配置

Ephemeral 模式(低频使用,每次启动新进程):

stt_provider: "local"
stt_local_cmd: "python3 scripts/stt_once.py {file}"

Persistent 模式(高频使用,常驻进程零冷启动,推荐):

stt_provider: "local"
stt_local_cmd: "python3 scripts/stt_server.py --model iic/SenseVoiceSmall"
stt_local_idle_ttl: 15m   # 空闲 15 分钟后自动关闭子进程

测试验证

步骤 1 — 检查 Python 依赖

python3 -c "from funasr_onnx import SenseVoiceSmall; print('funasr-onnx: OK')"
python3 -c "from modelscope.hub.snapshot_download import snapshot_download; print('modelscope: OK')"
python3 -c "import onnx; print('onnx: OK')"

步骤 2 — 检查模型文件

ls -lh ~/.cache/modelscope/hub/models/iic/SenseVoiceSmall/model.onnx
# 应显示约 900MB 的模型文件

步骤 3 — 手动转写测试

echo '{"audio_path": "/path/to/test.opus"}' | python3 scripts/stt_server.py --model iic/SenseVoiceSmall
# 预期输出: {"text": "转写结果", "error": ""}

步骤 4 — 启动 Gateway 验证

make dev

飞书:发送语音消息,日志应出现 feishu stt: transcribed text="你好世界"。 Slack:发送语音消息,日志应出现 persistent stt: transcribed text="hello world"

Docker 部署

Dockerfile 添加 STT 依赖:

FROM python:3.12-slim

# Install STT dependencies
RUN pip install --no-cache-dir funasr-onnx modelscope onnx

# Copy STT scripts
COPY scripts/stt_server.py /opt/hotplex/scripts/
COPY scripts/fix_onnx_model.py /opt/hotplex/scripts/
COPY scripts/stt_once.py /opt/hotplex/scripts/

Volume 挂载模型缓存(避免每次重建都下载 3GB):

# docker-compose.yaml
services:
  gateway:
    volumes:
      - stt-models:/root/.cache/modelscope
      - ./scripts:/opt/hotplex/scripts

volumes:
  stt-models:

飞书配置(Docker 环境):

feishu:
  stt_provider: "feishu+local"
  stt_local_cmd: "python3 /opt/hotplex/scripts/stt_server.py --model iic/SenseVoiceSmall"
  stt_local_idle_ttl: 30m

Slack 配置(Docker 环境):

slack:
  stt_provider: "local"
  stt_local_cmd: "python3 /opt/hotplex/scripts/stt_server.py --model iic/SenseVoiceSmall"
  stt_local_idle_ttl: 30m

故障排查

问题日志关键词解决方案
funasr-onnx 未安装funasr-onnx not installedpip3 install funasr-onnx
ONNX 模型加载失败type mismatchpython3 scripts/fix_onnx_model.py,或删除缓存重新下载
云端 STT 返回空transcribed text=""检查飞书 speech_to_text 权限是否开通
Slack 语音未被识别user shared a file确认 slack.stt_provider"local"stt_local_cmd 非空
Persistent 子进程频繁重启persistent stt 多条 start 日志检查内存是否不足(OOM Kill)、模型文件是否损坏
Ephemeral 模式延迟 3-5s正常现象,切换到 persistent 模式
模型下载慢/失败ModelScope 服务器在国内,海外可能较慢;可手动下载后放入 ~/.cache/modelscope/hub/models/iic/SenseVoiceSmall/

飞书云端 STT

仅限飞书平台,无需额外安装。Gateway 使用飞书 speech_to_text API,自动将音频转为 PCM 后上传。需配置飞书 App ID 和 App Secret。

依赖检查

使用 hotplex doctor 自动检测语音功能所需依赖:

hotplex doctor

检查项包括:

检查器检测内容
stt.runtimepython3、funasr-onnx、onnxruntime、onnx、stt_server.py 部署状态、ffmpeg
tts.runtimeffmpeg、python3(MOSS)、MOSS Python 包、模型目录和入口脚本

检测逻辑根据实际配置动态判断:仅配置了 edge 的 TTS 不检查 Python,仅配置了 feishu 的 STT 不检查本地 STT 包。

相关源码

模块路径
STT 核心internal/messaging/stt/stt.go
飞书 STTinternal/messaging/feishu/stt.go
TTS 核心internal/messaging/tts/tts.go
Edge TTSinternal/messaging/tts/edge.go
MOSS TTSinternal/messaging/tts/moss.go + moss_process.go
音频转码internal/messaging/tts/audio.go
摘要生成internal/messaging/tts/prompt.go
环境检查internal/cli/checkers/stt.go + tts.go
配置模板configs/env.example

延伸阅读