长期记忆 (Long-term Memory)
概述
LangChain 智能体使用 LangGraph 持久化来启用长期记忆。这是一个更高级的主题,需要了解 LangGraph 才能使用。
记忆存储
LangGraph 将长期记忆作为 JSON 文档存储在存储库中。
每个记忆都组织在一个自定义的 namespace(类似于文件夹)和一个独特的 key(类似于文件名)下。命名空间通常包含用户或组织 ID 或其他标签,使信息组织更加容易。
这种结构实现了记忆的层次化组织。然后通过内容过滤器支持跨命名空间搜索。
import { InMemoryStore } from "@langchain/langgraph";
const embed = (texts: string[]): number[][] => {
// 替换为实际的嵌入函数或 LangChain embeddings 对象
return texts.map(() => [1.0, 2.0]);
};
// InMemoryStore 将数据保存到内存字典。在生产中使用数据库支持的存储。
const store = new InMemoryStore({ index: { embed, dims: 2 } });
const userId = "my-user";
const applicationContext = "chitchat";
const namespace = [userId, applicationContext];
await store.put(
namespace,
"a-memory",
{
rules: [
"用户喜欢简短、直接的语言",
"用户只使用中文和 TypeScript",
],
"my-key": "my-value",
}
);
// 通过 ID 获取"记忆"
const item = await store.get(namespace, "a-memory");
// 在此命名空间内搜索"记忆",按内容等价过滤,按向量相似度排序
const items = await store.search(
namespace,
{
filter: { "my-key": "my-value" },
query: "语言偏好"
}
);
有关记忆存储的更多信息,请参阅持久化指南。
在工具中读取长期记忆
import * as z from "zod";
import { createAgent, tool, type ToolRuntime } from "langchain";
import { InMemoryStore } from "@langchain/langgraph";
// InMemoryStore 将数据保存到内存字典。在生产中使用数据库支持的存储。
const store = new InMemoryStore();
const contextSchema = z.object({
userId: z.string(),
});
// 使用 put 方法将示例数据写入存储
await store.put(
["users"], // 命名空间,将相关数据分组(用户数据的 users 命名空间)
"user_123", // 命名空间内的键(用户 ID 作为键)
{
name: "张三",
language: "中文",
} // 为给定用户存储的数据
);
const getUserInfo = tool(
// 查找用户信息
async (_, runtime: ToolRuntime>) => {
// 访问存储 - 与提供给 `createAgent` 的相同
const userId = runtime.context.userId;
if (!userId) {
throw new Error("需要 userId");
}
// 从存储中检索数据 - 返回带有值和元数据的 StoreValue 对象
const userInfo = await runtime.store.get(["users"], userId);
return userInfo?.value ? JSON.stringify(userInfo.value) : "未知用户";
},
{
name: "getUserInfo",
description: "从存储中按 userId 查找用户信息。",
schema: z.object({}),
}
);
const agent = createAgent({
model: "gpt-4o-mini",
tools: [getUserInfo],
contextSchema,
// 将存储传递给智能体 - 使智能体在运行工具时能够访问存储
store,
});
// 运行智能体
const result = await agent.invoke(
{ messages: [{ role: "user", content: "查找用户信息" }] },
{ context: { userId: "user_123" } }
);
console.log(result.messages.at(-1)?.content);
/**
* 输出:
* 用户信息:
* - 姓名:张三
* - 语言:中文
*/
从工具写入长期记忆
import * as z from "zod";
import { tool, createAgent, type ToolRuntime } from "langchain";
import { InMemoryStore } from "@langchain/langgraph";
// InMemoryStore 将数据保存到内存字典。在生产中使用数据库支持的存储。
const store = new InMemoryStore();
const contextSchema = z.object({
userId: z.string(),
});
const updateUserInfo = tool(
async (
{ name, language },
runtime: ToolRuntime>
) => {
const userId = runtime.context.userId;
if (!userId) {
throw new Error("需要 userId");
}
// 使用 put 方法将数据写入存储
await runtime.store.put(
["users"], // 命名空间
userId, // 键
{ name, language } // 要存储的数据
);
return `用户信息已更新:${name}, ${language}`;
},
{
name: "updateUserInfo",
description: "更新用户信息",
schema: z.object({
name: z.string().describe("用户姓名"),
language: z.string().describe("首选语言"),
}),
}
);
const agent = createAgent({
model: "gpt-4o-mini",
tools: [updateUserInfo],
contextSchema,
store,
});
// 运行智能体更新用户信息
const result = await agent.invoke(
{ messages: [{ role: "user", content: "将我的姓名更新为李四,语言更新为英文" }] },
{ context: { userId: "user_456" } }
);
// 验证数据已写入存储
const storedUser = await store.get(["users"], "user_456");
console.log(storedUser?.value);
// { name: "李四", language: "英文" }
生产环境存储
在生产环境中,使用数据库支持的存储而不是 InMemoryStore:
import { PostgresStore } from "@langchain/langgraph-checkpoint-postgres";
const DB_URI = "postgresql://user:password@localhost:5432/mydb";
const store = await PostgresStore.fromConnString(DB_URI);
最佳实践
- 命名空间设计 - 使用有意义的命名空间层次结构(如 [userId, appContext])来组织记忆
- 键命名 - 使用描述性的键名,使记忆易于查找
- 过滤器使用 - 利用内容过滤器高效搜索相关记忆
- 向量搜索 - 对于语义搜索,配置嵌入函数以启用相似度搜索