LLM Agent记忆模块实战:从基础实现到优化落地
引言:为什么Agent离不开Memory?
做AI Agent开发的同学都清楚,大模型本身是无状态(Stateless) 的,单次对话结束后,模型不会留存任何历史信息——就像HTTP协议一样,每次请求都是独立的,没有上下文关联。
这就导致纯大模型对话,根本没法实现多轮交互:上一句刚说完红烧肉做法,下一句问“好吃吗”,模型就完全接不上话。而想要打造懂用户、能连贯对话、完成复杂任务的智能Agent,记忆(Memory)模块就是核心基石。
再看当下主流的LLM扩展架构:LLM + Tool(工具调用) + RAG(知识库) + Memory(对话记忆) ,Memory占据着不可替代的位置。对比RAG(低成本丰富上下文)、微调(高成本提升能力),Memory是实现多轮对话最直接、最低成本的方案,也是Agent具备“连贯性”的关键。
一、Memory核心原理:无状态模型的破局之道
1.1 大模型无状态的痛点
大模型的运行逻辑很纯粹:接收请求→消耗算力生成内容→返回结果,全程不存储对话历史,高并发部署也依赖这种无状态特性。但放到实际业务场景,这种特性会让对话变得割裂,无法完成连贯交互、复杂任务拆解等操作。
1.2 Memory的核心作用
Memory模块的本质,就是通过维护消息数组(messages) ,把多轮对话的上下文留存下来,每次发起新请求时,把历史消息一并传给大模型,让模型感知到对话上下文,实现连贯交互。
不管是工具调用(modelWirhTools)、RAG知识库增强,还是复杂任务流转,底层都依赖Memory留存上下文:给模型设定角色(SystemMessage)、留存用户问题(HumanMessage)、记录AI回复(AIMessage)、挂载工具返回结果(ToolMessage),靠消息队列串联起整个对话流程。
二、基础实战:内存级记忆(InMemoryChatMessageHistory)
LangChain作为主流的LLM开发框架,封装了完善的记忆模块,上手门槛极低。先从最简单的内存级记忆入手,实现基础的对话留存与消息截断。
2.1 依赖安装
npm install @langchain/core js-tiktoken dotenv
2.2 内存记忆+消息截断实现
单纯留存消息会有一个致命问题:对话轮数变多,消息数组越来越长,Token消耗暴涨,甚至触发模型上下文窗口限制。所以实战中必须搭配消息截断,采用滑动窗口(LRU)思路,只保留最新N条消息,兼顾上下文与Token成本。
import { InMemoryChatMessageHistory } from '@langchain/core/chat_history';
import { HumanMessage, AIMessage } from '@langchain/core/messages';
import { getEncoding } from 'js-tiktoken';
// 消息数量截断:保留最新N条对话
async function messageCountTruncation() {
// 初始化内存级聊天记忆
const history = new InMemoryChatMessageHistory();
// 设定最大保留消息数,控制Token开销
const maxMessages = 4;
// 模拟多轮人机对话数据
const mockMessages = [
{ type: 'human', content: '我叫张三' },
{ type: 'ai', content: '你好张三,很高兴认识你!' },
{ type: 'human', content: '我今年25岁' },
{ type: 'ai', content: '25岁正是青春年华,有什么我可以帮助你的吗?' },
{ type: 'human', content: '我喜欢编程' },
{ type: 'ai', content: '编程很有趣!你主要用什么语言?' },
{ type: 'human', content: '我住在北京' },
{ type: 'ai', content: '北京是个很棒的城市!' },
{ type: 'human', content: '我的职业是软件工程师' },
{ type: 'ai', content: '软件工程师是个很有前景的职业!' },
];
// 遍历消息,存入内存记忆
for (const msg of mockMessages) {
if (msg.type === 'human') {
await history.addMessage(new HumanMessage(msg.content));
} else {
await history.addMessage(new AIMessage(msg.content));
}
}
// 获取全量历史消息
const allMessages = await history.getMessages();
console.log("=====全量历史消息=====", allMessages);
// 核心截断逻辑:截取最后maxMessages条消息
const trimmedMessages = allMessages.slice(-maxMessages);
console.log("=====截断后保留消息数=====", trimmedMessages.length);
console.log("=====截断后对话内容=====",
trimmedMessages.map(m => `${m.constructor.name}:${m.content}`).join("\n")
);
}
// 执行函数,捕获异常
async function runAll() {
await messageCountTruncation();
}
runAll().catch(console.error);
2.3 代码解析
- InMemoryChatMessageHistory:LangChain内置的内存记忆组件,临时存储对话,程序重启后记忆清空,适合调试、临时对话场景。
- 消息截断逻辑:通过数组slice(-n)方法,实现滑动窗口截断,只保留最新消息,是最轻量化的上下文优化方案。
- 消息类型:区分HumanMessage(用户消息)、AIMessage(AI回复),规范对话格式,适配大模型输入要求。
三、进阶实战:文件持久化记忆(FileSystemChatMessageHistory)
内存记忆虽便捷,但重启程序就丢失数据,没法实现会话恢复、历史复盘,就像Cursor编辑器的对话历史,关闭后仍能找回之前的会话,靠的就是持久化记忆。
借助LangChain的FileSystemChatMessageHistory,可将对话存入本地JSON文件,实现记忆持久化,支持多会话管理、断点续聊。
3.1 新增依赖安装
npm install @langchain/openai @langchain/community
3.2 持久化记忆+多轮对话实现
import 'dotenv/config';
import { ChatOpenAI } from '@langchain/openai';
import { FileSystemChatMessageHistory } from "@langchain/community/stores/message/file_system";
import { HumanMessage, AIMessage, SystemMessage } from "@langchain/core/messages";
import path from "node:path";
// 初始化大模型实例,配置环境变量参数
const model = new ChatOpenAI({
modelName: process.env.MODEL_NAME,
apiKey: process.env.OPENAI_API_KEY,
temperature: 0,
configuration: {
baseURL: process.env.OPENAI_BASE_URL,
},
});
// 文件持久化对话记忆
async function fileHistoryDemo(){
// 定义对话存储文件路径、会话ID
const filePath = path.join(process.cwd(),"chat_history.json");
const sessionId = "user_session_001";
// 设定系统提示词,定义AI角色
const systemMessage = new SystemMessage(
"你是一个友好的做菜助手,喜欢分享美食和烹饪技巧。"
);
// 初始化文件持久化记忆,绑定会话与存储文件
const chatHistory = new FileSystemChatMessageHistory({
filePath: filePath,
sessionId: sessionId,
});
// 第一轮对话
console.log("=====第一轮对话=====");
const userMessage1 = new HumanMessage("红烧肉怎么做");
await chatHistory.addMessage(userMessage1);
// 拼接系统提示+历史消息,调用大模型
const messages1 = [systemMessage,...(await chatHistory.getMessages())];
const response1 = await model.invoke(messages1);
await chatHistory.addMessage(response1);
console.log(`用户:${userMessage1.content}`);
console.log(`助手:${response1.content}`);
console.log(`✔️对话已保存至:${filePath}\n`);
// 第二轮对话
console.log("=====第二轮对话=====");
const userMessage2 = new HumanMessage("好吃吗?");
await chatHistory.addMessage(userMessage2);
const messages2 = [systemMessage,...(await chatHistory.getMessages())];
const response2 = await model.invoke(messages2);
await chatHistory.addMessage(response2);
console.log(`用户:${userMessage2.content}`);
console.log(`助手:${response2.content}`);
console.log(`✔️对话已更新至文件\n`);
// 第三轮对话(断点续聊,直接复用历史记忆)
console.log("=====第三轮对话=====");
const userMessage3 = new HumanMessage("需要哪些食材?");
await chatHistory.addMessage(userMessage3);
const messages3 = [systemMessage,...(await chatHistory.getMessages())];
const response3 = await model.invoke(messages3);
await chatHistory.addMessage(response3);
console.log(`用户:${userMessage3.content}`);
console.log(`助手:${response3.content}`);
}
// 执行并捕获异常
fileHistoryDemo().catch(console.error);
3.3 核心价值
- 持久化存储:对话记录存入本地JSON文件,重启程序不丢失,支持断点续聊。
- 多会话管理:通过sessionId区分不同会话,像Cursor一样按主题拆分对话,切换主题新建会话即可。
- 上下文连贯:每轮对话都携带历史消息,AI能精准承接上文,实现多轮连贯交互。
四、Memory优化方案:解决Token爆炸问题
不管是内存记忆还是持久化记忆,随着对话轮数增加,消息数组膨胀带来的Token消耗剧增、响应变慢、超出上下文窗口问题无法避免,针对这些痛点,分享3种实用优化方案:
4.1 滑动窗口截断(轻量化首选)
也就是前文实现的slice(-n)方案,保留最新N条消息,舍弃早期无关对话,实现成本极低,适配绝大多数简单对话场景,也是LRU缓存思想的落地。
4.2 对话总结压缩(保留核心信息)
当消息达到阈值时,不直接截断,而是调用大模型对历史对话做总结提炼,把长文本压缩成短句,既保留上下文核心,又大幅缩减Token占用。可自动触发(Token占比达40%)或手动触发(/compact指令)。
4.3 记忆+RAG结合(长对话终极方案)
把历史记忆存入数据库/向量库,对话时先通过RAG检索相关历史,再拼接最新消息,打破上下文窗口限制,适配超长篇对话、AI Agent深度交互场景,让Agent越来越懂用户。
4.4 手动清空记忆(场景化优化)
开启新任务、切换话题时,手动清空消息数组,避免无关上下文干扰,同时节省Token开销,提升响应效率。
五、总结与实战避坑
5.1 核心总结
Memory是AI Agent的核心组件,解决了大模型无状态的痛点,是实现多轮连贯对话的基础:
- 入门用InMemoryChatMessageHistory,快速实现内存级记忆,搭配截断优化控制成本;
- 生产用FileSystemChatMessageHistory,实现持久化存储,支持会话恢复与多会话管理;
- 长对话结合截断、总结、RAG,平衡上下文完整性与Token开销。
5.2 实战避坑
- 避免全量消息透传:务必做截断/总结,否则Token成本飙升,甚至触发模型上下文限制;
- 规范消息类型:严格区分SystemMessage、HumanMessage、AIMessage,保证模型输入格式正确;
- 会话隔离:多场景对话用不同sessionId,避免上下文混淆,提升对话准确性。
后续还可以基于LangChain拓展向量库记忆、Redis持久化记忆等高阶方案,适配更复杂的AI Agent场景,让你的智能体真正具备“记忆”与“思考”能力