2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-03-27 10:49:34 +08:00
2026-03-29 23:01:39 +08:00
2026-04-08 14:38:04 +08:00
2026-03-27 10:49:34 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-03-27 17:10:41 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00
2026-04-08 14:38:04 +08:00

3D 数字人语音聊天系统

这个项目提供一个浏览器侧渲染的 3D 数字人语音交互原型:

  • 浏览器通过 WebSocket 上传麦克风 PCM 音频,并接收服务端回传的 AI 语音片段。
  • 浏览器通过 WebSocket 订阅字幕和口型/头部控制数据。
  • 服务端执行 VAD -> ASR -> DeepSeek LLM -> TTS,并把音频特征转换为驱动 3D 数字人的控制帧。

当前形态更接近单机演示和研发验证环境,不是已经拆分完毕的生产化架构。

当前运行约束:

  • 服务端按“单客户端接管”模式工作。新的浏览器页面连上后,会主动断开旧页面的音频、字幕、动画 WebSocket。
  • 浏览器刷新后,语音通道可能仍受浏览器媒体安全策略限制,需要一次页面点击或点“连接语音通道”来恢复麦克风与播放上下文。
  • 前端目前默认保留随机眨眼;更激进的 idle body motion 已回退,避免部分 VRM 模型进入 T pose。

当前架构

语音链路:

Browser Mic
  -> /ws/audio
  -> FastAPI / websocket audio
  -> VAD
  -> ASR
  -> LLM
  -> TTS
  -> /ws/audio
  -> browser audio playback

控制链路:

LLM reply text / TTS audio
  -> AvatarService
  -> /ws/animation
  -> browser animation buffer
  -> VRM or GLTF morph targets / head bones

字幕链路:

ASR text / LLM segmented reply
  -> /ws/subtitles
  -> browser message panel

关键目录

  • main.py: FastAPI 入口,聚合音频 WebSocket、字幕/动画 WebSocket、健康状态和文本对话接口。
  • core/pipeline.py: 语音/文本对话流水线,负责 ASR、LLM、TTS 编排。
  • core/state_machine.py: 简单会话状态机,处理用户说话、思考、数字人说话之间的切换。
  • services/asr.py: ASR 封装,优先使用 FunASR,失败时进入降级模式。
  • services/llm.py: 在线 LLM 封装,默认兼容 DeepSeek OpenAI-style API。
  • services/tts.py: TTS 封装,优先使用 Kokoro,失败时返回静音兜底。
  • services/vad.py: VAD 封装,基于 silero-vad。
  • services/avatar.py: 把音频转换成 blendshape 和头部控制帧。
  • web/app.js: 浏览器端 3D 舞台、音频 WebSocket、字幕和动画消费逻辑。
  • scripts: 启动、证书生成、巡检和服务管理脚本。

快速启动

安装依赖:

pip install -r requirements.txt

准备环境变量,至少包括:

export LLM_API_KEY="..."
export LLM_BASE_URL="https://api.deepseek.com/v1"
export LLM_MODEL="deepseek-chat"

复制 .env.example.env,填入有效的 LLM_API_KEY

本地权重(除 LLM 外全部在仓库内 models/:首次可用联网环境执行 python scripts/vendor_hf_models.py;其中 VAD 的 silero_vad.jit 会从已安装的 silero-vad 包复制到 models/vad/。ASR 使用 models/asr/SenseVoiceSmall/TTS 使用 models/tts/Kokoro-82M/

完整部署步骤(新机器、模型打包、HTTPS、防火墙、排错)见 docs/deployment.md

语音如果偏快、偏硬,可以优先调这几个参数:

  • 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,减轻段间拼接的突兀感。

部署到任意机器时:在项目根目录运行(保证 models/web/certs/ 等相对路径有效)。证书可在 .env 里写相对路径(例如 certs/dev-cert.pem),会按项目根目录解析。也可用环境变量 VISUAL_CHAT_PYTHON 指定解释器。

本地启动:

python main.py

或使用脚本:

bash scripts/start-test.sh auto

如果你明确要固定走当前 shell/虚拟环境里的 Python,也可以用:

bash scripts/start-public.sh

基础自检:

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 里已经配置了 HTTP_PORT=8018SSL_CERTFILE / SSL_KEYFILE,实际访问地址通常会变成:

  • https://<server-ip>:8018/
  • https://<server-ip>:8018/health

如果浏览器与服务端跨机器访问,麦克风采集通常需要 HTTPS。可以先生成自签证书:

bash scripts/gen-self-signed-cert.sh

再在环境变量或 .env 中配置 SSL_CERTFILESSL_KEYFILE

监听规则:

  • 服务监听地址固定按 0.0.0.0 规则执行。
  • 即使把 HTTP_HOST 写成 127.0.0.1localhost::1,启动时也会自动归一化成 0.0.0.0,避免局域网访问被误伤。

核心接口

  • GET /: 前端页面。
  • POST /chat/text: 文本输入链路,跳过 ASR/VAD。
  • POST /chat/reset: 清空上下文和运行时状态。
  • GET /health: 返回后端运行指标和组件健康状态。
  • GET /meta: 返回 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。这个文档说明了:

  • 语音与文本两条链路的完整时序。
  • 前后端之间的协议约定。
  • 当前实现的边界和适合继续扩展的位置。
  • 已知的工程化约束。
S
Description
No description provided
Readme
9.6 MiB
Languages
Python 73.2%
JavaScript 13.5%
Shell 5.5%
CSS 4.2%
HTML 3.6%