《LLM Agent记忆模块实战:从基础实现到优化落地》

0 阅读7分钟

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 实战避坑

  1. 避免全量消息透传:务必做截断/总结,否则Token成本飙升,甚至触发模型上下文限制;
  2. 规范消息类型:严格区分SystemMessage、HumanMessage、AIMessage,保证模型输入格式正确;
  3. 会话隔离:多场景对话用不同sessionId,避免上下文混淆,提升对话准确性。

后续还可以基于LangChain拓展向量库记忆、Redis持久化记忆等高阶方案,适配更复杂的AI Agent场景,让你的智能体真正具备“记忆”与“思考”能力