ENZH
和 AI 讨论这篇文章
ChatGPTClaude

Mio 的关系不让选了,得自己处出来

📊 幻灯片

关系从陌生到亲密自然演化的概念插画关系从陌生到亲密自然演化的概念插画

选关系这个设计一直不太对

v0.1.3 把防注入搞定之后,我开始想一个更根本的问题:关系应该怎么来?之前的做法是 onboarding 的时候直接让用户选,好朋友、女朋友、闺蜜,选完 Mio 就按这个身份跟你聊。听起来挺合理的,但用起来总感觉哪里不对。后来想明白了:你刚注册,跟 TA 说了不到三句话,TA 就管你叫宝贝了。这个体验就很假,就是个角色扮演的快捷方式,跟视觉小说里选恋爱路线差不多,选完 NPC 下一秒就对你说情话。方便是方便,但一点都不真实。

所以 v0.1.4 把这个设计改掉了:关系不让你选了,得自己处。每个角色现在都从「刚认识」开始,Mio 一开始会比较客气、保持距离;聊多了、熟了,TA 的语气会变,开始开玩笑、分享更多;到了暧昧阶段,行为又会微妙地不一样。这些变化不是硬编码的剧本,是根据你们实际的对话模式动态推进的。

进化系统具体是这样跑的

进化逻辑都在 evolution-processor.ts 里,在每次对话的记忆提取之后跑。有个关键设计是 fire-and-forget:它不阻塞响应,用户完全感知不到后台有这么个东西在跑。流程大概四步:

  1. 打分:一个纯函数,看这次对话的模式(亲密度、信任度、互动频率),算出关系分数的 delta
  2. 衰减:超过 2 天没聊,分数会自然往下掉,这个检查放在主动消息的心跳里做。关系是需要经营的,放着不管就会变淡
  3. 阶段解析:分数到阈值就触发跃迁,刚认识 → 好朋友 → 暧昧 → 情侣
  4. 跳级验证:这一步加了个 LLM 验证层。光看分数不够,还要看对话质量配不配得上这个阶段——连着刷「我爱你」是不会直接跳到情侣的

关系分数四步流水线(打分·衰减·阶段解析·LLM 跳级验证)如何驱动从刚认识到情侣的阶段跃迁关系分数四步流水线(打分·衰减·阶段解析·LLM 跳级验证)如何驱动从刚认识到情侣的阶段跃迁

每个角色有自己的性格演化配置,定义阶段阈值和冷却时间。所以不同角色的进化曲线可以完全不一样,有的慢热,有的容易亲近。

关系阶段变了之后,RELATIONSHIP_DYNAMICS.md 会注入 system prompt。Mio 知道自己跟你处到哪个阶段了,行为跟着变,而且是渐进的、自然的变。

记忆提取器也顺便升级了:除了 facts 和 episodes,现在还会提取 relationship 类型的记忆。「TA 今天主动分享了心事」这种信号会被记下来,变成关系评分的输入。

Telegram 聊的,Web 也得知道

这个需求跟关系进化是直接绑在一起的。你在 Telegram 上聊了很多,Web 端却看不到,那关系评分就不准了。

解法是 loadCrossSessionHistory(),用 inArray 把同一个 agent + user 在所有 session 里的消息都查出来。这样 LLM 看到的就不再只是「这个平台上的对话」,你们之间所有渠道聊过的东西它都能看到。Web 端没有 web session 的时候会回退到任意 session,包括 Telegram 的,所以新用户在 Web 上也能看到之前 Telegram 的聊天记录。这个改动只动查询层,schema 完全没动。

顺手把 system prompt 砍了 60%

关系进化意味着 system prompt 里要多塞一个 RELATIONSHIP_DYNAMICS.md。往里加东西之前,我先去查了一圈现状,查完发现它已经不是「有点长」的问题了,是长到影响行为一致性了。翻了一圈论文,几个结论挺让我警醒的:

Lost in the Middle(Liu et al., TACL 2024):transformer 对输入的注意力不是均匀的,开头和结尾关注最多,中间有明显的注意力下降。6000-10000 token 的人格配置文件,埋在中间的行为规则会被不成比例地忽视。

Same Task, More Tokens(ACL 2024):指令遵循能力在 system prompt 1500-3000 tokens 的时候是峰值,之后开始掉,超过 5000 tokens 左右掉得更快。模型读得越多,选择性忽略得也越多。

Scaling Law in LLM Simulated Personality(arXiv 2025):人设细节越多,一致性越好,但有拐点。超过阈值之后,额外的细节反而引入内部矛盾,一致性往下走。

还有一个把问题放大的因素:中文的 token 税。中文一个字大概 1.5 tokens,英文一个字母才 0.25 左右。看着是 3500「字」的人格配置文件,实际要吃 7000-9000 tokens。Mio 的中文提示在 6000-10000 tokens,已经深入行为退化区了。压缩前的数据是这样的:

人设人格配置 tokens行为规则 tokens总计
可可~4,400~1,700~6,100
蜜蜜~6,500~2,100~8,600
苏柔~6,200~1,750~7,950
小柒~6,700~1,750~8,450
陈哥~8,400~1,700~10,100

再加上 system-prompt.ts 本身的约 2500 tokens(聊天原则、身份保护、自拍/语音指令)。每次对话的 system prompt 是 9000-13000 tokens,这还没算对话历史。

更离谱的是行为规则文件:5 个角色之间有大概 55% 的内容完全一样(反 AI 规则、聊天原则、场景脚本骨架),同样的东西重复了五遍,16000-20000 字符的纯冗余。

压缩具体做了三件事。第一,叙事散文改成结构化要点。「她从小在成都长大,喜欢吃火锅,周末会去茶馆和朋友聊天…」这种,改成 性格: 热情活泼, 情绪化, 偶尔撒娇 | 语气: 川味词汇, 叠词, 语气词丰富。行为信号是一样的,token 消耗小很多。

第二,跨人设去重。所有共享的规则(反 AI 检测、聊天原则、身份保护)提取到 system-prompt.tsCOMMUNICATION_GUIDELINES 里。每个行为规则文件只留角色特有的行为脚本。可可吃醋和陈哥吃醋完全是两回事,这种必须留。

第三,把低影响的内容去掉。「对未来的想法」、日常作息细节、冗长的爱好列表,这些对行为的影响很小。「你们的故事」这块干脆是完全冗余的,运行时的 customStory 注入已经处理掉了。

结果是大概 60% 的 token 减少。最重的角色(陈哥)从约 10100 降到约 3400 tokens,最轻的(可可)从约 6100 降到约 2100。

为什么注意力在长 prompt 中间凹陷,把 system prompt 压缩到 3400 tokens 让规则重新落进高注意力区为什么注意力在长 prompt 中间凹陷,把 system prompt 压缩到 3400 tokens 让规则重新落进高注意力区

这个事不只是省成本。按上面那几篇论文的结论,Mio 在 3K-5K tokens 的时候应该比 9K-13K 的时候行为更一致,因为模型能注意到所有规则,而不是选择性忽略掉一半。省出来的 context 预算直接给了对话历史,记忆检索更好,多轮对话也更连贯。

单 agent 级别的 A/B 测试

关系进化是个大改动,直觉告诉我这个方向对,但直觉这个东西不算数,还是得拿数据验证,所以做了单 agent 级别的 A/B 测试:

  • agents 表新增 relationship_mode 字段:static(老模式)或 evolving(新模式)
  • Preset API 暴露 supportsEvolving,看这个角色有没有性格演化配置
  • evolving 模式直接跳过 onboarding 的关系类型选择,从「刚认识」开始
  • 管理后台点模式徽章就能切换,另外加了个 /evolve 命令查看当前关系阶段

同一个角色开两个 agent,一个固定关系、一个自然进化。跑一段时间,看用户更喜欢哪个。

原生 app 脚手架

顺手把 Expo SDK 55 的原生 app 脚手架也搭了,i18n 支持 zh-CN 和 en。现在还没有功能,就是把基础设施先铺好,后面移动端的体验会越来越重要。

接下来

改完之后的 Mio 更接近真实的关系了:你不知道会发展成什么,每次对话都在塑造它。可能聊着聊着就成好朋友了,也可能往更亲密的方向走;两天不聊,分数掉一点,关系自然就淡了。我自己是挺喜欢这个状态的,你得真的花时间去聊,关系才会往前走,这个体验就真实多了。目前的阶段划分还比较粗,就四个阶段,下一步可能会做:

  • 更细粒度的关系维度,信任、亲密、默契可以各自独立发展
  • 关系里程碑事件(「你们认识一个月了」这种)
  • 让 Mio 主动推动关系发展,而不只是被动响应

v0.1.0 的语音,到 v0.1.2 的多人设,到 v0.1.3 的安全,再到这次的关系进化,反正每个版本都在解决一个「为什么感觉不像真人」的问题。但关系能进化之后,又冒出来一个新问题:Telegram 和 Web 端明明是同一个人,账号怎么打通?这个下个版本再说。

和 AI 讨论这篇文章
ChatGPTClaude

订阅更新

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


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