把 Agent 的记忆写进文件:内存 vs 文件,两把钥匙搞定多会话

把 Agent 的记忆写进文件:内存 vs 文件,两把钥匙搞定多会话

上一篇我讲了 Agent 的 Memory 怎么在上下文预算里做截断 。这一篇换一个方向,讲 Agent 的"记事本"到底存在哪

还是先给结论:同样的对话历史,可以存进内存,也可以落进文件。 存哪个,决定你的 Agent 是"关掉就失忆",还是"重启依然记得你昨天聊过什么"。

我手上有三个 LangChain 的小 demo,正好是一套完整的演进:

  • history-test:内存记忆(最基础)
  • history-test2:文件记忆 · 写入
  • history-test3:文件记忆 · 恢复后续聊

这一篇就顺着这三步,讲透一个概念------记忆的存储介质,决定了 Agent 的生命周期

一个概念:Agent 的"记住"有生命周期

很多刚写 Agent 的人会有个错觉:加个 memory,模型就"一直记住了"。不是的。

Chat 模型本身无状态,所谓的记忆,其实是你每轮把历史塞回 messages。那么问题就变成:这些"塞回去的东西",你存在哪、能活多久?

text 复制代码
内存        -> 快,但程序一停,记忆清零(进程级)
文件 / 存储  -> 慢一点,但能躺着,重启不丢(跨进程、跨会话)
向量库      -> 最"在线",配合检索(RAG + Memory)

这一篇只玩前两个:内存文件

第一站:内存记忆(InMemoryChatMessageHistory

最直接的实现是 InMemoryChatMessageHistory------一个放在内存里的历史对象,你往里 addMessage,再 getMessages 取回来。

js 复制代码
import { ChatOpenAI } from '@langchain/openai';
import { InMemoryChatMessageHistory } from '@langchain/core/chat_history';
import { HumanMessage, SystemMessage } from '@langchain/core/messages';

const history = new InMemoryChatMessageHistory();
const systemMessage = new SystemMessage("你是一个幽默的做菜助手");

// 第一轮
await history.addMessage(new HumanMessage("你今天吃的什么?"));
const messages1 = [systemMessage, ...(await history.getMessages())];
const response1 = await model.invoke(messages1);
await history.addMessage(response1);            // 记得把回复也写回 history

// 第二轮,history 已经带上上一轮记忆
await history.addMessage(new HumanMessage("好吃吗?"));
const messages2 = [systemMessage, ...(await history.getMessages())];
const response2 = await model.invoke(messages2);

console.log(`共保存 ${(await history.getMessages()).length} 条对话`);

关键动作 :每一轮,既要把用户说的话 addMessage,也要把模型的回复 addMessage------否则历史链条会断,模型看不到自己刚才说过什么。

但它有个死穴:InMemory,对象一销毁、进程一停,全没了。 适合单次会话,适合演示,不适合要"记得住"的生产。

第二站:文件记忆,写入(FileSystemChatMessageHistory

要跨进程持久化,LangChain 给了文件版。它的构造函数需要两把钥匙

js 复制代码
const filePath = path.join(process.cwd(), 'chat_history.json');
const sessionId = 'user_session_001';   // 多用户:一个用户一把钥匙

const history = new FileSystemChatMessageHistory({ filePath, sessionId });
  • filePath:记忆写在哪个文件
  • sessionId:同一个文件里,区分"这是谁的会话"

之后 API 和内存版一模一样------addMessage / getMessages 照用,但历史会真正落盘成一个 JSON 文件。

js 复制代码
const systemMessage = new SystemMessage("你是一个幽默的做菜助手");

// 第一轮
await history.addMessage(new HumanMessage("红烧肉怎么做?"));
const messages1 = [systemMessage, ...(await history.getMessages())];
const response1 = await model.invoke(messages1);
await history.addMessage(response1);

// 第二轮,自动带上文件里的历史
await history.addMessage(new HumanMessage("好吃吗?"));
const messages2 = [systemMessage, ...(await history.getMessages())];
const response2 = await model.invoke(messages2);

const all = await history.getMessages();
console.log(`共保存 ${all.length} 条对话`);

跑完你会发现,chat_history.json 里真的多出一段 JSON------对话被写进了硬盘。

第三站:文件记忆,恢复后续聊(history-test3

文件版的真正价值在"换一个进程也能接上 "。重启 Node,重新 new FileSystemChatMessageHistory(同样的 filePath + sessionId),历史就从文件里"回来"了:

js 复制代码
const restoredHistory = new FileSystemChatMessageHistory({ filePath, sessionId });
const restoredMessages = await restoredHistory.getMessages();

console.log(`从文件中恢复了 ${restoredMessages.length} 条历史记录`);
restoredMessages.forEach((msg, i) => {
  console.log(`${i + 1}. ${msg.type === 'human' ? '用户' : '助手'}:` +
              `${msg.content.substring(0, 50)}...`);
});

// 接着续聊第三轮,模型记得前两轮
await restoredHistory.addMessage(new HumanMessage("需要哪些食材"));
const messages3 = [systemMessage, ...(await restoredHistory.getMessages())];
const response3 = await model.invoke(messages3);
await restoredHistory.addMessage(response3);
console.log(response3.content);

看到没?前面两轮的记忆"存活"到了第三轮,因为它躺在文件里,而不是藏在早已销毁的内存对象里。

一张表看懂三站

InMemory FileSystem
存储介质 内存 磁盘 JSON
进程重启 清零 保留,可恢复
多用户区分 需要自己管 sessionId
典型场景 单次会话 / 演示 跨会话、需持久化

一个反直觉的点:记忆"放哪",比"多聪明"更早决定体验

你花大价钱把 prompt 优化得再聪明,如果记忆只存在内存里,用户重启浏览器换个进程,Everything 全忘------体验直接断崖。

所以搭 Agent 的时候,先问的不是"记忆怎么割",而是:这次对话,它该活多久? 活一场 → 内存;活几场 → 文件;要"大海捞针"地记 → 向量库(RAG + Memory)。

而这三种存储介质,管的还是同一件事:把该记得的,从"窗口塞回去"这件事,做得又稳又持久。


相关推荐
judezh17 分钟前
一次 agent 请求要经过 12 个服务,它们分别在替你做什么
微服务·架构
QUOR18 分钟前
Zorv AI 内置浏览器技术架构与开发指南(新版)
架构·github
SL_staff19 分钟前
风控规则不该写代码:一个开发者视角的规则引擎实践拆解
java·架构·全栈
OpsEye26 分钟前
公有+私有化混合大模型架构,流量治理底层原理科普
网络·架构
武子康26 分钟前
RoboLab 解读:机器人策略评测为什么不能只看二元成功率
人工智能·llm·agent
the局外人40 分钟前
别让 Codex 一口气写完整个前端:5 组 Skills,把页面、逻辑、测试和构建拆清楚
前端·人工智能·agent
Dawson Zhu1 小时前
Agent 评估方法:评估环境、验证器与统计显著性
人工智能·语言模型·架构·aigc·agi
严同学正在努力1 小时前
Oracle数据技术运维大全(下篇)
运维·数据库·ai·oracle·架构
随遇而安zx1 小时前
Spring Cloud Gateway 微服务网关 设计思想与深度解析
java·spring cloud·微服务·架构