本文是「数字伴侣建造记」的第五篇。项目进行到中段时,上游开源项目对系统的贡献只剩一层 LLM 转发——记忆被我们改写、语音被我们重建、界面被我们重写。于是做了最大的一次手术:自研对话引擎,让系统完全独立。本文记录大脑的设计,以及上线后与“口头禅复读”的一场持久战。
为什么抛弃 session
聊天工具的多会话模型来自任务场景:每个任务开一个会话,做完归档。伴侣不是任务——她是一段连续的关系,“新建会话”这个动作本身违背语义。上游项目的会话切换功能常年带着刷新才生效的顽疾,与其修它,不如承认这个概念在此场景不该存在。
替代方案是一条永续的时间线 + 四层记忆:
- 近期窗口:最近约 30 条原文,负责眼下的连贯。
- 滚动摘要:超出窗口的部分由模型折叠成概要,按内容语境分两份(见系列第三篇的隔离设计),各自滚动。
- 长期记忆便签:蒸馏出的稳定事实(习惯、纪念日、约定),纯文本文件,人可直接编辑。
- 全量档案 + 检索:每句话永久落盘;每次用户发言触发一次轻量相关性检索(字符二元组重叠打分),把最相关的旧对话作为“回忆”垫进上下文——提上个月的事,她能接上。
迁移的验收标准是原有的 22 项内容隔离测试原样通过。
口头禅战争
上线数日后出现了这个项目最有教学价值的故障。
症状:她的某句语气词开始出现在每条回复的结尾。统计确认:近期窗口内二十条她的发言中十一条含同一短语;更糟的是,滚动摘要里赫然写着“她说话时常夹杂××”——一时的语癖已被摘要器固化为长期人格。
病理分三层,缺一不可:
- 起点是一次完全正常的生成——那句语气词在当时的语境里很自然。
- 自增强:它进入近期窗口后成为示范;语言模型天然模仿自己的历史文本,第二次出现的概率高于第一次,随后滚雪球。非推理型模型(单次前向、无自查步骤)对此尤其敏感。
- 固化:摘要器如实记录了“她常说××”,从此这句成为每次生成都会读到的长期记忆——窗口会被新对话冲刷,摘要不会。闭环完成。
第一次反击失败了:在系统提示中加入“不要重复使用相同短语”的指令,收效甚微。结论值得加粗:说明书打不过例子——十几条在场的示范文本,权重远高于一行抽象指令。
第二次反击是机制化的,四件套:
- 摘要手术:人工删除已固化的口头禅记载(事实全保留)。
- 摘要器疫苗:折叠指令中永久加入“只记录事实与情绪,不把具体口头禅或句式写进概要”——切断未来的固化通道。
- 生成层守卫:反复读指令保留(它在干净窗口里是有效的,只是打不赢污染窗口)。
- 翻篇:新增一个可随时执行的动作——把当前未摘要的对话全部折叠进概要,然后把上下文切点移到现在。切点之前的内容仍在档案里(检索照常命中),但不再进入对话窗口。事实存活,腔调死亡。这是对“窗口已饱和污染”的唯一有效解,做成了控制台上的一个按钮。
执行翻篇后复读立止。此病无法根除(这是自回归模型的天性),但从此可管理。
快慢双模型
最后一块设计来自一个观察:系统里的 LLM 调用按延迟敏感度天然分成两类。
| 任务 | 延迟敏感度 | 模型选择 |
|---|---|---|
| 对话回复 | 极高(用户在等她开口) | 非推理型快模型 |
| 摘要折叠 / 翻篇 | 零(后台执行,无人等待) | 旗舰推理模型 |
后台任务的调用频率极低(每几十条对话折叠一次),旗舰模型的成本摊下来每月不足一元,换来的是更高质量的记忆压缩——而记忆质量会反哺每一次对话。对话侧则坚持快模型:等未来的流式合成管线上线后,首句生成速度直接决定她的开口延迟,届时这个选择的价值更大。
可复用的原则
- 概念要匹配场景:任务用会话,关系用时间线。
- 语言模型的风格病要用数据机制治(清洗、切断、免疫),不能只靠指令劝。
- 摘要器是记忆的守门人,它写下的每个“特征描述”都会变成自我实现的预言——约束它只记事实。
- 按延迟敏感度给模型分工,旗舰智力可以出现在用户永远等不到的地方。