用 NVIDIA Nemotron 开放模型打造一座鬼屋

或者更确切地说……

嘘!现在从我的草坪上滚开!

BlogThumbnail

TL;DR:MonsterHouse 是我为万圣节打造的互动投影装置。他会倾听“不给糖就捣蛋”的孩子们说话,用一艘脾气暴躁的老海盗船的嗓音回应,而当访客记得讲礼貌时,他的语气就会软化下来。他基于NVIDIA Nemotron Voice Agent构建,搭配 NVIDIANemotron ASR、Nemotron 3.5 Bolt E4B 4GB、Magpie TTS Zeroshot以及Audio2Face-3D,并且运行在一台工作站上。

我希望万圣节来我家玩的孩子们能以有趣而独特的方式互动。这座房子将负责掌控局面并分发糖果,但它的脾气更多是暴躁而非吓人。不知怎的,我的解决方案是给车库门装上一张嘴,并教它发牢骚。

这就成了 MonsterHouse:一座脾气暴躁的老建筑,满口海盗故事,咧着一张火焰般的笑容,还有一个可疑的弱点——对“请”字毫无抵抗力。NVIDIA Nemotron Voice Agent 提供对话基础。Nemotron Speech 负责聆听,Bolt E4B 是大脑,NVIDIA Magpie TTS Zeroshot 是实时嗓音,而 NVIDIA Audio2Face-3D 让那张木头嘴开口说话。

Monster House 的工作原理

一张满是木刺的笑容

嘴是最先出现的。我想象着木质车库门的木板裂成参差不齐的嘴唇,每当他张开那张被诅咒的嘴时,地狱的烈焰便从门后照亮车道。他需要看起来古老、富有表现力,并且对访客的存在略带不满。

fireMouth一座有满肚子话要说的房子摆出的几种表情。

创建自定义动画绑定是构建过程中较为耗费人力的环节之一。在把它彻底搞定之后,我就可以导出面部表情形状,供 Audio2Face-3D 后续进行动画驱动。角色 SkelMesh 在 Autodesk Maya 中构建,然后导出到 Unreal Engine,在门后添加了虚拟火焰和特效。没有眼睛、没有眉毛、也不眨眼,他是个出奇省资源的怪物。

在构建角色的种种琐碎事务进行的同时,NVIDIA 打造了 Nemotron Voice Agent。原本应该是构建中最困难的部分,现在变得相当有趣、可迭代且简单。我开箱即拥有了聆听、对话和语音的基础。这座房子已经有了嘴巴。现在他终于能找到可以抱怨的事情了。

教它没礼貌

这座房子需要一个和它性格一样暴躁的过去,所以我设计了一个背景故事:这座房子是用一艘沉没海盗船的木材建造的,被其暴风雨般的过往所萦绕。那些航海风格的抱怨现在有了理由。这就是那艘船,转世成了房地产,而且对此并不完全满意。

表演需要一个引导,一条从问候到告别的路径,同时沿途提供奖励。首先,房子会用“我正忙着嘟囔呢,去烦邻居吧!”之类的话把访客打发走。但坚持会让他软化。一句“请”可以立刻突破,而“谢谢”则能赢得温暖的告别。一旦访客赢得了他,我希望他就保持被赢得的状态。没人想和同一扇车库门谈判两次。

这正是 Voice Agent 真正出彩的地方。你可以选择任何你喜欢的 LLM。因为我希望系统离线且完全自包含,所以我有内存和算力预算。

这个角色需要的不仅仅是一句“脾气暴躁”的提示词。它必须遵循一个主对话流程,回应访客实际说的话,跟踪遭遇状态,并为流水线的其余部分留出足够的算力。

在测试了十多个模型之后,我最终选定了Bolt E4B来提供实时对话。它是一个专为本地对话智能体和交互角色打造的开源小型语言模型,这个描述用来形容一扇会说话的车库门倒也贴切。我使用 4-bit 量化版本(名字里的“4GB”即指此)作为默认的回复模型,并搭配BF16 检查点用于对比。

如果到现在还不明显的话,我在 NVIDIA 工作,正因如此,我得以在 Bolt E4B 公开发布前进行测试,并发现它是一个可靠的选择,其占用空间比其他领跑者——Qwen3 30B-A3B 和 Qwen 3.6 35B-A3B——更小。

它运行在 Nemotron Voice Agent 流水线中,把访客说的话转化为下一句恰到好处的不情愿回复。

Bolt 提供了对话基础。我的提示词、应用逻辑以及在数千条经过审核的示例对话上训练的 LoRA 适配器,将它塑造成这艘特定的固执飞船。

创建一个包含 5000 多条多轮对话的数据集,意味着我可以为每个我认为足够好、值得作为选项的模型训练 LoRA。有些训练了大约一小时,有些则要几个小时。一个本地智能体负责运行一切并对结果进行基准测试。那个智能体和一个大语言模型最初创建并评判了这个数据集。这些工作是在夜间或我做白天本职工作时进行的。

在 LoRA 就位后,我会检查回复的连贯性、时机和语气,对模型和适配器进行质量、重复度和速度的基准测试,然后试听表现。房子最终应该会让步,但不能变成一个欢快的门房。一点小小的抗拒正是魅力所在。和车库门无休止地争论则不是。

值得等待的声音

image两条捷径,两种输出。预写文本跳过实时对话生成,保存的音频跳过语音合成。干净的语音独立于观众音效来驱动动画。

如果鬼屋抱怨得太慢,就会失去威信。流水线会在整个回答完成之前就放出可说的文本,因此鬼屋可以在句子其余部分还在传输时就开始说话。预先准备好的问候语和糖果笑话覆盖了常见的互动:文本库节省了语言模型的工作量,缓存的录音节省了语音合成。即使是古老的暴躁鬼,也能从排练中受益。

median end-of-speech-to-audio with Bolt Q4 & TTS = 1.70 seconds
median end-of-speech-to-audio cached responses = 0.72 seconds
(note: speech-end detection currently waits for 300 ms of silence )

NVIDIA Magpie TTS Zeroshot将文本转换为标准英式英语。它的零样本语音条件使用一段简短的参考样本,而不是训练过的声音;我的样本提供了暴躁海盗的角色。鬼屋有七种情绪,从暴躁、凶恶到愉悦、欣喜,由提示模板指定、由 LLM 提供的情绪标签决定每句台词的表达方式。由于每种情绪都需要一段不同的 3-10 秒语音样本,我在 UI 的工具标签页上添加了一个“Sample your Voice”区块。然后我就可以用我最拿手的快乐海盗或愤怒海盗的声音朗读并录制一段示例文本。到了“开始”的时候,Bolt E4B 决定说什么,Magpie 则把那段文本转换成带有恰当愉悦或绝望情绪的语音。

流水线中的下一个环节是Audio2Face-3D以及我在 Epic 的 Unreal Engine 中创建的 3D 角色场景。默认模型权重最适合干净、中性的男声或女声,不要太低沉,也不要太尖细(根据你的用例选择合适性别的模型)。我希望鬼屋拥有非常低沉、恶魔般的声音,并带有回声和混响。在这种情况下,由于我的语音样本并不具备那种戏剧性的厚重感,这反而成了优势。Audio2Face-3D 从样本中获得干净的语音,第二条音频流则添加可调节的实时人声效果,通过扬声器播放出来。

为了让一切顺畅运行,我把工作拆分到两块 GPU 上:语言和语音模型流水线运行在 NVIDIA RTX PRO 6000 Blackwell Max-Q 上,Unreal Engine 图形与 Audio2Face-3D 运行在 NVIDIA RTX PRO 4000 Blackwell 上。内存不是问题,算力才是。我希望无论智能体流水线在做什么处理,动画都能保持一致的帧率。

语音、动画和特效必须同步到位。一个有说服力的怪物需要的不仅仅是一个快速回答;它听起来必须像是这栋楼在抱怨。就我而言,他做到了。同步音频输出的微调是可用的。我发现我可以设置一次就忘掉它们。(100ms 补偿 Audio2Face-3D 的缓冲)

怪物有一张控制台

在这一过程中,“让房子说话”需要一个充满控件的界面。我可以试听缓存台词、调整开场情绪、跟踪对话、更改语音处理并调整嘴部动画。标签页涵盖对话、模型选择、录音、提示词、性能监控和摄像头画面,而音板(soundboard)和自定义对话字段让我能把话塞进他嘴里。还有模型加载控件和一个 Unreal Engine 连接指示器,因为有时候怪物不是在生闷气。他是断线了。

imageMonsterHouse 控制台,带有音板、自定义台词、开场情绪和对话流程。

火焰和光晕也加入了表演。可编辑规则将视觉提示与输出绑定:实时提示随语音开始,而录制的表演可以跟随保存的时间线。我可以引导情绪变化,而无需重建场景。

实时模式运行“不给糖就捣蛋”角色。演示模式说服房子解释自己。麦克风模式让我直接通过房子说话,绕过识别、对话生成和语音合成。我干净的声音驱动嘴部;观众听到的是处理后的版本。房子认为这是附身。我认为这是一个有用的后备方案。

下一次敲门

NVIDIA Nemotron 3 Nano Omni是一个 VLM,为房子提供视觉上下文。作为可选的观察者,它解读摄像头图像并描述可见的服装,让角色有具体的东西可以做出反应。不想重新发明每一个轮子,我选择了流行的目标检测和跟踪模型 YOLO 和 ByteTrack 来统计和跟踪当前视野中的人。将这些任务分开,让房子能注意到一群人,而无需让 Omni 叙述每一帧。

为了做一个足够离谱的测试,我给 Omni 看了一段五只穿服装的羊在门口的视频。它保存的分析识别出了海盗、吸血鬼、女巫、南瓜和幽灵装扮。这正是我希望这栋房子使用的视觉上下文。显然,连牲畜的万圣节计划都比我更精心。

image合成测试回放中的五个毛茸茸的讨糖者。服装标签来自保存的 Omni 分析;方框和跟踪 ID 仅作示意。

这些羊是合成测试,并非万圣节之夜成功运行的证据。真实的车道人群、服装和夜间照明仍需测试。检测确实会触发自动问候,但我担心流行服装识别和恰到好处的机智回应可能需要一些后训练。由于这栋房子没有眼睛,他称自己的访客感知为回声定位。这一点我就由着他了。

还有更多工作要做:动画总能改进,完整的性能测试需要真实访客,糖果终有一天会自动提供。我还没达到最初的目标——让他负责万圣节,而我带着家人去看其他街区的鬼怪。但这仍然是一切的意义所在。总有一天,一个孩子会遇到一栋脾气暴躁的老房子,试着付出一点善意,然后发现这栋老木头也有温柔的一面。还会送出糖果棒。

打造你自己的离谱角色

MonsterHouse 代码本身并未打包发布,但它使用的每一个 NVIDIA 模型如今都可获取,而 NVIDIA Nemotron Voice Agent 能帮你完成大部分工作。这是我会遵循的路径。

  1. 从语音流水线开始。克隆NVIDIA Nemotron Voice Agent并运行 Generic Assistant 示例。你会得到一个级联流水线,包含 Nemotron Speech ASR、一个 LLM 和 Magpie TTS,外加语音活动检测和话语结束处理,这样你的角色就知道何时该说话。
  2. 让 Bolt E4B 充当大脑。Voice Agent 包含Bolt E4B面向云端、服务器和单 GPU 部署的配置文件。你可以先在build.nvidia.com上试用该模型。尝试对话模式以获得简短、适合语音的回复,并对简单交流禁用推理。
  3. 赋予它个性。从一个系统提示和几段相互关联的示例对话开始。把对话状态(例如谁已经说过“请”)保存在你的应用里,而不是让模型去记住它。当提示不够用时,训练一个 LoRA 适配器;NVIDIA NeMo AutoModel为 Bolt E4B 提供了 LoRA 配方。
  4. 给它一副嗓音。Magpie TTS Multilingual 是 Voice Agent 的默认选项。若要自定义角色,Magpie TTS Zeroshot只需一段简短的参考样本即可工作。可以在 build.nvidia.com 上试用,并申请访问权限以便自行运行。[CONFIRM: 访问路径和链接] 请仅使用你已获得授权的声音。
  5. 给它一张脸。 NVIDIA Audio2Face-3D可将语音转换为角色的面部动画。输入干净的语音,之后再添加特效。
  6. 可选:给它一双眼睛。Voice Agent 的 Nemotron Omni Assistant Subagents 示例通过以下模型增加了实时摄像头理解能力:Nemotron 3 Nano Omni。如果你需要人数统计,可以搭配一个轻量级的人员追踪器。

一个缓存响应库,以及为你最常用的台词预先保存的音频,能让哪怕反应很慢的怪物也显得敏捷。事实上,如果你恰好受限于算力,完全可以做一个 Live 版本,只用 Nemotron 3 ASR、一个 Python 对话路由器和全部缓存音频。你可以用 Bolt E4B 和 Magpie 流水线离线生成数百条响应,然后在演出时使用缓存。我打算在互动南瓜灯上就这么做。

资源