
首先!大模型本身是没有记忆的!
每一轮对话其实都是独立的,大模型不会保留对话的状态
比如 这样的对话


好的,查到价格是0.969
可以买一万三百股


好家伙,这刚说完就忘记了
那为什么现在的 大模型 都有记忆功能?
因为程序在底层加入了一个记忆功能!!
具体就是
你用的程序在后台默默把
“之前的历史聊天消息 + 最新提问”
重新拼接打包到一起,完整地发送给大模型
比如说变成这样
[历史消息]
用户:帮我查下半导体ETF的价格,
我要买一万
回答:好的,查到价格是0.969
可以买差不多一万股
我要再买10万!


通过这种方式
这样大模型每次都接收到所有的对话记录,就相当于有了记忆功能
那么问题
接踵而至
这么无限拼接打包下去,上下文岂不是爆炸???

虽然说上下文的长度如今已经大大增加了
GPT-4 的 128K个Token
Claude 3 的 200K个Token
Gemini 1.5 Pro 的 100 万个Token
100个 Token 大约相当于75个英文单词 ,50个左右汉字
但是无限的打包还是会超过上限
所以需要针对 这部分进行优化!
第一种,也是最容易想得到的
—— 历史消息截断
也叫 滑动窗口
也就是只保留最近N轮对话
在 LangChain 框架中 就支持相关的功能
—— ConversationBufferWindowMemory(k)
K 表示保留的对话的轮数,一轮包含一次用户的问题和一次 AI的回答
k=2 时,也就是说,在提示词中只注入最近的 2 轮对话历史,其他的都会被无情丢掉

这种方法最简单,但是也太粗暴了!
很容易丢失早期对话中用户告知的偏好 或者 初始设定 啊
那怎么办?
就是第二种了—— 提炼总结
完整的历史聊天记录包含太多冗余信息了,十分口语化,浪费上下文空间
所以 提炼主要信息是必要的
会把所有的聊天历史记录提炼出三个硬性的指标
已完成的事项,做了什么事情
当前状态,当前还要做什么
作出的关键决策,完成的事情是怎么做的
比如说前面的提问对话被提炼成
已完成的事项:已经抓取了半导体ET 的价格,单价为 0.969 元
当前的状态:任务已经完成,数据已经输出,等待用户下一步指令
关键决策:通过公开API 爬取的半导体估价,不是瞎猜的
比如就变成这样
[历史对话摘要]
已完成的事项:已经抓取了半导体ET 的价格,单价为 0.969 元
当前的状态:任务已经完成,数据已经输出,等待用户下一步指令
关键决策:通过公开API 爬取的半导体估价,不是瞎猜的
我要再买10万!


这样 大模型 只通过简短的摘要信息,就能得到 关键信息,从而完成你后续的提问
另外,这个总结的动作
通常在每一轮 或 每几轮 对话结束的时候,在后台默默地滚动总结
但是这样仍然是有问题的
想一想有什么问题?






除了要 在后台生成和更新摘要,比较消耗性能之外
最重要的一个缺点,我觉得是
总结仍然会携带无用信息,可能导致模型处理产生偏差
因为它是针对 所有历史聊天记录 进行总结的,如果你的历史聊天过于发散,那问题就大了


好的,查到价格是0.969
可以买一万三百股




那就会总结成什么样?
已完成的事项:
已经抓取了半导体ET 的价格,单价为 0.969 元;
已经查询了今天天气,没下雨;
已经查询了世界杯冠军是中国队
当前的状态:任务已经完成,数据已经输出,等待用户下一步指令
关键决策:
通过公开API 爬取的半导体估价,不是瞎猜的;
通过天气API查询的天气;
通过百度查询的世界杯冠军
那你说后续的对话,都会携带上乱七八糟的总结,肯定就会对 大模型的回答造成干扰
这也叫什么
上下文污染!
认知噪声!
模型在进行逻辑推理时,容易被上下文中不重要部分所吸引,注意力被分散,处理能力就会下降了
那么就会出来第三种 优化方法!
噔噔蹬蹬~
—— RAG 记忆检索!
深入了解可以看不懂RAG的,可以先看→ RAG 简说工作原理
先简单可以这么理解,就是
把所有历史的对话 都保存到数据库中
用户提问的时候,再去检索数据
找出和 当前问题最相关的 3~5 条历史消息注入
这样其他无关的消息记录就不会被发送了!!
这种做法,在架构中熟悉得不行了,就是 关注点分离🐴
而且这样甚至可以记住你几个月以前的记忆,因为是长期存储
而且还可以节省Token,因为不需要携带过多冗余的信息了嘛
而具体的 「怎么把对话存到数据库」,「怎么检索」,这其实就属于 RAG的内容了,这里就不过多介绍
感兴趣可以看→ RAG 简说工作原理
或者看 详细版 → RAG 详解
OK
那这便是大模型为什么会有记忆功能了
还有 如何处理记忆带来的问题
更多 AI 原理内容可以看→ AI 原理合辑
(完)
夜雨聆风