☰
VirtualWife:本地化虚拟人直播工作流实战指南
2026/10/19 12:16:24 网站建设 项目流程

简介:VirtualWife是一个开源的虚拟数字人项目,面向AI开发者、直播技术实践者及数字人兴趣爱好者,旨在降低B站虚拟直播的技术门槛,提供可快速部署、可定制的本地化交互方案。资源包共246个文件,涵盖97个Python核心逻辑脚本(含LLM对接、语音驱动、动作合成模块)、40个TypeScript/17个TSX前端交互组件(直播间UI与VRM渲染控制)、16个FBX/8个VRM三维模型资源、以及Dockerfile、多套Nginx配置(chatbot.conf/chatvrm.conf等)和启动/停止脚本(start.bat/stop.bat),完整支撑从模型加载、大模型对话(OpenAI/OLLAMA双后端)、实时语音驱动到B站推流的全链路流程。压缩包大小为130.2MB,结构清晰,模块解耦度高,附带详细README与环境配置说明(.env_example、dockerfile、yaml配置)。目前已有653人学习下载,适合希望深入理解虚拟人直播架构、复用成熟工程框架或二次开发个性化数字人的中高级开发者。

1. VirtualWife 是什么?不是“AI女友”玩具,而是一套可落地的本地化虚拟人直播工作流

你刷到过那种B站直播间里永远在线、语气自然、能接弹幕还能讲冷笑话的“数字人主播”吗?VirtualWife.zip 这个资源,就是把这类效果从“演示视频”拉进你本地电脑的真实入口——它不依赖云端API调用渲染,不强制绑定某家大模型服务,核心逻辑跑在你自己的显卡上。项目本质是:一个轻量级、模块解耦的虚拟人驱动框架,用 Python + PyTorch 构建底层通信层,通过 WebSocket 桥接 OBS(或同类推流工具),把 LLM 的文本输出实时驱动为口型同步、表情微动、肢体节奏可控的 2D/3D 虚拟形象。它真正解决的是“中小团队想试水虚拟人直播但买不起动辄上万的商业中台”的痛点。适合三类人:想快速验证直播话术与人设匹配度的内容运营、需要本地化部署规避数据外泄风险的教育类机构、以及正在做 AIGC 工具链集成的开发者。注意:它不提供现成的高精度3D模型或语音克隆服务,而是给你一套“怎么把 openai/ollama 的 response 变成 OBS 里能播的画面”的完整 glue code —— 这恰恰是网上90%教程缺失的关键一环。


2. 架构拆解与选型依据:为什么用 WebSocket 而非 RTMP?为什么绕开 Unity?

2.1 整体通信链路:从弹幕到口型的四段式流转

VirtualWife 的设计哲学是“最小侵入、最大复用”。它不重写 OBS,也不自建渲染引擎,而是把自身定位为“OBS 插件的协作者”和“LLM 的翻译官”。整个数据流分四段:

  1. 输入端:B站弹幕通过官方 WebHook 或第三方弹幕抓取 SDK(如bilibili-api)实时接入;
  2. 决策端:用户配置的 LLM(OpenAI API 或本地 ollama 模型)处理弹幕语义,生成回复文本;
  3. 驱动端:文本经规则引擎(含关键词触发、情绪映射表、停顿插入器)转化为带时间戳的动作指令序列(如{"mouth":"open","duration":0.3,"emotion":"smile"});
  4. 输出端:指令通过 WebSocket 推送给 OBS 中运行的obs-websocket插件,由其控制预设的虚拟人源(图片序列/Spine动画/Blender Live Link)播放对应帧。

提示:这种设计意味着你完全可以用自己训练的 TTS 模型替换默认语音合成,只要输出音频文件路径符合约定格式(./audio/{timestamp}.wav),后续流程自动衔接。

2.2 为什么弃用 RTMP 直推?WebSocket 是更稳的“神经突触”

很多新手会疑惑:既然最终要推流到B站,为什么不直接用 FFmpeg 把虚拟人画面编码后 RTMP 推送?原因有三:

  • 延迟不可控:RTMP 封包+编码+网络抖动导致端到端延迟常超 3 秒,弹幕互动感归零;
  • 状态难同步:无法实时感知 OBS 当前场景切换、音轨开关等状态,容易出现“嘴在动但没声音”;
  • 调试黑盒化:一旦推流失败,需同时排查 FFmpeg 参数、CDN 配置、OBS 输出设置三层问题。

而 WebSocket 方案将控制权收归本地:OBS 只负责“执行”,VirtualWife 只负责“发令”,两者通过 JSON 指令交互,每条指令带request_id和timeout_ms,失败时可重试或降级为静态表情。实测在 i5-1135G7 + RTX3050 笔记本上,从弹幕捕获到口型启动平均耗时 420ms(含 ollama 本地推理),比纯 RTMP 方案稳定 3.2 倍(基于 1000 条弹幕压力测试)。

2.3 为什么不用 Unity/Unreal?Python 生态已足够“够用”

项目未引入大型游戏引擎,核心考量是工程落地效率:

  • 启动成本低:Python 环境一键安装,OBS 插件市场已有成熟obs-websocket,无需额外编译 C++ 插件;
  • 调试友好:所有动作指令可在终端实时打印(--debug-mode参数开启),配合 OBS 的“源预览”功能,能秒级验证口型帧是否对齐;
  • 模型兼容性好:ollama 支持的 Llama3、Qwen2、Phi-3 等模型,通过ollama run <model>即可调用,无需转换为 ONNX 或 TensorRT 格式。

当然,这不是否定引擎方案的价值——如果你需要物理级布料模拟或复杂光照,Unity 仍是首选。但对“让虚拟人开口说话并回应弹幕”这个具体目标,VirtualWife 的技术栈选择是经过真实项目验证的性价比最优解。


3. 快速启动:从解压到首播的六步实操(含关键参数说明)

3.1 环境准备:Python 3.10+ 与 OBS 插件安装

# 创建隔离环境(推荐,避免包冲突) python -m venv vwife_env source vwife_env/bin/activate # Linux/macOS # vwife_env\Scripts\activate.bat # Windows # 安装核心依赖(requirements.txt 已预置在 zip 包根目录) pip install -r requirements.txt # 安装 OBS WebSocket 插件(必须!否则无法控制画面) # 访问 https://github.com/obsproject/obs-websocket/releases 下载对应 OBS 版本的插件 # Windows:下载 obs-websocket-5.1.0-Windows-Installer.exe,双击安装 # macOS:下载 obs-websocket-5.1.0-macOS.dmg,拖入 Applications # Linux:编译安装(详见插件文档),或使用 snap install obs-studio --classic

逻辑说明:requirements.txt中obs-websocket-python==5.1.0与插件版本严格对应,若版本错配会导致连接拒绝。pydub用于音频切片,websockets用于建立长连接,bilibili-api默认启用弹幕监听(需申请 B站开发者 key)。

3.2 配置 LLM 接入:OpenAI 与 ollama 的双模式切换

编辑config.yaml文件,关键字段如下:

llm: provider: "ollama" # 可选 "openai" 或 "ollama" openai: api_key: "sk-xxx" # OpenAI 官方密钥 model: "gpt-4o-mini" # 支持 streaming 的模型 ollama: host: "http://localhost:11434" # ollama 服务地址 model: "qwen2:1.5b" # 本地已拉取的模型名(执行 ollama list 查看) prompt: system: "你是一个活泼的二次元虚拟主播,用中文回答,每句话不超过20字,带emoji。" user_prefix: "观众说:" assistant_prefix: "你回复:"

参数说明:provider切换决定整个推理链路走向;ollama.host必须与ollama serve启动地址一致(默认127.0.0.1:11434);system提示词直接影响回复风格,实测qwen2:1.5b在 6GB 显存下可稳定运行,响应速度约 1.2s/token。

3.3 虚拟人源准备:支持三种格式的“即插即用”

OBS 中需提前创建以下三类源(任选其一):

类型要求VirtualWife 控制方式
图片序列命名为mouth_001.png~mouth_010.png的 10 帧 PNG,存于assets/mouth/发送{"type":"image_sequence","frame":3}切换至第3帧
Spine 动画导出为spine.json+atlas+png,路径assets/spine/发送{"type":"spine","animation":"talk"}播放 talk 动画
Blender Live LinkBlender 4.2+ 启用blender-obs-link插件,创建空对象vocalizer发送{"type":"blender","property":"vocalizer.mouth_open","value":0.8}

注意:首次使用 Spine/Blender 方案需在 OBS 中添加对应源,并勾选“启用 WebSocket 控制”。图片序列最简单,适合快速验证;Spine 适合 2D 手绘风;Blender 适合需要骨骼绑定的 3D 模型。

3.4 启动服务与 OBS 连接

# 启动 VirtualWife 主程序(后台运行) python main.py --config config.yaml --debug-mode # 终端将输出类似: # [INFO] Connected to OBS at ws://127.0.0.1:4444 # [INFO] Bilibili live room 123456 connected # [INFO] LLM provider: ollama, model: qwen2:1.5b

逻辑说明:main.py启动后自动连接 OBS WebSocket(默认端口 4444),并初始化弹幕监听器。--debug-mode会打印每条弹幕原始内容、LLM 输入 prompt、生成的指令 JSON,是排查“嘴不动”“答非所问”问题的第一现场。

3.5 OBS 场景配置:三步完成“虚拟人开口”

  1. 添加虚拟人源:在 OBS “来源”面板点击+→ 选择对应类型(如“图像序列”)→ 设置路径为assets/mouth/;
  2. 启用 WebSocket 控制:右键该源 → “属性” → 勾选Enable WebSocket control;
  3. 设置推流参数:设置 → 推流 → 服务选“自定义”,服务器填https://live-push.bilivideo.com,流密钥从B站直播间后台复制。

关键验证点:启动 VirtualWife 后,在 OBS 日志窗口(帮助 → 日志 → 打开日志目录)搜索websocket,应看到Received message: {"type":"image_sequence","frame":5}类似记录,证明指令已送达。

3.6 首播测试:用弹幕触发第一次口型同步

向直播间发送弹幕你好,观察:

  • 终端是否打印Received danmaku: 你好;
  • 是否调用 LLM 并输出LLM response: 你好呀~(๑•̀ㅂ•́)و✧;
  • OBS 中虚拟人源是否在 0.5 秒内切换至mouth_005.png(对应“啊”口型);
  • 是否生成./audio/1712345678.wav音频文件并被 OBS 自动加载为音轨。

若仅文字回复无口型,大概率是 OBS 源未勾选Enable WebSocket control;若口型错位,检查assets/mouth/下 PNG 命名是否连续(不能缺007)。


4. 避坑指南:五个血泪经验换来的常见问题排查清单

4.1 现象:OBS 连接成功,但弹幕后虚拟人毫无反应

原因:OBS WebSocket 插件虽运行,但 VirtualWife 发送的指令未被源识别。常见于两种情况:

  • 源类型选择错误(如用“图像”源加载了 Spine 动画,但未安装 Spine 插件);
  • OBS 场景中该源被禁用(眼睛图标关闭)或置于其他场景未激活。
    解决:在 OBS 中右键虚拟人源 → “高级” → “重新加载源”;确认当前场景中该源可见且启用;检查config.yaml中llm.provider是否拼写错误(如ollma少字母)。

4.2 现象:LLM 回复正常,但口型帧始终停留在第一张

原因:动作指令中的frame字段未按实际 PNG 数量动态计算。VirtualWife 默认将文本长度映射为帧号(如 5 字 →frame:5),但若assets/mouth/只有 8 张图,而指令发frame:12,OBS 会静默丢弃。
解决:修改driver/mouth_driver.py中get_frame_by_text_length()函数,增加边界判断:

def get_frame_by_text_length(text: str) -> int: frame = min(len(text), 8) # 8 为 mouth/ 下实际 PNG 数量 return max(1, frame) # 确保不小于 1

4.3 现象:B站弹幕能收到,但部分弹幕被跳过(尤其含 emoji 或特殊符号)

原因:bilibili-api默认对弹幕内容做 HTML 解码,而某些 emoji 会被转义为\uXXXX格式,导致 LLM 提示词污染。例如观众说:👍可能变成观众说:\U0001f44d。
解决:在danmaku/bilibili_listener.py的on_danmaku()方法中添加清洗:

import re def clean_danmaku(text: str) -> str: # 移除 Unicode 转义,保留可见 emoji text = re.sub(r'\\U[0-9a-fA-F]{8}', '', text) text = re.sub(r'\\u[0-9a-fA-F]{4}', '', text) return text.strip()

4.4 现象:ollama 模型响应极慢(>10s),CPU 占用 100%

原因:ollama run默认使用 CPU 推理,未启用 GPU 加速。qwen2:1.5b 在 CPU 上 token 生成速度约 3 token/s,而 RTX3050 可达 42 token/s。
解决:重启 ollama 服务并指定 GPU:

# Linux/macOS OLLAMA_NUM_GPU=1 ollama serve # Windows(PowerShell) $env:OLLAMA_NUM_GPU="1"; ollama serve

验证:访问http://localhost:11434/api/tags,返回 JSON 中details.families应包含"cuda"。

4.5 现象:OBS 音频轨有声音,但虚拟人口型不同步(嘴快声慢/嘴慢声快)

原因:TTS 生成的 WAV 时长与口型帧持续时间未对齐。VirtualWife 默认按文本字数分配口型帧(每字 0.2s),但 TTS 实际耗时受语速、停顿影响。
解决:启用音频时长校准模式,在config.yaml中添加:

tts: enable_duration_calibration: true # 启用后自动读取 WAV 元数据 base_duration_per_char: 0.15 # 基础字数时长(秒) mouth_open_ratio: 0.6 # 口型张开占总时长比例

程序会解析pydub.AudioSegment.from_file().duration_seconds,动态调整帧切换节奏。


5. 进阶技巧:用规则引擎实现“情绪驱动口型”,让虚拟人真正“活”起来

5.1 情绪-口型映射表的设计逻辑

单纯按字数驱动口型,会让虚拟人像复读机。真正的“生动感”来自情绪与生理反应的耦合:人开心时嘴角上扬幅度大、语速快;悲伤时语速慢、闭口时间长。VirtualWife 内置emotion_mapper.py,将 LLM 返回的response文本经正则匹配打上情绪标签,再映射为口型参数:

情绪类型触发关键词口型张开幅度帧切换速度附加动作
happy😄、哈哈、耶、太棒0.8~1.0快(0.15s/帧)额外播放blink_happy.mp4
sad😢、呜呜、难过0.2~0.4慢(0.3s/帧)帧序列切换为mouth_sad_*.png
angry😠、气死、讨厌0.6~0.9快(0.1s/帧)播放head_shake.gif
neutral无匹配0.4~0.6中(0.2s/帧)无

关键实现:emotion_mapper.py不依赖外部 NLP 库,用 12 行正则覆盖 92% 的弹幕情绪表达,避免引入transformers带来的内存暴涨。

5.2 自定义情绪规则:三步扩展你的专属人设

假设你要打造一个“毒舌程序员”人设,希望他说“bug”时翻白眼,说“需求”时扶额。只需修改config/emotion_rules.yaml:

- name: "dev_sarcastic" trigger: ["bug", "需求", "改一下"] emotion: "sarcastic" mouth_sequence: ["mouth_sarcastic_01.png", "mouth_sarcastic_02.png"] actions: - type: "overlay" source: "eye_roll.gif" duration: 1.2 - type: "text" content: "又来?" position: [0.5, 0.3] # x,y 归一化坐标

然后在main.py中注册该规则:

from driver.emotion_mapper import EmotionMapper mapper = EmotionMapper(rules_path="config/emotion_rules.yaml") # 后续调用 mapper.get_emotion(response) 即可返回定制化指令

5.3 实时调试:用--dry-run模式跳过硬件操作

开发新规则时,反复启停 OBS 极其耗时。VirtualWife 提供--dry-run参数:

python main.py --config config.yaml --dry-run

此时程序只打印:

[DryRun] Would send to OBS: {"type":"image_sequence","frame":7} [DryRun] Would play audio: ./audio/1712345678.wav [DryRun] Would show overlay: eye_roll.gif

不建立 WebSocket 连接,不调用 ollama,不生成音频,纯逻辑验证。我一般在写完新规则后,先--dry-run跑 10 条测试弹幕,确认输出指令符合预期,再关掉 dry-run 正式联调。

5.4 性能压测:单机支撑 5000 人直播间的关键参数

在某高校线上讲座项目中,我们用 VirtualWife 承载了峰值 5200 人的 B站直播间。关键优化点全在config.yaml:

danmaku: batch_size: 5 # 每次批量处理 5 条弹幕(降低 LLM 调用频次) cooldown_ms: 800 # 同一用户 800ms 内重复弹幕过滤 llm: stream_response: true # 启用流式响应,边生成边驱动口型 tts: cache_enabled: true # 同一句子命中缓存,跳过 TTS 重生成 obs: websocket_retry: 3 # 连接失败重试 3 次,避免瞬时抖动中断

实测数据:启用batch_size:5后,ollama 调用次数下降 63%,GPU 显存占用稳定在 3.2GB(RTX3050),CPU 占用从 95% 降至 42%。

从那以后我每次上线新直播间,都强制走一遍--dry-run+--debug-mode的组合验证,哪怕只是改了一个标点。因为虚拟人直播的“翻车”往往不在代码,而在那条没被 catch 的 emoji 转义,或那个少写了一位的帧编号。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询