FireTable's Log
← 返回
💻 程序员

我做了个端侧二次元 AI 伴侣:小蠢

音频

我做了个端侧二次元 AI 伴侣:小蠢

0:00/0:00
✦ ✦ ✦
没有后端,打开网页就能聊:Project XiaoChun 不过叠个甲:端侧模型受限于浏览器里 WebGPU 的算力,能力有限
接一个更强的云端 LLM 效果会好很多,任何 OpenAI 兼容接口都行(Ollama、LM Studio、vLLM 或云端 API)
notion image

我想试试虚拟人方向

市面上的数字虚拟人,我总觉得可以做得更好
  • 传统的 Live2D 强烈依赖画师,动作也有限,而且我也不会做
  • 很多虚拟人其实是生成好的视频,再换个嘴型
在体验过 Grok 里的 Ani 之后,我觉得 3D 方向的虚拟人不错,后来 Ani 从 Grok 下线,搬去了单独收费的 App
那就自己做一个吧,反正用 VRoid Studio 捏人也简单
一开始我就决定不要后端,模型、听写、动作、记忆全在浏览器里跑,部署就是 Cloudflare 上一个静态站,原因有两个:
  1. 想试试端侧模型到底能做到什么程度
  1. 二次元伴侣这类东西,大家多少有些不想给别人看的需求,涉及隐私,也可能涉及 NSFW 内容,数据留在自己设备上更安心

小蠢能做什么

从你发出一句话到她开口比划,要过三块:想(LLM)、说(TTS)、动(EMAGE)。用语音输入的话,前面再加一块听(STT)
除了 TTS,其他几块都在浏览器里跑,各自待在自己的 Worker 里,不抢渲染线程
notion image

想:WebLLM 跑在 WebGPU 上

  • 理解你说的话,生成她的回复
  • 默认 MiniCPM5 2B,显存不够或在手机上自动降到 Qwen2.5 0.5B
  • 引擎跑在 Web Worker 里,逐 token 流式输出,多轮对话复用 KV Cache,后续轮次几乎不用等
  • 小模型上下文一长就容易复读跑偏,所以短期只带最近 2 轮,长期信息存在 IndexedDB,拼进系统提示词
  • 换成 OpenAI 兼容接口后,WebLLM 就不加载了,能省下 1~2 GB 显存

说:Edge-TTS 流式合成

  • 把回复变成声音,这是全链路唯一要实时联网的一块
  • LLM 边吐字边按标点切句,第 1 句的声音和动作一好就开口,后面的边念边准备
  • 用我自己手写的 WebSocket 客户端,经 Cloudflare Workers 转发
  • 下一句没到时,她会停在上一段动作的最后一个姿势,不会突然站直

动:EMAGE 全身手势

  • 根据声音生成全身动作,让她边说边比划
  • 音频喂给 Worker 里的 EMAGE,每 64 帧推理一个窗口,推完一段送一段
  • 跑在 ONNX Runtime 的 wasm + INT8 上,不走 WebGPU,不跟 LLM 抢显卡
  • 每个窗口接着上一段最后 4 帧往下推,动作是连贯的

听:SenseVoice 端侧听写

  • 把你说的话转成文字,填进输入框
  • 录音按停顿自动断句,每段交给 Worker 里的 SenseVoice Small INT8 识别,再去掉「嗯、啊」这类语气词
  • 模型约 228 MB,首次从 CDN 下载后缓存;支持中英日韩粤,自带标点,不走任何云端识别
另外还有 FootIK、视线跟随、三语界面,以及第一次打开时的新手剧情引导
notion image
新手剧情引导
notion image
想、说、动流程

如何进行动作驱动

小蠢身上的动作来源很多:待机呼吸、思考、EMAGE 说话手势、跳舞打招呼这类临时动作,还有跟着镜头转身的步伐
如果各自直接改骨骼,很快就会互相覆盖、抽搐、闪回 T-pose
所以我做了一套统一的动作融合管线(motion pipeline),所有动作都走这一条路:
  • 统一入口:不管是 URL、二进制还是解析好的动画,都用同一个 playMotion 播放,自动重定向到当前模型的骨骼
  • 单一写入者:主动作按「临时动作 > 说话手势 > 思考 > 待机」选出一个,每一帧只有它能写骨骼
  • 分层:最底层是呼吸、手指放松这些基础姿态,中间是主动作,转身走路只接管腿,不影响上半身的手势
  • 过渡:切换时先给 52 根骨骼拍快照,再用五次平滑曲线淡入淡出 0.75 秒,起止都没有顿挫
  • 约束:最后叠上 FootIK 让脚踩实、视线跟随镜头,再统一提交给模型
  • 兼容:所有动作都按 VRM 1.0 的坐标写,遇到 0.x 模型在提交那一步统一翻转,动作源里不用写任何分支
notion image
说话手势接进这条管线时,还要和声音对齐:
  • 嘴型跟着音量走,第一段手势先压着,等声音响了才放
  • 从思考切到说话时,思考会一直放到 EMAGE 写出第一帧才停,不会先弹回站姿再开始比划

场景和衣服

小蠢的形象是我用 VRoid Studio 捏的,现在有 9 套衣服
notion image
我也试过 Meshy、Tripo 这类 AI 生成模型
但它们处理不好这种比较靓丽的二次元形象,自己想调整也两眼一抹黑,所以我还是更倾向手工做的模型
场景有 3 个:线稿世界、3D「阳光海滩」,以及新手引导用的星尘空间
市面上的换装基本是一套衣服一个完整 VRM,换一次就全量下载一次
小蠢拆成一个 base 加若干增量 vrmaddon,base 只下一次,换装只下差量,整体下载量少了约 65%

更快、更小、性能和多端

我一直想让她更快、更小,尤其是支持嵌入之后,没人愿意为了一个挂件等几百 MB
  • EMAGE:原始 FP32 版本光一个文件就 504 MB;量化成 INT8,再砍掉用不上的脸部分支、剪掉几乎没干活的层,一整套从 191 MB 压到 71.6 MB,效果没变差,推理也从 548 ms 降到 274 ms
  • 首屏:web-llm 改成用到才加载,代码包从 6.95 MB 降到 1.01 MB
  • 渲染:合并后处理,阴影图不再每帧更新,GPU 开销大概省了一半;手机端锁 40 帧
瘦下来之后,她就能跑在更多地方:
  • 桌面版:Tauri 做的,macOS、Windows、Linux 都有,带自动更新,Mac 还能用 Homebrew 装
  • 嵌入:/embed 加 npm 包 @firetable/project-xiaochun,一行 script 就能把她塞进任何网页;宿主页面可以让她念一段文字、直接丢一段音频,或者播指定动作
我的个人站点 firetable.tech 已经接上了
notion image

回顾

09-04 第一个 commit,10-13 发到 v0.1.29,40 天,全部分支 402 个 commit,TS + TSX 大概 5.5 万行
日期
里程碑
说明
09-04
第一个 commit
流式语音 + 端侧记忆
09-05
回到 WebLLM
放弃 transformers.js,适配移动端
09-06
自定义模型
接 OpenAI 兼容接口
09-07
第一版换装
体型调节、部件穿脱
09-12
base + addon
换装只下差量
09-16
兼容 VRM 0.x
老模型也能用
09-18
桌面版
Tauri 透明桌宠 + Homebrew,发布 v0.1.0
09-19
MToon 渲染
二次元着色 + 描边
09-20
自动更新
桌面版自带更新
09-22
语音输入
端侧 SenseVoice 听写
10-02
嵌入 SDK
一行代码嵌进任何网页
10-03
EMAGE 瘦身
191 MB 压到 71.6 MB
10-05
海滩场景
纯 Three.js 的 3D 海滩
10-11
新手剧情引导
沉浸式新手引导,10-13 发 v0.1.29
这既是端侧模型研究的应用原型,也是个我喜欢的产品
做到现在,我也想清楚了小蠢该是什么
我想她应该是一个「小蠢发射器」:只管渲染、说话和动作,记忆怎么优化、好感度怎么算,对这个项目来说都是低优先级
这些已经有更好的生态项目在做,比如 SillyTavern(酒馆)、AIRI
而它们负责灵魂,小蠢负责把她自己活出来

相关资源