Skip to content

07 · 数字人 ​

架构剖析第七篇:数字人(Digital Human)渲染管线、语音合成(GPT-SoVITS)、后端抽象、VRAM 门控。 对应 ARCHITECTURE.md §8。


§8.1 系统概览 ​

数字人 = 语音克隆(GPT-SoVITS) + 动画驱动(anime backend) + 流式多路复用

text gen ──► tts_worker ──► audio_q ──┬─► backend_worker ──► frame_q ──┐
                                      │                                │
                                      └─► mux_worker ◄─────────────────┘
                                              │
                                              ▼
                                          StreamChunk

三个 asyncio 任务通过有界 asyncio.Queue 通信。取消:关闭消费者迭代器 → finally 块取消所有 worker。

§8.2 DigitalHumanSession(src/digital_human/session.py) ​

8.2.1 构造 ​

python
class DigitalHumanSession:
    def __init__(
        self,
        config: Optional[DigitalHumanConfig] = None,
        library: Optional[CharacterLibrary] = None,
    ):
        self.config = config or DigitalHumanConfig()
        self.tts = StreamingTTS(self.config)
        self.library = library or CharacterLibrary()
        self._backend: Optional[Backend] = None
        self._source: Optional[Source] = None

8.2.2 生命周期 ​

python
async def prepare(
    self,
    *,
    voice_ref: str,           # voice.wav 路径
    voice_ref_text: str = "",  # 参考文本
    character_id: Optional[str] = None,
    backend: Optional[str] = None,
) -> None:
    """加载 TTS + 选择后端。
    
    backend 未指定时由 select_backend() 根据输入自动选择。
    """

async def stream(
    self,
    text_source: AsyncIterator[str],
) -> AsyncIterator[StreamChunk]:
    """流式生成。text_source 是 token 异步迭代器。
    
    产出 StreamChunk = (pcm_bytes, video_frame)。
    """

8.2.3 TTS 存活检查 ​

python
@property
def _tts_loaded(self) -> bool:
    """True when TTS subprocess is admitted AND actually alive.
    
    旧版只检查 self.tts._runner is not None → 死后仍标记 "loaded" →
    用户首次 _synth 命中 5-15s 冷启动(正是 eager load 要防止的死寂)。
    
    现在轮询子进程;死了 → 当作 not-loaded → 下次 load() 重新 spawn。
    """

§8.3 DigitalHumanConfig(src/digital_human/config.py) ​

python
@dataclass
class DigitalHumanConfig:
    # 路径
    gpt_sovits_dir: str = _default_gpt_sovits_dir()   # GPT-SoVITS 集成包根
    gpt_sovits_version: str = "v2"                     # v1/v2/v3
    gpt_sovits_checkpoint: Optional[str] = None         # .ckpt/.pth 权重
    tha3_dir: str = _default_tha3_dir()                # THA3 demo checkout
    tha3_variant: str = "separable_float"              # THA3 变体
    
    # 流式参数
    audio_chunk_ms: int = 200                          # PCM chunk 目标大小
    
    # 图像处理
    anime_auto_bust_up: bool = True                    # rembg + 半身重构

环境变量:

  • GPT_SOVITS_DIR —— 覆盖 GPT-SoVITS 路径
  • THA3_DIR —— 覆盖 THA3 路径

§8.4 后端抽象 ​

8.4.1 后端清单 ​

后端目录说明
anime_morphbackends/anime_morph/主后端。配对口型 atlas + 显式 mask blend
anime_dynamicbackends/anime_dynamic/THA3 单图驱动(fallback)
anime_pretrainedbackends/anime_pretrained/Live2D 预训练角色
anime_seethroughbackends/anime_seethrough/透视模式
realphoto_dynamicbackends/realphoto_dynamic/真实照片驱动(已弃用)

8.4.2 Backend 基类 ​

python
# src/digital_human/backends/base.py
class Backend:
    """抽象基类。每个后端实现:
    - prepare(spec: PrepareSpec) → Source
    - drive(audio_chunk) → list[MotionFrame]
    - render(frame) → np.ndarray
    """

@dataclass
class PrepareSpec:
    voice_ref: str
    voice_ref_text: str
    character_id: Optional[str]
    source_image: Optional[str]

@dataclass
class Source:
    """后端资源句柄。"""

8.4.3 后端选择 ​

python
# src/digital_human/backends/registry.py
def select_backend(hint: Optional[str] = None, **kwargs) -> Backend:
    """根据 hint 和可用资源选择后端。
    
    hint=None → 默认 anime_morph。
    """

8.4.4 项目意图 ​

The project is intentionally anime-first. Older LivePortrait / JoyVASA real-photo experiments are deprecated because they did not produce reliable Chinese lip-sync and required far more weights/runtime complexity than the current mouth-atlas pipeline.

§8.5 流式参数 ​

8.5.1 audio_chunk_ms = 200 ​

为什么 200ms(旧值 500ms):

mux worker 将每个 audio chunk 配对 audio_chunk_ms × fps / 1000 渲染帧后
产出 StreamChunk。

500ms × 25fps = 12 帧/chunk
  → seethrough GPU render ~30-50ms/帧
  → 每 chunk 等待 360-600ms 墙钟时间
  → 首 chunk 到达比 TTS 完成晚 ~500ms

200ms × 25fps = 5 帧/chunk
  → ~150-250ms 渲染预算
  → 首 chunk 几乎在 TTS 完成时到达 renderer

代价:更多 WS 消息/秒。收益:首 chunk 延迟大幅降低。

8.5.2 StreamChunk 结构 ​

python
# src/digital_human/types.py
@dataclass
class StreamChunk:
    audio_pcm: bytes         # PCM 音频数据
    video_frame: np.ndarray  # 视频帧
    sample_rate: int         # 采样率
    pts_ms: float            # 呈现时间戳

§8.6 管线子模块 ​

src/digital_human/pipeline/
├── tts.py              # StreamingTTS(GPT-SoVITS 子进程通信)
├── motion.py           # 动作生成(音频 → 驱动信号)
├── render.py           # 渲染(驱动信号 → 视频帧)
├── emotion.py          # 情绪分类器
├── theme.py            # 主题/风格
└── voice_ref_gen.py    # 声音参考生成

§8.7 VRAM 门控 ​

8.7.1 GPU 分级与功能矩阵 ​

分级VRAM数字人支持说明
mode0无 GPU❌全部 API-only
tier4~4GB⚠️ 受限音频 only,无视频/情绪分类
tier6~6GB⚠️ 音频音频 + 基础视频
tier8≥8GB✅ 完整全功能

8.7.2 VRAM 优先级 ​

优先级数字人组件说明
1 — CRITICALTTS(说话中)、whisper(听中)用户活跃请求
2 — IMPORTANTbge embedding稳态交互
3 — NICEemotion tagger, avatar poser有用但可等
4 — BACKGROUND训练永不用户可见

8.7.3 预热策略 ​

Bridge 启动时预热"最近 Agent 的 DH":

python
# src/bridge/app.py 模块级
# 预热 embedder / rembg / rewriter / 最近 Agent 的数字人

启动后瞬时可交互。

§8.8 与记忆协同 ​

  • DH 会话用同一套 DialogueManager / 记忆
  • 数字人只是另一个"发言外壳"
  • 记忆不因 DH 隔离
  • Assistant.send_message() 是入口:
python
async def send_message(self, text_stream, *, ref_text="",
                       character_id=None, backend=None):
    """Stream reply as text+audio+video.
    
    text_stream: 异步 token 迭代器
    voice_ref: {agent_path}/voice.wav
    yield: (text_chunk, StreamChunk)
    """
    session = DigitalHumanSession(DigitalHumanConfig())
    await asyncio.to_thread(session.prepare,
                            voice_ref=voice_ref, voice_ref_text=ref_text,
                            character_id=character_id, backend=backend)
    async for chunk in session.stream(_text_gen()):
        yield chunk

§8.9 CharacterLibrary ​

python
# src/digital_human/library.py
class CharacterLibrary:
    """角色库管理。
    
    get_default() → 默认角色条目
    list() → 所有可用角色
    """

角色数据存储在 workspace/agents/agent_<id>/:

  • voice.wav —— 声音参考
  • client_portraits/{key}.png —— 客户端肖像(白色背景 → strip_white_file 透明化)

下一篇:08-安全与监控 →

Familiars · 多 Agent AI 桌面应用