Memory全解析:截断、总结、检索,AI 的三种记性怎么选
你有没有遇到过这种情况:和 AI 聊了十几轮,突然它说"你刚才说的是什么来着?"——其实是你塞进 context 的消息太多,早几轮的内容被截掉了。或者反过来:把所有历史消息都带上,token 爆了,每次调用都超级贵。
Memory 管理,是多轮对话系统里最容易踩坑的环节。这篇把三种主流策略从原理到代码全拆一遍。
为什么需要 Memory 管理?
LLM 本质上是无状态的。每次调用 API,它都不记得上一次说了什么。要实现"记忆",唯一的方法是把历史对话放进messages数组传给它。
但问题来了:
- 上下文窗口有限:GPT-4o 是 128K tokens,图片、工具调用、系统 prompt 会吃掉大量空间
- Token 费用是实打实的:每次都带上 100 轮历史,费用直接乘以历史长度
- 信息密度不均:第 1 轮说的"我叫小明"比第 98 轮的废话重要得多,但朴素截断会把它一起丢掉
所以,Memory 管理本质是一个信息压缩与检索的问题:怎么在有限的 token 预算里,保住最有价值的上下文?
三种记忆策略
LangChain.js 提供了三种主流 Memory 方案:
| 策略 | 一句话定义 | 类比 |
|---|---|---|
| 窗口截断 | 只保留最近 N 轮对话 | 鱼的记忆——只记得最近的事 |
| 摘要总结 | 将历史压缩成一段摘要 | 会议纪要——把几小时的会议浓缩成一页 |
| 向量检索 | 历史存入向量库,按需检索相关片段 | 笔记本+搜索——记住所有,按需翻出来 |
一、窗口截断(Buffer Window Memory)
只保留最近 K 轮对话,更早的直接丢弃。适合闲聊类、上下文不需要跨越太远的场景。
import { ChatOpenAI } from"@langchain/openai";import { ConversationChain } from"langchain/chains";import { BufferWindowMemory } from"langchain/memory";import { ChatPromptTemplate, MessagesPlaceholder } from"@langchain/core/prompts";const model = newChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0.7 });const memory = newBufferWindowMemory({k: 5, // 保留最近 5 轮returnMessages: true, // ChatModel 必须设为 truememoryKey: "chat_history",});const prompt = ChatPromptTemplate.fromMessages([ ["system", "你是一个友好的 AI 助手。"],newMessagesPlaceholder("chat_history"), ["human", "{input}"],]);const chain = newConversationChain({ llm: model, memory, prompt });// 手动查看/写入 Memoryconst vars = await memory.loadMemoryVariables({});console.log("当前窗口:", vars);await memory.saveContext( { input: "LangChain 和 LangGraph 有什么区别?" }, { output: "LangChain 是链式调用,LangGraph 是有向图" });二、摘要总结(Summary Memory)
让 LLM 把旧对话压缩成摘要,新对话继续追加。历史信息不丢失,代价是额外的 LLM 调用。适合长对话、需要保留早期用户背景的场景。
import { ChatOpenAI } from"@langchain/openai";import { ConversationSummaryBufferMemory } from"langchain/memory";import { ConversationChain } from"langchain/chains";import { ChatPromptTemplate, MessagesPlaceholder } from"@langchain/core/prompts";const cheapModel = newChatOpenAI({ modelName: "gpt-4o-mini", temperature: 0 });const smartModel = newChatOpenAI({ modelName: "gpt-4o" });// 混合版:近期保留原文,历史压缩成摘要const memory = newConversationSummaryBufferMemory({llm: cheapModel, // 摘要用小模型省钱maxTokenLimit: 1000, // 超过 1000 tokens 触发摘要压缩returnMessages: true,memoryKey: "chat_history",});const prompt = ChatPromptTemplate.fromMessages([ ["system", "你是一个专业的 AI 助手。以下是对话摘要,帮助你了解上下文背景。"],newMessagesPlaceholder("chat_history"), ["human", "{input}"],]);const chain = newConversationChain({ llm: smartModel, memory, prompt });// 即使对话很长,早期的"后端工程师"、"AI客服"等信息都会保留在摘要里const res = await chain.invoke({ input: "现在遇到 Memory 管理的问题,你有什么建议?" });const memVars = await memory.loadMemoryVariables({});console.log("当前摘要:", memVars.chat_history);三、向量检索(VectorStore Memory)
把每一轮对话存入向量数据库,每次对话用当前输入做语义检索,找出最相关的历史片段塞进上下文。适合超长对话和知识密集型问答。
import { ChatOpenAI, OpenAIEmbeddings } from"@langchain/openai";import { VectorStoreRetrieverMemory } from"langchain/memory";import { MemoryVectorStore } from"langchain/vectorstores/memory";import { ConversationChain } from"langchain/chains";import { ChatPromptTemplate } from"@langchain/core/prompts";const vectorStore = newMemoryVectorStore(newOpenAIEmbeddings());const memory = newVectorStoreRetrieverMemory({vectorStoreRetriever: vectorStore.asRetriever(3), // 检索最相关的 3 条memoryKey: "relevant_history",inputKey: "input",});const prompt = ChatPromptTemplate.fromMessages([ ["system", `你是专业 AI 助手。以下是从历史对话检索到的相关片段:{relevant_history}根据这些上下文回答问题。`], ["human", "{input}"],]);const chain = newConversationChain({llm: newChatOpenAI({ modelName: "gpt-4o-mini" }), memory, prompt,});// 预加载历史await memory.saveContext( { input: "我叫小明,用的向量库是 Milvus" }, { output: "了解,Milvus 适合生产环境" });// 跨越多轮后,相关信息依然能被语义检索出来await chain.invoke({ input: "我应该用什么向量库?" }); // 会检索到 Milvus 相关历史生产环境可以无缝替换为持久化向量库(如上一篇讲的 Milvus):
import { Milvus } from"@langchain/community/vectorstores/milvus";const milvusStore = awaitMilvus.fromExistingCollection(newOpenAIEmbeddings(), { collectionName: "conversation_memory", url: "http://localhost:19530" });const persistentMemory = newVectorStoreRetrieverMemory({vectorStoreRetriever: milvusStore.asRetriever(5),memoryKey: "relevant_history",inputKey: "input",});选型对比:什么场景用哪种?
| 维度 | 窗口截断 | 摘要总结 | 向量检索 |
|---|---|---|---|
| 实现复杂度 | ⭐ 极简 | ⭐⭐ 中等 | ⭐⭐⭐ 较高 |
| Token 消耗 | 低(固定窗口) | 中(额外摘要调用) | 低(只取相关片段) |
| 早期信息 | ❌ 会丢失 | ✅ 摘要保留 | ✅ 语义检索保留 |
| 适合场景 | 闲聊、短对话 | 长对话、上下文连贯 | 知识问答、超长历史 |
| 推荐指数 | 🌟🌟🌟 | 🌟🌟🌟🌟 | 🌟🌟🌟🌟🌟 |
选型决策树:
- 对话轮数 < 10 轮 → 窗口截断(最简单)
- 对话轮数 > 10 轮,且上下文连贯性重要 → 摘要总结(用混合版 SummaryBuffer)
- 对话轮数极长 / 需要精准回溯 → 向量检索
- 追求极致效果 → 摘要 + 向量检索组合
常见坑
坑1:returnMessages忘了设true
// ❌ 返回字符串,ChatModel 不认识const memory = new BufferWindowMemory({ k: 5 });// ✅ 返回 Message 对象,适配 ChatModelconst memory = new BufferWindowMemory({ k: 5, returnMessages: true });坑2:memoryKey和 Prompt 占位符不一致
// ❌ memory 用 "history",Prompt 用 "chat_history",直接报错const memory = new ConversationSummaryMemory({ memoryKey: "history" });const prompt = ChatPromptTemplate.fromMessages([ new MessagesPlaceholder("chat_history"), // 对不上]);// ✅ 保持一致const memory = new ConversationSummaryMemory({ memoryKey: "chat_history" });坑3:摘要 Memory 的额外 LLM 调用计入费用
摘要每次对话后都会触发一次 LLM。高频场景下费用不可忽视。建议摘要专用小模型,并设合理的maxTokenLimit避免频繁压缩。
坑4:向量检索结果不够稳定
向量检索是语义相似度,不是精确匹配。用户说"你之前说的第 3 点"时,检索结果可能召回语义相近但并非第 3 点的内容。对"明确引用历史"的意图,可结合时间戳/序号做精确检索。
坑5:多用户场景 Memory 没做隔离
// ❌ 所有用户共享同一个 Memory 实例,历史互相污染const sharedMemory = newBufferWindowMemory({ k: 5 });// ✅ 按 userId / sessionId 隔离const memoryStore = newMap<string, BufferWindowMemory>();functiongetMemory(userId: string) {if (!memoryStore.has(userId)) { memoryStore.set(userId, newBufferWindowMemory({ k: 5, returnMessages: true })); }return memoryStore.get(userId)!;}可收藏清单 ✅
选型:
- 对话 < 10 轮 →
BufferWindowMemory - 需要保留早期信息 →
ConversationSummaryBufferMemory(混合版) - 超长对话 / 知识问答 →
VectorStoreRetrieverMemory
代码规范:
- ChatModel 场景,
returnMessages: true必须设 memoryKey与 Prompt 占位符严格对齐- 摘要 Memory 的 LLM 单独指定便宜模型
- 多用户按
userId/sessionId隔离 Memory 实例
性能与成本:
maxTokenLimit设合理值控制摘要频率- 向量检索的
k值 3~5 条通常够用 - 生产向量库选持久化方案(Milvus、Pinecone)
调试技巧:
memory.loadMemoryVariables({})随时查看 Memory 内容verbose: true查看完整 LLM 调用链路
总结
这篇从头到尾拆解了 LangChain 三种 Memory 策略:
- 窗口截断:实现最简单,只保留最近 K 轮,适合轻量级闲聊场景
- 摘要总结:用 LLM 压缩历史,信息不丢失,混合版(SummaryBuffer)是长对话首选
- 向量检索:语义召回历史片段,适合超长对话和知识密集型问答,生产级方案
- 选型核心:轮数少用截断,轮数多用摘要,轮数极长且需精准回溯用向量检索
- 关键细节:
returnMessages、memoryKey对齐、多用户隔离,三个坑几乎人人踩过
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
