前三篇我们解决了 Agent 的三个核心问题:第一篇搞清楚了 Agent 是什么(大模型 + 工具调用能力),第二篇手写了"模型决策 + 代码执行"的循环,第三篇把循环交给了 LangGraph 框架。这一篇我们要解决最后一个问题:怎么让 Agent 记住之前说过什么、做过什么。
先放结论:LangChain 的记忆机制已经发生了根本性变化------旧版 ConversationBufferMemory 已被淘汰,新版记忆是 LangGraph 图状态的一部分,天然支持持久化、断点续跑、多会话隔离。
为什么大模型天生"失忆"
做个实验就能验证:
arduino
// 第一次调用
await model.invoke('你好,我叫张三');
// 第二次调用
await model.invoke('我叫什么名字?');
第二次调用时,模型完全不知道"张三"是谁。原因很简单:大模型本身是无状态的,每次调用都是一次全新的请求,它不记得上一轮对话说了什么。
我们平时用的 ChatGPT、通义千问这些产品之所以能"记住",是因为应用程序在模型外面建了一套记忆系统------把每一轮对话存下来,下一次调用时把历史消息一起发给模型。
LangChain 的记忆模块,做的就是这件事。
旧版记忆:ConversationBufferMemory(已淘汰)
早期的 LangChain 用 ConversationBufferMemory 来管理记忆:
javascript
// 旧版写法,已废弃
import { ConversationBufferMemory } from 'langchain/memory';
import { ConversationChain } from 'langchain/chains';
const memory = new ConversationBufferMemory();
const chain = new ConversationChain({ llm, memory });
await chain.invoke({ input: '你好,我叫张三' });
await chain.invoke({ input: '我叫什么名字?' }); // 能记住
它的问题很明显:
- 只能存内存:程序一重启,全部丢失
- 附加在 Chain 上:和 Agent/Graph 体系割裂,多轮工具调用时记忆容易乱
- 没有会话隔离:多用户场景下记忆会串
- 不支持断点恢复:程序崩溃后无法从中间状态继续
所以新版 LangChain 已经把它淘汰了。
新版记忆:LangGraph Checkpoint
新版记忆的核心思路是:把记忆变成图状态的一部分,而不是外挂的组件。
第三篇我们定义的 AgentState 里,messages 字段就是记忆:
javascript
const AgentState = {
messages: {
value: (x, y) => x.concat(y), // Reducer:追加,不覆盖
default: () => [],
},
};
所有节点读写同一个中央状态,多轮对话历史天然就存在 messages 数组里。但这还不够------默认情况下状态只存在于内存,程序重启就丢。要让它持久化,需要加一个 Checkpointer。
从内存到数据库:三种存储后端
1. 内存存储(测试用)
ini
import { InMemorySaver } from '@langgraph/checkpoint-memory';
const checkpointer = new InMemorySaver();
const app = workflow.compile({ checkpointer });
// 调用时传入 thread_id 区分会话
const config = { configurable: { thread_id: 'session_1' } };
await app.invoke({ messages: [...] }, config);
InMemorySaver 把状态存在内存里,同一个 thread_id 的多次调用能共享历史。但程序重启后数据丢失,只适合测试。
2. SQLite 存储(开发/轻量生产)
ini
import { SqliteSaver } from '@langgraph/checkpoint-sqlite';
const checkpointer = SqliteSaver.fromConnString('./agent.db');
const app = workflow.compile({ checkpointer });
一行代码,状态自动持久化到 SQLite 文件。程序重启后,用同一个 thread_id 调用,能完整恢复之前的对话历史和工具调用记录。
3. PostgreSQL 存储(生产环境)
ini
import { PostgresSaver } from '@langgraph/checkpoint-postgres';
const checkpointer = PostgresSaver.fromConnString(
'postgresql://user:pass@localhost:5432/mydb'
);
checkpointer.setup(); // 自动创建表结构
const app = workflow.compile({ checkpointer });
setup() 会自动创建四张表:
- checkpoints:主表,存每个 thread 的 checkpoint 快照
- checkpoint_blobs:存复杂 channel 值(比如很长的消息列表)
- checkpoint_writes:存中间写入/pending writes
- checkpoint_migrations:迁移版本表
生产环境推荐 PostgreSQL,支持 ACID 事务、高可用、水平扩展。
实战:让 Agent 记住用户名字
arduino
import { createAgent } from '@langchain/core/agents';
import { InMemorySaver } from '@langgraph/checkpoint-memory';
const agent = createAgent({
model: modelWithTools,
tools: [readFileTool],
checkpointer: new InMemorySaver(),
});
const config = { configurable: { thread_id: 'user_zhangsan' } };
// 第一轮
await agent.invoke({
messages: [new HumanMessage('你好,我叫张三')],
}, config);
// 第二轮------Agent 能记住
const result = await agent.invoke({
messages: [new HumanMessage('我叫什么名字?')],
}, config);
console.log(result.messages[result.messages.length - 1].content);
// 输出:你叫张三。
关键点:
checkpointer:创建 Agent 时传入,开启记忆功能thread_id:调用时通过config.configurable.thread_id传入,区分不同用户/会话- 同一个
thread_id的多次调用共享状态 ,不同thread_id完全隔离
记忆的两个维度:短期 vs 长期
上面的 Checkpoint 机制解决的是短期记忆 (线程级上下文)------记住当前会话里之前说了什么。但生产环境还需要长期记忆:
| 维度 | 存储位置 | 作用 |
|---|---|---|
| 短期记忆 | Checkpointer(内存/SQLite/Postgres) | 记住当前会话的对话历史 |
| 长期记忆 | LangGraph Store / 向量数据库 | 记住跨会话的用户偏好、关键事实 |
长期记忆的典型实现是用 LangGraph Store,通过 store.put() 和 store.search() 主动读写:
csharp
// 存储用户偏好
await store.put(['user_preferences', 'user_123'], 'likes_music', {
value: '轻音乐',
context: '用户主动声明',
});
// 检索用户偏好
const prefs = await store.search(['user_preferences', 'user_123']);
短期记忆保证"当前对话不断片",长期记忆保证"换了会话还记得你是谁"。两者配合,Agent 才真正拥有"记忆"。
记忆管理的三个策略
对话轮次多了之后,messages 数组会越来越长,最终超出模型的上下文窗口。这时候需要管理策略:
窗口截断:只保留最近 N 轮对话,自动淘汰老旧内容。简单粗暴,但会丢失早期关键信息。
摘要压缩:用 LLM 把历史对话生成摘要,用摘要替代原始消息。节省 Token,但可能丢失细节,且每轮都要额外调用一次模型。
混合策略(生产首选) :近期对话完整保留(窗口模式),超出窗口的历史自动生成摘要。兼顾细节保留与长度控制。
小结
- 旧版
ConversationBufferMemory已淘汰,新版记忆是 LangGraph 图状态的一部分 - 短期记忆靠 Checkpointer :
InMemorySaver(测试)、SqliteSaver(开发)、PostgresSaver(生产),通过thread_id区分会话 - 长期记忆靠 LangGraph Store:跨会话存储用户偏好、关键事实
- 记忆管理三策略:窗口截断、摘要压缩、混合策略(生产首选)