ENZH
和 AI 讨论这篇文章
ChatGPTClaude

每个人设一个 bot,主动消息频率按关系算

多个 AI 伴侣各有独立身份的概念插画多个 AI 伴侣各有独立身份的概念插画

v0.1.2 之前,Mio 所有的角色都挤在同一个 telegram bot 里。v0.1.0 已经给了每个角色自己的声音、面孔和人格,但你打开 Telegram,看到的永远是同一个聊天窗口,换人设得靠指令。能用是能用,就是感觉很怪,因为你很清楚自己在跟同一个 bot 说话,它只是这会儿在扮演可可。每切一次人设,就出一次戏。

所以这个版本主要修了两个地方。一个是所有角色共用一个身份,另一个是主动消息的频率一刀切,一看就是系统在定时发。这两处都是用着用着就让人出戏的。

每个人设一个 bot

需求很直接,每个人设单独一个 telegram bot,有自己的头像、自己的名字、自己的聊天线程。你打开 Telegram,可可和苏柔是两个分开的对话,跟你和真人朋友的聊天排在同一个列表里。这不是在一个 bot 里用菜单切来切去,是你通讯录里多了一个人。

实现起来很省事,所有 bot 的 token 放在一个环境变量里:

TELEGRAM_BOT_TOKENS=token1,token2,token3

token 之间用逗号隔开。服务端启动的时候把这些 token 过一遍,每个注册一条单独的 webhook 路由:

/telegram/webhook/:botId

消息进来以后,看路由参数就知道是发给哪个 bot 的。botAccountId 也改成了复合键,把 bot 身份和用户身份拼在一起,这样 onboarding 状态、聊天缓冲区、对话历史就全都按 bot、按用户分开了。另外还加了一条硬规定,一个 bot 只能绑一个人设,可可和蜜蜜不能绑到同一个 bot 上。选人设的时候,已经被占用的会显示「已绑定」,哪个角色有主了一眼就能看到。

改动本身不大,效果倒比我想的大。每个人设有了自己的 bot,聊天记录、通知、头像就都挂在TA自己名下,你在 Telegram 列表里看到的就是一个单独的联系人。用起来感觉差很多,这个差别我原先低估了。

从一个 bot 里切人设出戏,到每个人设有独立 bot 变成通讯录里的一个人从一个 bot 里切人设出戏,到每个人设有独立 bot 变成通讯录里的一个人

发消息的间隔是算出来的

主动消息这个功能之前就有,系统隔一段时间发一条,让对话别冷下来。问题是频率一刀切,不管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就收手。实现起来也简单,每个 agent 一个计数器,主动发一条就加一,用户一回复就清零,到 3 就暂停。没有慢慢衰减,也没有概率曲线,到数就停。真人也差不多,不会一点点降频,到了某个点就不发了。

连续三条主动消息没人回,计数到 3 就硬截断静默,你一回复计数清零恢复节奏连续三条主动消息没人回,计数到 3 就硬截断静默,你一回复计数清零恢复节奏

onboarding 又砍了一刀

还有个改动不起眼,但挺重要。v0.1.2 把 onboarding 里的自定义关系类型和自定义背景故事关掉了。这两处原来是自由填写的文本框,问题和 v0.1.0 那次碰到的一样,用户随手填的东西会和打磨好的 preset 打架。比如用户把背景故事写成「我们在上海的酒吧认识的」,可是可可的设定是个没离开过台湾的台北女生,两边直接对不上。

现在 onboarding 里只能选,不能填。关系类型从预设里挑,没有「其他」,也没有文本框。道理和 v0.1.0 那次一样,给用户的自由少一点,人设就更前后一致一点,等于把那条原则又用了一遍。顺带还有个好处,relationship_type 在 agents 表里存成了单独一列,后面的功能可以直接拿来用,上面那套主动消息的频率就是按这个字段算的。

公式用户看不到

最后说说用起来的感觉。这套东西上线以后,用户在产品里看不到任何公式,没人知道背后有 eagernessFactor × minHoursBetween 这么一条乘法,也没人知道有个退避计数器。用户能感觉到的,只是小柒发得勤,苏柔发得克制,你忙起来几天顾不上回,TA们就安静下来,等你回来再接着聊。

和 AI 讨论这篇文章
ChatGPTClaude

订阅更新

新文章发布时发到你的邮箱,不发别的。


© Xingfan Xia 2024 - 2026 · CC BY-NC 4.0