# 智能可视化语音聊天系统 — 详细任务清单(本机实时版) > **目标**:在本机(RTX 5090 / WSL2)实现"浏览器访问 → WebRTC 传输 → 单工语音对话 → 数字人口型同步"的**实时**可用系统。 > **基础**:`environments.md` 与 `research-notes.md` 中已验证的环境、模型和踩坑修复全部复用。 > **最后更新**:2026-03-25 — 经过逐组件可行性硬核验证后修订(第三版)。 --- ## 0. 逐组件可行性验证报告(执行前必读) ### 0.1 组件全景审计表 | 组件 | 选型 | 许可证 | 纯本地 | RTX 5090 验证 | 实时性证据 | 风险等级 | |------|------|--------|--------|---------------|-----------|---------| | **VAD** | Silero VAD 6.x | MIT | 是(CPU) | N/A(纯CPU) | 流式 VADIterator,32ms 帧级延迟 | 无 | | **ASR** | SenseVoice-Small (FunASR) | Apache 2.0 | 是(GPU) | 已有用户确认 cu128 可跑 | 中文 CER 4.2%,CPU 14x实时,GPU 更快 | 低 | | **LLM** | 在线 API (OpenAI 兼容) | N/A | 否(设计如此) | N/A | 流式,首 token ~0.5-1.5s | 中(网络) | | **TTS** | Kokoro-82M-v1.1-zh | Apache 2.0 | 是(GPU) | 确认支持 CUDA 12.8 RTX 50 系 | 210x实时(4090),<2GB VRAM | 低 | | **TTS 升级** | Qwen3-TTS 0.6B | Apache 2.0 | 是(GPU) | 确认可跑,需 torch.compile 优化 | 97ms 首音频,RTF<0.25(优化后) | 中 | | **口型** | MuseTalk v1.5 | MIT | 是(GPU) | 已在你机器验证通过 | 30fps+(V100),RTX 5090 更快 | 中(需改流式) | | **头动底片** | LivePortrait | MIT | 是(离线预处理) | 已在你机器验证通过 | 离线,无实时要求 | 无 | | **WebRTC** | aiortc | BSD-3-Clause | 是 | N/A(纯Python) | asyncio 原生,H.264 已优化 | 低 | **结论:全部组件均为开源许可(MIT / Apache 2.0 / BSD-3),全部可本地运行,全部有 RTX 5090 兼容路径。** ### 0.2 两个致命问题及修正 #### 问题一:faster-whisper 中文识别率灾难 | | faster-whisper large-v3-turbo | SenseVoice-Small | |---|---|---| | 中文 WER/CER | **WER 77%**(几乎不可用) | **CER 4.2%**(优秀) | | 中文方言 | 不支持 | 7 大方言 + 26 种口音 | | 推理速度(GPU) | ~50x 实时 | RTF 0.0076(130x 实时) | | 模型大小 | ~1.5GB | ~234MB | | 许可证 | MIT | Apache 2.0 | **决定:ASR 从 faster-whisper 改为 SenseVoice-Small。** faster-whisper 的 turbo 模型砍掉了 28/32 层 decoder,导致中文(需要更复杂解码的语言)准确率暴跌,完全不可接受。 #### 问题二:CosyVoice2 实际延迟远超宣传 | | 宣传值 | 开源版实测值(社区) | |---|---|---| | 流式首块延迟 | 150ms | **1 - 4.5 秒** | | 原因 | 阿里云预训练音色,无特征提取 | 开源 zero-shot 需提取 token/embedding | | 流式实现 | 优化的 chunk 流 | 累积式 chunk(1A, 2A, 3A...),无 KV cache | **决定:TTS 从 CosyVoice2 改为 Kokoro-82M(快速启动) + Qwen3-TTS 0.6B(质量升级路径)。** ### 0.3 修正后的端到端延迟预算 | 阶段 | 选型 | 预估延迟 | 数据来源 | |------|------|----------|---------| | VAD 尾部检测 | Silero VAD | ~300ms | 默认 silence_duration_ms=300 | | ASR 转写(3s) | SenseVoice-Small GPU | ~50-150ms | RTF 0.0076,3s × 0.0076 ≈ 23ms + 开销 | | LLM 首 token | 在线流式 | ~500-1500ms | 取决于模型/网络 | | TTS 首音频块 | Kokoro-82M | ~20-50ms | 210x 实时 on 4090 | | MuseTalk 首帧批 | MuseTalk batch=4 | ~100-200ms | 30fps+ → 4帧 ≈ 130ms | | WebRTC 编码传输 | aiortc H.264 | ~50-100ms | 软编码 | | **合计** | | **~1.0 - 2.3s** | | > 比上版预算更优,因为 SenseVoice 比 faster-whisper 更快,Kokoro 比 CosyVoice2 快一个数量级。 ### 0.4 GPU 显存预算(RTX 5090 = 32GB) | 模型 | 精度 | VRAM | 常驻 | |------|------|------|------| | MuseTalk (UNet + VAE + Whisper) | fp16 | ~3-4GB | 是 | | SenseVoice-Small | fp16 | ~0.8GB | 是 | | Kokoro-82M | fp16 | ~1.5GB | 是 | | PyTorch CUDA 上下文 | — | ~1GB | 固定 | | **合计** | | **~6-7GB** | | > 32GB VRAM 用不到 1/4,若后续升级 Qwen3-TTS 0.6B(+3GB)仍有充裕余量。 ### 0.5 "开源可控"确认清单 | 组件 | 许可证 | 可商用 | 代码仓库 | 离线可用 | |------|--------|--------|----------|---------| | Silero VAD | MIT | 是 | github.com/snakers4/silero-vad | 是 | | SenseVoice-Small | Apache 2.0 | 是 | github.com/FunAudioLLM/SenseVoice | 是 | | Kokoro-82M | Apache 2.0 | 是 | github.com/hexgrad/kokoro | 是 | | Qwen3-TTS | Apache 2.0 | 是 | github.com/QwenLM/Qwen3-TTS | 是 | | MuseTalk v1.5 | MIT | 是 | github.com/TMElyralab/MuseTalk | 是 | | LivePortrait | MIT | 是 | github.com/KlingTeam/LivePortrait | 是 | | aiortc | BSD-3 | 是 | github.com/aiortc/aiortc | 是 | **零云端依赖(LLM 除外,且 LLM 有本地兜底),零非开源组件。** ### 0.6 "稳健落地"保障策略 | 风险 | 发生时 | 保底方案 | 切换成本 | |------|--------|----------|---------| | SenseVoice 在 nightly PyTorch 编译失败 | M0 | 用 MuseTalk 自带 whisper-tiny 做 ASR(精度低但能跑) | 改一行 import | | Kokoro 中文质量不达标 | M1 | 切 Qwen3-TTS 0.6B(已验证 RTX 5090 可跑) | 换一个 TTS 服务封装 | | Qwen3-TTS RTX 5090 推理慢 | M4 | torch.compile + CUDA graphs 优化(社区已有方案,RTF<0.25) | 加 2 行代码 | | MuseTalk 流式改造困难 | M1 | 先用"分段文件处理":TTS 输出完整 WAV → MuseTalk 批量合成 → 整段播放 | 延迟增加 2-3s 但可用 | | 在线 LLM 不可用 | 运行时 | 本地预设话术兜底,8s 超时自动触发 | 已内建 | | aiortc 软编码瓶颈 | M2 | 改用 ffmpeg + NVENC 硬编码管道 | 约 1 天工作量 | --- ## 1. 总体架构 ### 1.1 系统拓扑 ``` 浏览器 (Chrome) │ │ WebRTC (ICE/DTLS/SRTP) │ ▼ ┌─────────────────────────────────────────────────────┐ │ webrtc-gateway (aiortc + FastAPI) │ │ ┌──────────┐ ┌──────────────────────┐ │ │ │ 上行音频 │ │ 下行视频 + 音频轨 │ │ │ └────┬─────┘ └──────────▲───────────┘ │ └───────┼───────────────────────────────┼─────────────┘ │ │ ▼ │ ┌──────────────┐ ┌──────┴──────┐ │ Silero VAD │ │ mixer │ │ (CPU,MIT) │ │ (音视频合流) │ └──────┬───────┘ └──▲───────▲──┘ │ speech.end │ │ ▼ │ │ ┌──────────────┐ ┌──────┴──┐ ┌──┴──────────┐ │ SenseVoice │ │MuseTalk │ │ TTS 音频 │ │ ASR (GPU) │ │ 口型渲染│ │ (Kokoro) │ │ Apache 2.0 │ │ (GPU) │ │ Apache 2.0 │ └──────┬───────┘ │ MIT │ └──────▲──────┘ │ text └────▲────┘ │ ▼ │ wav │ text ┌──────────────┐ ┌─────┴──────┐ │ │ LLM Client │──text──▶│ Kokoro TTS ├───────┘ │ (在线 API) │ │ (GPU) │ └──────────────┘ └────────────┘ ``` ### 1.2 单工状态机 ``` speech.start speech.end ┌────┐ ──────────▶ ┌──────────────┐ ────────▶ ┌──────────┐ │IDLE│ │USER_SPEAKING │ │THINKING │ └──▲─┘ ◀────────── └──────────────┘ └────┬─────┘ │ barge-in │ tts.ready │ (用户抢话) ▼ │ ┌─────────────────┐ ┌────────────────┐ └───────────────│ │◀────────│AVATAR_SPEAKING │ playback.done │ (回到 IDLE) │ └────────────────┘ └─────────────────┘ ``` **状态规则**: - `IDLE`:数字人播放闭嘴循环视频,下行静音 - `USER_SPEAKING`:数字人立即切闭嘴循环(如正在说话则中断),录音开始 - `THINKING`:录音结束,ASR → LLM → TTS 管线启动,数字人继续闭嘴循环 - `AVATAR_SPEAKING`:MuseTalk 输出帧 + TTS 音频同步下发 - 任何状态下检测到 `speech.start` → 立即跳转 `USER_SPEAKING`(barge-in) ### 1.3 进程内通信(不用消息中间件) 单机单进程,所有模块通过 `asyncio.Queue` 和 `asyncio.Event` 通信,延迟最低: ```python audio_chunk_queue: asyncio.Queue[bytes] # VAD → ASR text_queue: asyncio.Queue[str] # ASR → LLM tts_chunk_queue: asyncio.Queue[bytes] # LLM→TTS → mixer avatar_frame_queue: asyncio.Queue[np.ndarray] # TTS→MuseTalk → mixer state_event: asyncio.Event # 状态变更通知 ``` --- ## 2. 环境与依赖 ### 2.1 创建统一 conda 环境 ```bash conda create -n digital-human python=3.10 -y conda activate digital-human conda install -y -c conda-forge ffmpeg # PyTorch nightly(RTX 5090 必须,已验证版本) pip install --pre torch torchvision torchaudio \ --index-url https://download.pytorch.org/whl/nightly/cu128 ``` ### 2.2 安装各模块依赖 ```bash # MuseTalk 依赖链(按 research-notes.md 已验证) cd /home/xsl/work/MuseTalk pip install -r requirements.txt pip install --no-build-isolation chumpy pip install mmengine MMCV_WITH_OPS=1 pip install mmcv==2.1.0 --no-build-isolation pip install "mmdet>=3.3.0" "mmpose>=1.3.0" pip install xtcocotools json-tricks munkres pip install "transformers>=4.45.0" # ASR - SenseVoice pip install funasr # TTS - Kokoro pip install kokoro soundfile # TTS 升级备选 - Qwen3-TTS(M4 阶段再装) # pip install qwen3-tts # VAD pip install silero-vad # WebRTC + Web 服务 pip install aiortc aiohttp uvicorn fastapi # 工具库 pip install pydantic python-dotenv tenacity numpy opencv-python-headless ``` ### 2.3 模型权重 | 模型 | 路径 | 状态 | 大小 | |------|------|------|------| | MuseTalk v1.5 全套 | `/home/xsl/work/MuseTalk/models/` | 已有 | 5.5GB | | LivePortrait 权重 | `/home/xsl/work/LivePortrait/pretrained_weights/` | 已有 | 1.2GB | | SenseVoice-Small | 首次调用自动下载 | **新增** | ~234MB | | Kokoro-82M-v1.1-zh | 首次调用自动下载 | **新增** | ~180MB | | Silero VAD | 首次调用自动下载 | **自动** | ~2MB | ```bash # SenseVoice 模型会在首次 AutoModel() 调用时自动下载 # Kokoro 模型会在首次 import kokoro 时自动下载 # 也可以手动预下载: python -c "from funasr import AutoModel; AutoModel(model='FunAudioLLM/SenseVoiceSmall', device='cuda:0')" python -c "import kokoro; kokoro.KPipeline(lang_code='z')" ``` ### 2.4 MuseTalk 补丁(必须,已验证) `torch.load(weights_only=False)` monkey patch — 保持 research-notes.md 中已验证的方案。 ### 2.5 验收清单 - [x] `python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name())"` → True, NVIDIA GeForce RTX 5090 - [x] SenseVoice 加载模型 + 转写一段中文音频成功 - [x] Kokoro 合成一句中文语音成功 - [x] MuseTalk 单样例推理输出视频成功 --- ## 3. VAD 语音活动检测(Silero VAD, MIT) ### 3.1 实现 ```python from silero_vad import load_silero_vad, VADIterator model = load_silero_vad() vad_iterator = VADIterator( model, threshold=0.5, sampling_rate=16000, min_silence_duration_ms=300, # 尾部静音判定 speech_pad_ms=30, ) # 流式处理 WebRTC 上行音频帧 for audio_chunk in webrtc_audio_stream: speech_dict = vad_iterator(audio_chunk) if speech_dict: if 'start' in speech_dict: arbitrator.on_speech_start() if 'end' in speech_dict: arbitrator.on_speech_end(recorded_audio) ``` ### 3.2 验收 - [x] 静音环境不误触发 - [x] 正常说话稳定触发 - [x] 说话结束后 ~300ms 内检测到 speech.end --- ## 4. ASR 语音识别(SenseVoice-Small, Apache 2.0, 本地 GPU) ### 4.1 模型加载 ```python from funasr import AutoModel from funasr.utils.postprocess_utils import rich_transcription_postprocess asr_model = AutoModel( model="FunAudioLLM/SenseVoiceSmall", device="cuda:0", hub="hf", ) ``` ### 4.2 推理接口 ```python async def transcribe(audio_array: np.ndarray) -> str: """16kHz mono float32 numpy array → 文本""" res = asr_model.generate( input=audio_array, cache={}, language="zh", use_itn=True, ) return rich_transcription_postprocess(res[0]["text"]) ``` ### 4.3 性能预期 - AISHELL-1 中文 CER: 4.2%(比 whisper-turbo 的 77% WER 好一个量级) - 3 秒语音推理延迟: ~50-150ms (GPU) - VRAM: ~0.8GB ### 4.4 验收 - [ ] 10 条中文测试语音,转写准确率主观 > 90% - [ ] 单段 3 秒语音延迟 < 300ms --- ## 5. LLM 对话(在线 API,流式输出) ### 5.1 客户端实现 ```python import openai client = openai.AsyncOpenAI( api_key=os.getenv("LLM_API_KEY"), base_url=os.getenv("LLM_BASE_URL"), ) async def chat_stream(messages: list[dict]) -> AsyncIterator[str]: response = await client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=messages, stream=True, max_tokens=150, temperature=0.7, ) async for chunk in response: if chunk.choices[0].delta.content: yield chunk.choices[0].delta.content ``` ### 5.2 流式分句送 TTS - LLM 流式输出 token → 累积到句子边界(。!?,\n)→ 立即送 TTS - 系统提示词要求"用简短口语回复,每句不超过 30 字" ### 5.3 降级策略 - 超时 8s 或网络错误 → 返回预设兜底话术 - 用 `tenacity` 做 1 次快速重试(2s 超时) ### 5.4 验收 - [x] 正常首 token < 2 秒 - [x] 断网 3 秒内返回兜底话术 - [x] 20 轮对话上下文连贯 --- ## 6. TTS 语音合成(Kokoro-82M, Apache 2.0, 本地 GPU) ### 6.1 模型加载 ```python import kokoro pipeline = kokoro.KPipeline(lang_code='z') # z = 中文 ``` ### 6.2 合成接口 ```python async def synthesize(text: str) -> tuple[np.ndarray, int]: """文本 → (audio_array, sample_rate)""" generator = pipeline(text, voice='zf_xiaoxiao') # 中文女声 audio_chunks = [] for _, _, chunk in generator: audio_chunks.append(chunk) audio = np.concatenate(audio_chunks) return audio, 24000 # Kokoro 输出 24kHz ``` ### 6.3 音频格式对齐 - Kokoro 输出 24kHz → 需重采样到 16kHz 供 MuseTalk whisper - 下行音频可保持 24kHz(WebRTC 支持) ### 6.4 升级路径(Qwen3-TTS) M4 阶段如果 Kokoro 中文质量不满意,切换到 Qwen3-TTS 0.6B: - 97ms 首音频延迟 - 更好的中文自然度 - 需额外 ~3GB VRAM(总计仍 <10GB) - RTX 5090 需 `torch.compile` + CUDA graphs 优化(社区已验证方案) ### 6.5 验收 - [ ] 40 字中文文本合成延迟 < 500ms - [ ] 音质清晰,无明显机器感 - [x] 连续合成 20 句无崩溃 --- ## 7. 数字人渲染(MuseTalk v1.5, MIT, 本地 GPU) ### 7.1 离线预处理:底片库(一次性) **Step 1**: 用 LivePortrait 生成闭嘴自然动作循环视频 ```bash conda activate digital-human cd /home/xsl/work/LivePortrait for drv in d3.mp4 d6.mp4 d10.mp4; do python inference.py \ -s /path/to/avatar_photo.jpg \ -d assets/examples/driving/$drv \ --flag_crop_driving_video \ -o /home/xsl/product/assets/avatar_loops/ done ``` **Step 2**: 用 MuseTalk 预处理(提取 latents/coords/masks → 缓存) ```bash cd /home/xsl/work/MuseTalk python -m scripts.realtime_inference \ --inference_config configs/inference/realtime.yaml \ --version v15 \ --unet_model_path models/musetalkV15/unet.pth \ --unet_config models/musetalkV15/musetalk.json ``` 预处理产物缓存后,运行时直接加载,不再需要人脸检测和解析。 ### 7.2 实时口型合成:改造 MuseTalk **核心**:将 `Avatar.inference()` 从完整音频文件改为流式音频 chunk。 ```python class RealtimeAvatar: def __init__(self, prepared_data_path): # 加载预处理缓存 self.latents = torch.load(f"{prepared_data_path}/latents.pt") self.coords = pickle.load(open(f"{prepared_data_path}/coords.pkl", 'rb')) self.frames = [...] # 预加载原始帧 self.masks = [...] # 预加载 mask self.frame_idx = 0 async def process_audio_chunk(self, pcm_16k: np.ndarray) -> list[np.ndarray]: """接收 TTS PCM 块 → 返回口型帧列表""" whisper_features = audio_processor.get_audio_feature(pcm_16k) whisper_chunks = audio_processor.get_whisper_chunk(whisper_features, ...) output_frames = [] for batch in datagen(whisper_chunks, self.latents, batch_size=4): pred = unet.model(batch_latent, timesteps, encoder_hidden_states=audio_feat) recon = vae.decode_latents(pred) for frame in recon: blended = blend_frame(frame, self.frame_idx) output_frames.append(blended) self.frame_idx = (self.frame_idx + 1) % len(self.frames) return output_frames def get_idle_frame(self) -> np.ndarray: """闭嘴循环帧(不过 UNet)""" frame = self.frames[self.frame_idx % len(self.frames)] self.frame_idx = (self.frame_idx + 1) % len(self.frames) return frame ``` ### 7.3 帧率与同步 - 25fps(与底片一致),每帧 40ms - TTS 音频时长 → 计算帧数 → MuseTalk 生成对应数量帧 - asyncio 定时器控制帧发送节奏 ### 7.4 验收 - [ ] 闭嘴循环 60 秒无卡顿 - [ ] 5 秒 TTS 音频口型帧 200ms 内开始输出 - [ ] 口型同步主观无 >200ms 错位 --- ## 8. WebRTC 网关与前端 ### 8.1 后端(aiortc + FastAPI, BSD-3) ```python @app.post("/webrtc/offer") async def webrtc_offer(request: Request): params = await request.json() offer = RTCSessionDescription(sdp=params["sdp"], type=params["type"]) pc = RTCPeerConnection() video_track = AvatarVideoTrack() # 从 MuseTalk 帧队列读取 audio_track = AvatarAudioTrack() # 从 TTS 音频队列读取 pc.addTrack(video_track) pc.addTrack(audio_track) @pc.on("track") def on_track(track): if track.kind == "audio": asyncio.ensure_future(process_incoming_audio(track)) await pc.setRemoteDescription(offer) answer = await pc.createAnswer() await pc.setLocalDescription(answer) return {"sdp": pc.localDescription.sdp, "type": pc.localDescription.type} ``` ### 8.2 前端页面(纯 HTML/JS,无框架依赖) ``` ┌──────────────────────────────┐ │ 数字人视频 │ │ (WebRTC video 标签) │ │ │ ├──────────────────────────────┤ │ 🎤 正在倾听... │ ← 状态指示 ├──────────────────────────────┤ │ 你: "今天天气怎么样?" │ ← ASR 字幕 │ AI: "今天天气不错呢..." │ ← LLM 回复字幕 └──────────────────────────────┘ ``` - 页面加载 → 自动建 WebRTC 连接 → 请求麦克风权限 - 下行视频+音频 → `