在多轮对话和Agent系统中,记忆机制是核心组件之一。大模型的上下文窗口有限,如何高效管理长期记忆,让AI在多轮对话中保持上下文连贯性,是一个关键问题。
在LLM Agent架构中,记忆通常分为两类:
| 记忆类型 | 存储位置 | 特点 | 用途 |
|---|---|---|---|
| 短期记忆 | 模型上下文(Context Window) | 速度快,能直接被模型读取 | 保存最近几轮对话 |
| 长期记忆 | 外部存储(向量库/数据库) | 容量大,需要检索召回 | 保存历史关键信息 |
大模型本身的上下文学习就是利用了短期记忆,而长期记忆通常需要借助外部向量存储和检索来实现。
LangChain等Agent框架提供了多种记忆实现方式,针对不同场景可以选择不同策略。
原理:保留全部历史对话,每次都输入给模型。
1from langchain.memory import ConversationBufferMemory
2memory = ConversationBufferMemory()
3memory.save_context({"input": "你好"}, {"output": "怎么了"})
4variables = memory.load_memory_variables({})适用场景:对话轮次少,总token数不超过上下文窗口的场景,比如电信客服。
优点:信息完整,不会丢失细节 缺点:token数增长快,很快耗尽上下文窗口,增加推理成本
原理:只保留最近k轮对话,超过窗口的历史对话直接丢弃。
1from langchain.memory import ConversationBufferWindowMemory
2# 只保留最后1次互动的记忆
3memory = ConversationBufferWindowMemory(k=1)适用场景:只需要关注最近几轮对话,较早对话不重要的场景,比如电商商品咨询。
优点:token数可控,推理速度快 缺点:丢失早期对话中的关键信息
原理:从对话中提取关键实体及其关系,只保留实体信息而不是原始对话。
1from langchain.memory import ConversationEntityMemory
2llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
3memory = ConversationEntityMemory(llm=llm)
4memory.save_context({"input": "公众号《LLM应用全栈开发》的作者是莫尔索"},
5 {"output": "是吗,这个公众号是干嘛的"})
6print(memory.load_memory_variables({"input": "莫尔索是谁?"}))
7# 输出:{'entities': {'莫尔索': '《LLM应用全栈开发》的作者。'}}适用场景:对话围绕特定人物、案件、实体展开,需要记住关键实体信息,比如法律咨询。
优点:相比原始对话,token占用少,保留关键信息 缺点:需要LLM提取实体,增加额外计算开销
原理:将对话中的实体和关系提取出来,构建知识图谱保存记忆。
1from langchain.memory import ConversationKGMemory
2from langchain.llms import OpenAI
3llm = OpenAI(temperature=0)
4memory = ConversationKGMemory(llm=llm)
5memory.save_context({"input": "小李是程序员"}, {"output": "知道了,小李是程序员"})
6memory.save_context({"input": "莫尔索是小李的笔名"}, {"output": "明白,莫尔索是小李的笔名"})
7variables = memory.load_memory_variables({"input": "告诉我关于小李的信息"})
8print(variables)
9# 输出: {'history': 'On 小李: 小李 is 程序员. 小李 的笔名 莫尔索.'}适用场景:医疗咨询、知识问答,需要记住多个实体之间关系。
优点:结构化存储记忆,关系清晰,可推理 缺点:构建和维护知识图谱开销大
原理:对历史对话进行阶段性总结,只保存总结摘要,不保存原始对话。
适用场景:教育辅导、长期咨询,需要理解整体上下文但不需要保留每轮原始对话。
优点:大幅压缩token数,保留整体上下文信息 缺点:可能丢失细节,摘要可能引入错误
原理:保留最近几轮对话的完整内容,对较早的对话进行压缩总结。
[较早对话摘要] ... [最近k轮完整对话]适用场景:长期技术支持问题排障,用户分多次对话提供信息,既需要最近详细信息,也需要历史问题摘要。
优点:兼顾最新细节和长期上下文,token占用可控 缺点:实现复杂度稍高
原理:按token数限制,保留最新的token不超过限制,自动淘汰最早的对话。
适用场景:金融咨询,需要聚焦最近和最关键的问题,避免token溢出。
原理:把所有对话都存在向量数据库,根据当前问题的相似度检索最相关的历史对话。
1from langchain.vectorstores import Chroma
2from langchain.embeddings import OpenAIEmbeddings
3from langchain.memory import VectorStoreRetrieverMemory
4
5vectorstore = Chroma(embedding_function=OpenAIEmbeddings())
6retriever = vectorstore.as_retriever(search_kwargs=dict(k=1))
7memory = VectorStoreRetrieverMemory(retriever=retriever)
8
9memory.save_context({"input": "我喜欢吃火锅"}, {"output": "听起来很好吃"})
10memory.save_context({"input": "我不喜欢看摔跤比赛"}, {"output": "我也是"})适用场景:需要从大量历史对话中召回和当前问题相关的内容,比如新闻问答、长期文档对话。
优点:存储容量几乎无限,只召回相关信息,token效率高 缺点:依赖嵌入模型和检索质量,可能漏检相关信息
大模型的上下文窗口(context window)大小有限,比如GPT-3.5是4k/16k,GPT-4是8k/32k/128k。多轮对话很快会占满上下文,需要有效的管理策略。
| 策略 | 思路 | 适用场景 |
|---|---|---|
| 滑动窗口 | 只保留最近N轮 | 短对话,只需要最近上下文 |
| 摘 要 法 | 压缩历史为摘要 | 长对话,保留整体信息 |
| 实 体 法 | 只保留关键实体 | 围绕实体的对话 |
| 检索法 | 全量存储,按需召回 | 非常长的对话/文档 |
| 混合法 | 最新完整对话 + 历史摘要 + 检索召回 | 大多数实际场景 |
**LLM Agent中的短期记忆和长期记忆有什么区别?
**LangChain中有哪些常见的记忆类型?各自适用什么场景?
**为什么需要记忆管理?直接把所有历史都放上下文不行吗?
**如何在长时间多轮对话中保持长期记忆?
**基于检索的记忆有什么优缺点?
**什么是实体记忆?它适合什么场景?