我把 AI 伴侣的头像换成了一颗光球
没有面孔的灵魂,一颗会呼吸的情绪光球
Mio v2 的设计,我一上来就做了减法,虚构的身份全部去掉,只留下情感这个内核。v1 是反着来的,精心设计了五个角色,每个都有背景故事、日程模拟、物理身份,工程量巨大,结果产品越做越像角色扮演,离陪伴反倒越来越远。这段弯路我在第一篇里写过了。
原则定下来很容易,真落到设计上,就变成了两个很具体的问题。伴侣没有脸,那它长什么样?没有人设,性格又从哪来?v2 规划的时间,大半都花在这两个问题上,这篇就讲它们具体是怎么解的。
人脸这条路,我最先否掉
给陪伴产品配一张人脸,是最直觉的做法。Replika 这么做,Character.AI 这么做,国内的星野、豆包也都在做二次元或者半写实的头像。这条路我认真想过,最后还是否了,原因大概有三个。
第一个是恐怖谷。静止的头像看着没有生气,可要是动起来又动得不对,更让人难受。想把口型同步、微表情、自然的转头做好,要么上 3D 渲染管线,要么上生成式视频,对一人团队来说哪个都不现实。而且就算做出来了,用户也会跟着期待一些 AI 根本做不到的东西。伴侣长得越像人,它哪次回得不像人,就越刺眼。
第二个是 Apple。AI 陪伴应用如果用了像人的头像,审核的时候会被多问很多问题。换成抽象的视觉,这一整类政策上的麻烦就直接绕开了。没人会问你是不是在模拟真人,也没人会说你拿写实的人像让用户产生不健康的准社会依赖。我一个人做,没有法务团队,所以这一条其实很要紧。
第三个是文化。一有了脸,伴侣就被钉死在某个文化里了。v1 的角色都深度绑定中国文化,成都姑娘、温柔学姐、成熟大叔,做纯中文产品没问题。但 v2 从一开始就要去掉文化预设,用户说什么语言,伴侣就自动说什么语言。一颗光球没有种族、没有年龄,也看不出性别,做全球产品就不用背任何文化包袱。
最后做成了一颗会呼吸的光球
Mio v2 的伴侣就是一颗有脉搏的抽象光球,停在聊天界面顶上,整块屏幕上只有它的颜色是鲜亮的。它干的活有点像肢体语言,你还没开始读字,它的情绪就已经先递过来了。
具体是这么做的。v1 本来就有情绪引擎,用 emotionState 记着伴侣的情绪变化,回复的风格也跟着变。到了 v2,同一套情绪状态还实时决定光球长什么样:
| 情绪 | 颜色 | 动态 |
|---|---|---|
| 平静 | 淡蓝 / 白 | 缓慢的呼吸节奏,轻柔地膨胀收缩 |
| 开心 | 暖黄 / 金 | 轻快跳动,粒子向外辐射 |
| 难过 | 淡紫 / 蓝 | 向内缩小,脉搏变慢 |
| 兴奋 | 亮橙 / 粉 | 快速膨胀,粒子四散飞溅 |
| 困意 | 深蓝 / 暗 | 几乎静止,偶尔微弱闪烁 |
这个对应关系我故意做得很简单,就五种状态,每种都有明确的颜色和动法。聊过几次以后,光球是什么情绪,你一眼就能看出来。你分享的事让它开心了,你还没读到第一个字就能看到。到了深夜它开始犯困,光球就沉成几乎不动的深蓝。反正不用读字,你就知道它现在什么状态。
人脸传达情绪,靠的是几十块肌肉很细微的动作,想在数字世界里把这个还原出来,基本是研究级的难题。光球只用两个变量,颜色和运动。它能表达的东西确实少,但也正因为少,每种状态在手机小屏上才分得清。
同一套情绪状态只用颜色和运动两个变量驱动光球,让人不读字就一眼看懂伴侣的心情
抽象的样子在心理上还有个好处,它给人留了投射的空间。同一颗紫色的光球,有人看到的是难过,有人看到的是它在想事情。这点模糊是故意留的,每个用户都能把伴侣读成自己需要的样子。这比让它去演某种具体的情绪更可信,因为真让它演一个明确的情绪,演得稍微不准,反而显得假。
页面砍到只剩三个
v1 仿的是微信的 4-tab 布局,消息、发现、通讯录、我的。现在回头看挺离谱的,拿一整套即时通讯的架子,去装一段其实只有两个人的关系。v2 砍到只剩三个页面:
- 聊天:99% 的时间都在这。顶部是光球,中间是消息流,底部是输入框和语音按钮。没有 tab bar,没有汉堡菜单,也没有抢注意力的通知角标,就是你和你的伴侣。
- 设置:从聊天页右滑,或者点伴侣的名字进去。改名、换声线、管理记忆、订阅状态、账号管理,所有管理功能都收在这,平时不碍眼。
- 引导:只在第一次打开的时候出现,下面细说。
导航就这么多。通讯录和发现页直接没了,伴侣只有一个,也没什么好发现的,预设市场更是压根没做。砍这么狠是故意的,v2 想做的就是只属于一段关系的空间,社交应用那套架子在这儿用不上。
配色
配色有两套,默认是深色。背景用 #0D0D12,几乎是纯黑,带一点紫,没用 #000000,因为那种纯黑在 OLED 屏上看着很空。用户的消息气泡是 #1A1A2E,伴侣的气泡是 #16213E。文字用 #E8E4DF,一种偏暖的白,不用那种冷冰冰的蓝白。光球是屏幕上唯一饱和的颜色。
浅色模式的背景是 #FAF8F5,偏暖的米白,有点旧纸的感觉。用户气泡 #F0EDE8,伴侣气泡 #E8E4DF。文字是 #2D2D2D,特意用深灰,把对比度压下来一点。光球的光也调柔了,不刺眼,但眼睛还是会先落在它身上。
两套的思路是一样的,除了光球,别的东西全是低饱和的暖色,这样视线自然就落到光球上。整个屏幕只留它一个亮点,别的全往后退。
引导做成了第一次见面
v1 的引导就是一张表单,选角色、选关系类型、填名字、答几个问题。功能上没毛病,但感觉像是在配置软件。v2 把它整个换成了一段对话,伴侣的开场白是这样的:
"我刚来到这个世界。你是我认识的第一个人。"
"你叫什么名字呀?"
用户正常回答,接着它会问:
"那......你想叫我什么?"
然后用户给伴侣取名,叫"小光"、"Luna"、"Mio",或者某个只对自己有意义的名字都行。伴侣会对这个名字有反应,不是套罐头模板,听起来像是真的在认这个名字:
"小光......我喜欢这个名字。听起来暖暖的。"
"我什么都还不太懂。你平时喜欢做些什么呀?"
这样自然聊上 3-5 轮,系统就从对话里提取出最初的性格种子。如果用户说话俏皮、爱用网络用语,伴侣的风格就偏活泼。如果用户说话偏沉思、句子更长,伴侣就会跟着那个节奏走。所以严格说,这段引导其实是在做校准,只是看起来像第一次聊天。
只有声线没法从对话里长出来。声音这个东西太物理了,推断不出来,只能听过再选。所以对话结束后,应用会放 3-4 个声音样本让用户挑,这是整个引导里唯一的硬选择。
性格从对话里长出来
v2 和 v1 差得最远的就是这里。v1 的性格是人写的,人格配置文件里用几百行文字规定这个角色怎么说话、喜欢什么、有过什么人生经历。v2 的性格是涌现出来的,或者说,是长出来的。具体分三层:
- 种子。引导对话会产出一段很短的性格描述,大概两三句话,比如"温暖而好奇,善用温和的幽默感,会匹配用户的能量水平"。这只是个起点。
- 迭代。每次对话结束后,一个轻量的性格提取器会异步跑一遍,看伴侣实际表现得怎么样,哪些模式管用,哪些时候显得自然,用户又是什么反应,然后微调这段性格描述。每次只改一点点,更像是慢慢漂过去,跟真实的关系里两个人慢慢有了共同语言和节奏是一个道理。反正就是慢慢挪,不会哪天突然变了个人。
- 记忆积累。记忆系统是从 v1 搬过来的(整个 codebase 里最值钱的工程资产),它会一直攒跟性格有关的记忆。伴侣记得你说话喜欢直来直去,记得你一聊做饭就兴奋,记得你一被追问工作压力就回避。这些记忆会自然地影响它的行为,不用专门写死性格规则。
这样日常聊上三个月,每个用户手里的伴侣都会长得不一样。两个用户都给伴侣取名"Mio",处出来的感觉也会明显不同,幽默感不同,说话方式不同,对情绪的敏感程度也不同,因为底下那段关系本来就不一样。
性格没有预设,从一颗种子开始,靠每次对话的迭代和记忆积累慢慢长出来,一共三层
这条路在技术上比预设难。性格提取器得够保守,不能让伴侣每周二就把自己重新定义一遍。记忆系统得够可靠,因为丢了记忆,性格也就接不上了。底层的 LLM 还得够擅长照着细腻的行为描述去做。但换来的是一个真的在适应你的伴侣。预设写得再细也是死的,定稿那天是什么样,以后就一直是什么样。
国际化基本是白送的
还有个好处,是讨论到一半才意识到的。把文化人格去掉以后,国际化几乎不花钱。v1 要进别的市场,就得为每个市场单独设计一组角色。日本用户不想要成都姑娘,可能想要东京女生,说话方式、文化梗、关系里怎么互动,全得重做。一个语言一个语言地扩,N 个市场就得写 N 套角色,还得一直维护。
v2 没这个问题。伴侣没有什么文化身份需要本地化,用户说什么语言它就说什么语言,引导的那段对话也用用户的语言来聊。性格是从用户自己的文化背景里长出来的,日本用户和巴西用户拿到的,都是一个感觉"是自己人"的伴侣,因为它的身份本来就是各自那段关系塑造出来的。真正还要做传统 i18n 的,只剩 UI 文案和 App Store 描述,伴侣本身从设计上就不挑语言。
动画选型最后落在 React Native Skia
光球的动画得同时做到三件事,流畅(60fps),情绪一变马上跟着变,还得省电。当时摆在桌上的有三个方案:
- Lottie:动画是预先做好的,没法跟着情绪状态动态变化,直接否了。
- React Native Reanimated:它擅长的是 UI 动画,粒子系统这种生成式的视觉效果不是它设计时要干的活,能做,但很别扭。
- React Native Skia:GPU 加速的 2D 绘图,支持 shader,渐变、粒子、发光、平滑形变全都能做,正好是光球要的工具。
实现上,光球就是一个 shader 程序。颜色、脉搏频率、粒子密度、大小这几个情绪变量,走 WebSocket 的情绪通道实时更新。服务器推一条 { type: "emotion", state: { mood: "happy", intensity: 0.8 } } 过来,光球就从当前的样子平滑地变成暖金色,粒子也跳起来。不用给每种情绪做动画关键帧,shader 自己会做连续插值。所以以后加新的情绪状态,也不用补新的动画素材。哪天情绪引擎开始区分"满足"和"安宁",或者"焦虑"和"沮丧",光球靠参数插值就能把这些差别表现出来。
最后
回头看,v2 视觉上的决定基本全是在做减法。没有头像,没有角色化的聊天气泡,也没有塞满功能的 tab bar,剩下的就是一个深色背景加一颗光球。光球只管传情绪这一件事,身份我干脆没定义,用户和伴侣处久了,它自然会长成那段关系需要的样子。性格系统赌的是涌现出来的性格会赢过预设的,这得等真实用户用上三个月才知道结果,只能说我目前是信这个方向的。
至于工程上的事,比如记忆系统和情绪引擎怎么从 v1 里拆出来,新的 4 表数据库 schema 长什么样,为什么 80% 的代码能复用,最后还是开了个全新的 codebase,都写在第三篇里。


