LLM 存储优化实战:解决大量 QA 与长对话记忆问题
在构建智能助手时,用户聊得久了模型容易'失忆',大量 QA 信息也存不下。这通常是因为模型有 Token 限制,长对话会截断,全存历史又耗资源。结合实战经验,聊聊怎么用 LangChain 的摘要存储解决这个问题,从面试考点到代码实现,一步步讲明白。
一、先搞懂面试常问:为什么会有'存储优化'需求?
面试官常问的两个核心问题,正好戳中痛点:
面试题 1:传统对话系统每次交互独立,模型无法感知历史,怎么解?
答:用记忆模块(如 LangChain 的 Memory)记录历史,但长对话会超 Token,所以需要摘要存储——不存完整对话,只存关键信息摘要,既保连贯性又省 Token。
面试题 2:长对话超出模型 Token 能力,信息截断、性能下降,怎么解?
答:核心是'压缩历史'——用大模型生成对话摘要,后续交互只传摘要而非全量历史,搭配分布式存储(如 MongoDB、Milvus),平衡连贯性、性能和资源消耗。
二、大模型存储的 3 大核心痛点
这些痛点直接影响用户体验:
| 痛点类型 | 具体表现 | 后果 |
|---|---|---|
| 技术限制 | 用户聊 10 轮就超 4k Token 限制 | 早期 QA 信息丢失,回答驴唇不对马嘴 |
| 效率瓶颈 | 全量存历史,检索一次要 600ms+ | 回复慢,用户吐槽'反应迟钝' |
| 业务&合规风险 | 存用户手机号、需求等敏感信息原文 | 有数据泄露风险,质检溯源难 |
三、核心解决方案:摘要存储 +LangChain 实战
解决思路很简单:用 ConversationSummaryMemory 生成对话摘要,只存摘要不存全量历史。优势特别明显:
核心目标
通过摘要存储维护长期上下文,解决'Token 不够用、资源消耗大、连贯性差'三大问题。
技术原理
就像记笔记——不抄老师每句话,只记重点。模型也一样:用大模型(如通义千问)把对话生成摘要,后续交互只传摘要,相当于'带着笔记聊天',而非'带着整本书聊天'。
优势
- 省 Token:摘要比全量历史小 80%,再也不担心超限制;
- 保连贯:摘要含关键信息,模型知道之前聊了啥;
- 易扩展:可存在 MongoDB、Milvus,支持多用户、大数据量。
四、带摘要存储的对话系统
实战准备
先安装依赖:
pip install langchain langchain-openai langchain-core pymilvus # 按需装存储依赖
实战 1:基础版——生成对话摘要
这是最基础的用法,用来测试摘要效果,看看模型能不能抓关键信息:
from langchain.memory import ConversationSummaryMemory
from langchain_openai import ChatOpenAI
# 初始化大模型(大家替换自己的 api_key)
llm = ChatOpenAI(
model_name="qwen-plus",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key="你的 api_key", # 提示:别硬编码,放环境变量里更安全
temperature=0.7 # 摘要不用太死板,0.7 刚好
)
# 初始化摘要记忆(核心组件)
memory = ConversationSummaryMemory(llm=llm)
# 模拟我和用户的对话(项目里的真实对话片段)
memory.save_context({"input":"AI 大模型怎么入门?"}, {"output":"先学基础概念,再调用 API,然后学 LangChain、RAG,最后实战做项目"})
memory.save_context({"input":"有没有适合零基础的课程?"}, {"output":"可以看零基础学 AI 大模型系列,从 API 调用讲到 Agent 实战"})
# 取摘要——看看效果
summary = memory.load_memory_variables({})
print("对话摘要")
print(summary["history"])
# 输出摘要,只存关键信息
实战 2:进阶版——带摘要存储的对话链
这是现在项目里用的核心代码,用 LCEL 链式结构,支持多轮对话,自动更摘要,特别稳定:
from langchain.memory import ConversationSummaryMemory
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
# 1. 初始化大模型
llm = ChatOpenAI(
model_name="qwen-plus",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
api_key="你的 api_key",
temperature=0.7
)
# 2. 初始化摘要记忆(memory_key 要和 prompt 里的变量名一致!)
memory = ConversationSummaryMemory(
llm=llm,
memory_key="chat_history", # 踩坑:这里和下面 prompt 的{chat_history}要同名
return_messages=True # 返回消息对象,更灵活
)
# 3. 定义 prompt——必须包含摘要变量(chat_history)
prompt = ChatPromptTemplate.from_messages([
("system", "你是 AI 助手,基于对话摘要回答问题,语气亲切像聊天"),
("human", "对话摘要:{chat_history}\n用户现在问:{input}")
])
# 4. 构建 LCEL 链(推荐用这个,比 LLMChain 灵活)
chain = (
RunnablePassthrough.assign(
chat_history=lambda _: memory.load_memory_variables({})["chat_history"]
)
| prompt
| llm
| StrOutputParser()
)
# 5. 模拟用户多轮对话
user_inputs = [
"怎么用 LangChain 做记忆功能?",
"之前说的 ConversationSummaryMemory,能存多少轮对话?",
"它和 Milvus 结合的话,怎么存摘要?"
]
# 6. 运行对话(重点注释:LCEL 需要手动保存上下文!)
for query in user_inputs:
print(f"\n用户:{query}")
response = chain.invoke({"input": query})
print(f"助手:{response}")
# 手动保存上下文到记忆(LLMChain 会自动存,LCEL 要手动!)
memory.save_context({"input": query}, {"output": response})
# 查看当前摘要(调试时必看,确保摘要正确)
current_summary = memory.load_memory_variables({})["chat_history"]
print(f"当前对话摘要:{current_summary}")
实战输出效果
用户:怎么用 LangChain 做记忆功能?
助手:可以用 LangChain 的 Memory 模块,比如 ConversationBufferMemory 存完整历史,ConversationSummaryMemory 存摘要,适合长对话...
当前对话摘要:用户询问如何用 LangChain 做记忆功能,助手建议使用 Memory 模块,举例 ConversationBufferMemory 和 ConversationSummaryMemory,后者适合长对话。
用户:之前说的 ConversationSummaryMemory,能存多少轮对话?
助手:它没有固定轮数限制,因为存的是摘要,不是全量对话,哪怕 100 轮,摘要也不会超太多 Token...
当前对话摘要:用户先问 LangChain 记忆功能做法,助手推荐 Memory 模块及两个子类;接着用户问 ConversationSummaryMemory 能存多少轮,助手称无固定限制,因存储的是摘要。
用户:它和 Milvus 结合的话,怎么存摘要?
助手:可以把生成的摘要转成向量,用 LangChain 的 VectorStoreRetrieverMemory,把摘要存在 Milvus 里,需要时检索相关摘要...
当前对话摘要:用户问 ConversationSummaryMemory 与 Milvus 结合的存摘要方法,助手建议将摘要转向量,用 VectorStoreRetrieverMemory 存入 Milvus,需用时检索。
看!就算聊多轮,模型也不会'失忆',摘要里全是关键信息,Token 消耗特别少~
五、关键知识点:LCEL 和 LLMChain 怎么选?
| 特性 | LCEL 链 | LLMChain |
|---|---|---|
| 记忆保存 | 需手动调用 memory.save_context() | 自动保存,不用手动写 |
| 链式灵活性 | 高,可加路由、日志(加了错误捕获) | 固定结构,灵活度低 |
| 调试&扩展 | 可插中间件,适合复杂项目(如多工具调用) | 靠 verbose=True 调试,适合入门 |
| 使用场景 | 智能助手、Agent 项目(复杂场景) | 简单对话 demo(快速验证想法) |
六、避坑指南
- 变量名要一致:memory_key(如"chat_history")必须和 prompt 里的变量名一样,不然会报'变量未定义';
- 摘要模板可定制:想让摘要包含更多 QA 细节,可改 prompt,比如加一句'摘要要包含用户问题和助手核心回答';
- 敏感信息过滤:保存摘要前,用正则替换手机号、邮箱,避免合规风险;
- 存储扩展:量大时,把摘要存 Milvus,用 VectorStoreRetrieverMemory,检索更快。
总结
其实解决大量 QA、长对话存储问题,核心就是'抓重点'——用 ConversationSummaryMemory 生成摘要,只存关键信息,再根据项目复杂度选 LCEL 或 LLMChain。


