和 AI 聊到第 20 轮,它忘了我是谁

5 分钟
AIBACKENDDESIGNREVIEW
和 AI 聊到第 20 轮,它忘了我是谁

做客服 AI Agent 的人,大概都见过这种工单:用户前面聊得好好的,到第十五六轮,AI 突然「断片」——忘了对方刚说过什么,把同样的问题从头又问一遍。用户气得截图发群:「这 AI 是金鱼吗?」

不是金鱼。LLM 没有会话级的持久记忆。

目录

大模型对话的本质:每次只读取拼装好的聊天清单

大模型每次回答,拿到的只是一份临时拼好的聊天清单:system 负责定身份、立规矩,user 是用户说的每句话,assistant 是它上一轮的回答,三者按时间顺序排成一列。它读完这份清单完成推理输出,本轮会话上下文就和模型实例解绑。下一轮,产品必须把会话历史重新拼装一遍,再喂给模型。

所以「记忆」从来不是模型原生长出来的,是上层产品平台替它保管的。清单拼得完整,它就像什么都记得;拼漏了一段,或者消息顺序错乱,它就直接「失忆」。

客服场景里莫名其妙的对话脱节,十有八九不是模型推理变笨,而是上下文拼装环节把某段历史弄丢、顺序错乱,或是工具调用的消息被过滤丢失。

会话清单存在上限:上下文窗口会被 token 占满

这份会话清单不是无限长的,受模型上下文窗口硬约束:8K、32K、128K,再大也有上限。每一条历史消息都会消耗 token,对话轮次变多,清单持续膨胀,迟早会触达窗口上限。这时产品必须做出取舍:丢什么,留什么?

一个方案是直接截断,舍弃最早的对话,只保留最近 N 轮消息。实现简单,但第一轮用户提出的核心诉求这类关键信息很容易直接丢失。

另一个方案是历史摘要压缩:让模型把大段旧对话浓缩成简短的纪要,保留关键事实、用户诉求、已达成结论,用摘要替换掉大批量原始消息。这不是粗暴删除,是信息提炼。ConversationSummaryBufferMemory 做的就是这件事——最近若干轮保留原始消息,更早的会话内容摘要化。

摘要到底放哪里合适?

一种做法是把摘要拼接进 system prompt。摘要权重高,每轮都会被读到,但 system 本应承担角色与规则设定,混入大量历史摘要会不断膨胀,分散模型注意力,也容易对后续输出产生不必要的全局干扰。这是很多业务侧的落地实践,但并不是 LangChain 的默认行为。

另一种做法是把摘要伪装成一条 user 消息,放在消息列表最头部,保持 system 干净,摘要也可以独立增删。代价是消息流会出现连续两条 user 角色消息,多数大模型对同角色连续消息鲁棒性较差,容易干扰对用户本轮真实意图的识别。

两种方案没有绝对标准答案,需要结合业务场景权衡。

记住你,从来不是模型的义务

说到底,上下文不是靠模型能力解决的问题,而是产品架构问题。更大的上下文窗口只是提升承载容量,不会自动帮模型做记忆取舍。

模型天生不自带会话记忆,记住什么、遗忘什么,全部依赖上层应用的设计:短期记忆维护当前会话事实;长期记忆把用户偏好、历史档案存入外部存储,还需要业务主动检索召回、注入上下文清单,模型才「看得见」。

下一次再遇到 AI「断片」,先别急着怪模型笨——回头看看那份拼装给它的聊天清单,是不是在哪个环节,悄悄少了一页。