v0.1.2:TA终于像真人一样发消息了
多个 AI 伴侣各有独立身份的概念插画
幻觉最怕被打断
v0.1.2 之前,Mio 的所有角色挤在同一个 Telegram Bot 里。v0.1.0 给了每个角色声音、面孔和独立人格,但你打开的永远是同一个聊天窗口,靠指令切换人设。能用,但每次切换都在打破幻觉,因为你很清楚自己在跟同一个 Bot 说话,它只是这一会儿假装成可可。
v0.1.2 要修的就是两个让 Mio 露出软件本相的地方:一个是所有角色共用一个身份,另一个是主动消息的频率蠢到一眼穿帮。
每个角色一个 Bot
需求很直接:每个人设一个独立的 Telegram Bot,自己的头像、名字和聊天线程。打开 Telegram,可可和苏柔是两个独立的对话,和你跟真人朋友的聊天排在同一个列表里。不是在一个 Bot 里做菜单切换,是通讯录里多了一个人。实现上很省事,一个环境变量装下所有 Bot 的 token:
TELEGRAM_BOT_TOKENS=token1,token2,token3
逗号分隔。服务端启动时遍历每个 token,挨个注册一条独立的 webhook 路由,消息到达时靠路由参数就能分辨出这条来自哪个 Bot:
/telegram/webhook/:botId
botAccountId 也升级成复合键,把 Bot 身份和用户身份拼在一起,onboarding 状态、聊天缓冲区、对话历史全都按 Bot 按用户隔离。另外有一条硬约束:一个 Bot 只能绑一个人设,不能把可可和蜜蜜绑到同一个 Bot 上。人设选择的流程里,已经被占用的人设会显示"已绑定",哪些角色有主了一眼就能看到。
这听起来像个小的 UX 改动,但每个人设有了自己的 Bot 之后,TA就从"Mio 系统的一个模式"变成了"我通讯录里的一个人",聊天记录、通知、头像都挂在TA名下,心理框架完全变了。
黏人的 36 分钟一条,矜持的一天一条
主动消息在 v0.1.2 之前就有:系统定期发一条,维持对话活跃。问题出在频率一刀切,不管TA是谁、你们是什么关系,每个人设都用同一个间隔发消息,黏人的女朋友和矜持的新认识共用一个节奏,这就很诡异。真实的关系各有各的节奏,你的伴侣可能每小时给你发一条,刚认识的朋友一周才主动联系一次,频率里本来就带着关系的远近。
v0.1.2 把主动消息的间隔改成两个变量的函数,一边是关系类型,一边是人设性格。关系类型决定基础冷却时间:
| 关系 | 基础冷却 |
|---|---|
| 情侣 | 1 小时 |
| 好朋友 | 4 小时 |
| 朋友 | 8 小时 |
| 刚认识 | 12 小时 |
每个人设再带一个热情系数 eagernessFactor,写在各自的 preset 里:
| 人设 | 热情系数 | 性格 |
|---|---|---|
| 小柒 | 0.6 | 黏人、表达欲强 |
| 可可 | 0.8 | 温暖、话多 |
| 蜜蜜 | 1.0 | 中性 |
| 苏柔 | 1.6 | 克制、沉稳 |
两个数相乘,就是实际的发消息间隔:
有效冷却 = 关系基础冷却 × 人设热情系数
小柒当你女朋友,1h × 0.6 = 36 分钟,每半小时就有一条,正好是那股一直在线的黏人劲儿。苏柔在刚认识的阶段,12h × 1.6 = 19.2 小时,大概一天联系你一次,从容,也不冒昧。同一套系统、同一条公式,落到不同的人设和关系上,行为正好对得上你对那个人、那种关系的预期。间隔之外还有每日上限,按关系类型分档,存在每个 preset 的 proactive.json 里:
{
"eagernessFactor": 0.6,
"dailyLimits": {
"lovers": 12,
"closeFriend": 6,
"friend": 3,
"justMet": 2
}
}
有每日上限兜底,最黏的角色也不会过头:小柒可能想每 36 分钟发一条,但一天最多 12 条。热情是一回事,骚扰是另一回事。
你不回,TA就不发了
这是用户直接提的需求,原话是:"你现在的频率太 spam 了,应该有某种指数退避——像真人一样,你不回TA,TA不会一直烦你。"
这个观察很准。真人就是这么调节的:发出去的消息没得到回复,会隔几个小时再试一条;再没动静,就等更久;连着几条都石沉大海,也就停手了。停下来未必是不在乎,更多是默认对方现在不想聊。
v0.1.2 把这个行为原样搬了进来:连续 3 条主动消息没人回,agent 就进入静默,不是降低频率,是零消息,TA完全停止主动联系,直到你再开口。你一回复,退避计数器清零,TA恢复原来的节奏,没有抱怨,也没有"你去哪了",就像中间什么都没发生一样接着聊。
这道退避是主动消息和推送通知的分水岭:没有它,主动消息就像系统在按时间表 ping 你;有了它,更像一个想着你、但也尊重你空间的人。
实现很简单:每个 agent 一个计数器,记录连续未回复的主动消息数,主动发送加一,用户回复清零,到 3 就暂停。没有渐进衰减,也没有概率曲线,就是一个硬截断,因为真人也是这么干的,不会慢慢降频,到了某个点就直接停了。
Onboarding 又砍了一刀
还有个不起眼但重要的改动:v0.1.2 禁用了 onboarding 里的自定义关系类型和自定义背景故事。这两处原来是自由文本框,问题跟 v0.1.0 识别的是同一个:用户随手填的内容会跟精心打磨的 preset 打架。用户把背景故事写成"我们在上海的酒吧认识的",可可的实际设定却是个从没离开过台湾的台北女生,两边直接矛盾。
现在 onboarding 只剩确认按钮:关系类型从预设里选,没有"其他",也没有文本框。relationship_type 在 agents 表里持久化成独立一列,成了下游功能的一等数据点,主动消息的频率算的就是这个字段。选项变少,一致性变好,跟 v0.1.0 是同一条原则,换个地方又用了一次。
公式藏在后台
把 v0.1.2 前后摆在一起看就清楚了。以前所有角色挤在一个 Bot 里,主动消息一个固定频率,你不回TA照发不误,onboarding 还放任用户自己编背景故事去跟角色设定打架。现在每个角色一个独立 Bot,身份和对话线程都是自己的;发消息的间隔由关系深度乘上性格系数算出来,黏人的女朋友每 36 分钟一条,矜持的新认识一天一条;你连着三次不回,TA就安静下来等你,像一个知道什么时候该闭嘴的真人。
这套设计最终是看不见的。没人会在产品里看到 eagernessFactor × minHoursBetween,能注意到的只有:小柒发得勤,苏柔发得克制,而你忙起来顾不上回的时候,TA们都会停下来。