163 lines
5.5 KiB
Markdown
163 lines
5.5 KiB
Markdown
# 3D 数字人语音聊天系统
|
|
|
|
这个项目提供一个浏览器侧渲染的 3D 数字人语音交互原型:
|
|
|
|
- 浏览器通过 WebSocket 上传麦克风 PCM 音频,并接收服务端回传的 AI 语音片段。
|
|
- 浏览器通过 WebSocket 订阅字幕和口型/头部控制数据。
|
|
- 服务端执行 VAD -> ASR -> DeepSeek LLM -> TTS,并把音频特征转换为驱动 3D 数字人的控制帧。
|
|
|
|
当前形态更接近单机演示和研发验证环境,不是已经拆分完毕的生产化架构。
|
|
|
|
当前运行约束:
|
|
|
|
- 服务端按“单客户端接管”模式工作。新的浏览器页面连上后,会主动断开旧页面的音频、字幕、动画 WebSocket。
|
|
- 浏览器刷新后,语音通道可能仍受浏览器媒体安全策略限制,需要一次页面点击或点“连接语音通道”来恢复麦克风与播放上下文。
|
|
- 前端目前默认保留随机眨眼;更激进的 idle body motion 已回退,避免部分 VRM 模型进入 T pose。
|
|
|
|
## 当前架构
|
|
|
|
语音链路:
|
|
|
|
```text
|
|
Browser Mic
|
|
-> /ws/audio
|
|
-> FastAPI / websocket audio
|
|
-> VAD
|
|
-> ASR
|
|
-> LLM
|
|
-> TTS
|
|
-> /ws/audio
|
|
-> browser audio playback
|
|
```
|
|
|
|
控制链路:
|
|
|
|
```text
|
|
LLM reply text / TTS audio
|
|
-> AvatarService
|
|
-> /ws/animation
|
|
-> browser animation buffer
|
|
-> VRM or GLTF morph targets / head bones
|
|
```
|
|
|
|
字幕链路:
|
|
|
|
```text
|
|
ASR text / LLM segmented reply
|
|
-> /ws/subtitles
|
|
-> browser message panel
|
|
```
|
|
|
|
## 关键目录
|
|
|
|
- [main.py](main.py): FastAPI 入口,聚合音频 WebSocket、字幕/动画 WebSocket、健康状态和文本对话接口。
|
|
- [core/pipeline.py](core/pipeline.py): 语音/文本对话流水线,负责 ASR、LLM、TTS 编排。
|
|
- [core/state_machine.py](core/state_machine.py): 简单会话状态机,处理用户说话、思考、数字人说话之间的切换。
|
|
- [services/asr.py](services/asr.py): ASR 封装,优先使用 FunASR,失败时进入降级模式。
|
|
- [services/llm.py](services/llm.py): 在线 LLM 封装,默认兼容 DeepSeek OpenAI-style API。
|
|
- [services/tts.py](services/tts.py): TTS 封装,优先使用 Kokoro,失败时返回静音兜底。
|
|
- [services/vad.py](services/vad.py): VAD 封装,基于 silero-vad。
|
|
- [services/avatar.py](services/avatar.py): 把音频转换成 blendshape 和头部控制帧。
|
|
- [web/app.js](web/app.js): 浏览器端 3D 舞台、音频 WebSocket、字幕和动画消费逻辑。
|
|
- [scripts](scripts): 启动、证书生成、巡检和服务管理脚本。
|
|
|
|
## 快速启动
|
|
|
|
安装依赖:
|
|
|
|
```bash
|
|
pip install -r requirements.txt
|
|
```
|
|
|
|
准备环境变量,至少包括:
|
|
|
|
```bash
|
|
export LLM_API_KEY="..."
|
|
export LLM_BASE_URL="https://api.deepseek.com/v1"
|
|
export LLM_MODEL="deepseek-chat"
|
|
```
|
|
|
|
也可以直接复制 [.env.example](.env.example) 作为本地配置模板。
|
|
|
|
语音如果偏快、偏硬,可以优先调这几个参数:
|
|
|
|
- `LLM_SYSTEM_PROMPT`:先把回答风格约束成口语化、非书面化,并明确禁止客服套话,再去调 TTS,收益更大。
|
|
- `LLM_TEMPERATURE`:默认 `0.55`,能减少奇怪人设和跑偏表达。
|
|
- `TTS_SPEED`:默认 `0.90`,比 `1.0` 更像真人正常说话速度。
|
|
- `TTS_SENTENCE_MAX_LEN`:默认 `64`,避免回复被切得太碎。
|
|
- `TTS_SEGMENT_PAUSE_MS`:默认 `90`,给分段之间留一点停顿,但不要太拖。
|
|
- `TTS_FADE_MS`:默认 `12`,减轻段间拼接的突兀感。
|
|
|
|
本地启动:
|
|
|
|
```bash
|
|
python main.py
|
|
```
|
|
|
|
或使用脚本:
|
|
|
|
```bash
|
|
bash scripts/start-test.sh auto
|
|
```
|
|
|
|
如果你明确要固定走当前 shell/虚拟环境里的 Python,也可以用:
|
|
|
|
```bash
|
|
bash scripts/start-public.sh
|
|
```
|
|
|
|
基础自检:
|
|
|
|
```bash
|
|
python scripts/model_probe.py
|
|
python scripts/qa_check.py --base-url http://127.0.0.1:8080 --rounds 3
|
|
python scripts/smoke_test.py --text "你好,请做一个简短自我介绍。"
|
|
```
|
|
|
|
启动后访问:
|
|
|
|
- `http://127.0.0.1:8080/`
|
|
- `http://127.0.0.1:8080/health`
|
|
|
|
如果 `.env` 里已经配置了 `WEBRTC_PORT=8018` 与 `SSL_CERTFILE` / `SSL_KEYFILE`,实际访问地址通常会变成:
|
|
|
|
- `https://<server-ip>:8018/`
|
|
- `https://<server-ip>:8018/health`
|
|
|
|
如果浏览器与服务端跨机器访问,麦克风采集通常需要 HTTPS。可以先生成自签证书:
|
|
|
|
```bash
|
|
bash scripts/gen-self-signed-cert.sh
|
|
```
|
|
|
|
再在环境变量或 `.env` 中配置 `SSL_CERTFILE` 和 `SSL_KEYFILE`。
|
|
|
|
监听规则:
|
|
|
|
- 服务监听地址固定按 `0.0.0.0` 规则执行。
|
|
- 即使把 `WEBRTC_HOST` 写成 `127.0.0.1`、`localhost` 或 `::1`,启动时也会自动归一化成 `0.0.0.0`,避免局域网访问被误伤。
|
|
|
|
## 核心接口
|
|
|
|
- `GET /`: 前端页面。
|
|
- `POST /chat/text`: 文本输入链路,跳过 ASR/VAD。
|
|
- `POST /chat/reset`: 清空上下文和运行时状态。
|
|
- `GET /health`: 返回后端运行指标和组件健康状态。
|
|
- `GET /meta`: 返回 STUN、HTTPS、端口和动画协议配置。
|
|
- `GET /events`: Server-Sent Events 运行状态流。
|
|
- `WS /ws/audio`: 双向语音通道,上传麦克风 PCM 并接收回复语音。
|
|
- `WS /ws/subtitles`: 字幕流。
|
|
- `WS /ws/animation`: 数字人控制流。
|
|
- `GET /avatar/schema`: 当前控制协议 schema。
|
|
|
|
说明:三个 WebSocket 通道当前都只保留一个活动客户端。新页面接入后,旧页面会收到 `1012 replaced` 并停止接收数据。
|
|
|
|
## 后续开发建议
|
|
|
|
优先阅读 [docs/development-guide.md](docs/development-guide.md)。这个文档说明了:
|
|
|
|
- 语音与文本两条链路的完整时序。
|
|
- 前后端之间的协议约定。
|
|
- 当前实现的边界和适合继续扩展的位置。
|
|
- 已知的工程化约束。
|