Documentation Index
Fetch the complete documentation index at: https://langchain-zh.cn/llms.txt
Use this file to discover all available pages before exploring further.
记忆功能让你的智能体能够在多次对话中学习与改进。Deep Agents 将记忆作为一等公民,提供基于文件系统的记忆机制:智能体以文件形式读写记忆,你可以通过后端控制这些文件的存储位置。
本页介绍的是长期记忆:跨对话持久保存的记忆。关于短期记忆(单次会话内的对话历史和临时文件),请参阅上下文工程指南。短期记忆作为智能体状态的一部分自动管理。
记忆工作原理
- 为智能体指定记忆文件路径:创建智能体时,通过
memory= 参数传递文件路径。后端控制这些文件的存储位置和访问权限。
- 智能体在启动时加载记忆:每次对话开始时,智能体将记忆文件内容读入系统提示词。
- 智能体在对话过程中更新记忆:当智能体学习到新信息时,会使用内置的
edit_file 工具更新记忆文件。更改会被持久化,并在下次对话中可用。
两种最常见的模式是智能体作用域记忆(所有用户共享)和用户作用域记忆(按用户隔离)。
智能体作用域记忆
为智能体提供一个共享的记忆文件,所有用户都可以读取和写入。随着智能体在每次对话中积累知识,它会随时间不断改进。当拥有写入权限时,它还可以学习和更新技能。参见技能作为程序性记忆。
关键在于后端命名空间:将其设置为 (assistant_id,) 意味着该智能体的每次对话都读写同一个记忆文件。
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";
import { getConfig } from "@langchain/langgraph";
const agent = createDeepAgent({
memory: ["/memories/AGENTS.md"],
backend: new CompositeBackend(
new StateBackend(),
{
"/memories/": new StoreBackend({
namespace: (ctx) => {
const config = getConfig();
return [config.metadata.assistantId];
},
}),
},
),
});
用户作用域记忆
为每个用户提供其专属的记忆文件。智能体可以记住每位用户的偏好、上下文和历史记录,而核心智能体指令保持不变。如果存储在用户作用域的后端中,用户还可以拥有专属的技能。
命名空间使用 (user_id,),因此每个用户都获得一个隔离的记忆文件副本。用户 A 的偏好永远不会泄露到用户 B 的对话中。
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";
const agent = createDeepAgent({
memory: ["/memories/preferences.md"],
backend: new CompositeBackend(
new StateBackend(),
{
"/memories/": new StoreBackend({
namespace: (ctx) => [ctx.runtime.context.userId],
}),
},
),
});
高级用法
以上部分涵盖了基础知识:配置记忆路径、选择作用域,然后让智能体处理其余部分。本节介绍更高级的模式。
| 维度 | 它回答的问题 | 选项 |
|---|
| 持续时间 | 记忆持续多久? | 短期(单次对话)或长期(跨对话) |
| 信息类型 | 存储什么类型的信息? | 情景记忆(过往经历)、程序性记忆(指令和技能)或语义记忆(事实) |
| 作用域 | 谁可以查看和修改? | 用户、智能体或组织 |
| 更新策略 | 记忆何时写入? | 对话期间(默认)或对话之间 |
| 检索方式 | 记忆如何读取? | 加载到提示词中(默认)或按需检索(例如,技能) |
| 智能体权限 | 智能体可以写入记忆吗? | 读写(默认)或只读(用于共享策略) |
情景记忆
情景记忆存储过去经历的记录:发生了什么、顺序如何以及结果是什么。与语义记忆(存储在 AGENTS.md 等文件中的事实和偏好)不同,情景记忆保留了完整的对话上下文,使智能体能够回忆如何解决问题,而不仅仅是从中学到了什么。
Deep Agents 通过检查点免费获得情景记忆:每次对话都作为检查点线程持久保存。为了使过去的对话可搜索,可以将线程搜索包装在一个工具中。user_id 从运行时上下文中获取,而不是作为参数传递:
import { Client } from "@langchain/langgraph-sdk";
import { getConfig } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });
const searchPastConversations = tool(
async ({ query }: { query: string }) => {
const config = getConfig();
const userId = config.metadata.userId;
const threads = await client.threads.search({
metadata: { userId },
limit: 5,
});
const results = [];
for (const thread of threads) {
const history = await client.threads.getHistory(thread.threadId);
results.push(history);
}
return JSON.stringify(results);
},
{
name: "search_past_conversations",
description: "搜索过去的对话以获取相关上下文。",
}
);
你可以通过调整元数据过滤器来按用户或组织限定线程搜索范围:
// 搜索特定用户的对话
const userThreads = await client.threads.search({
metadata: { userId },
limit: 5,
});
// 搜索整个组织的对话
const orgThreads = await client.threads.search({
metadata: { orgId },
limit: 5,
});
这对于执行复杂、多步骤任务的智能体非常有用。例如,一个编码智能体可以回顾过去的调试会话,直接跳转到可能的根本原因。
技能作为程序性记忆
技能是程序性记忆的一种形式:可重用的指令,告诉智能体如何执行任务。与语义记忆(事实)或情景记忆(经历)不同,程序性记忆编码了智能体可以按需应用的逐步能力。
技能可以是:
- 只读(开发者定义):开发者编写技能,智能体使用它们但不能修改。这是最常见的模式。
- 读写(智能体学习):智能体根据经验创建和更新技能。当智能体拥有记忆的写入权限时,它也可以写入其技能目录。使用策略钩子来控制哪些路径可写。
技能通常是智能体作用域的(所有用户共享),但如果存储在用户命名空间的后端中,也可以是用户作用域的。
在 Deep Agents 中,通过 skills= 参数传递技能。技能是按需加载的,而不是注入到每个提示词中,从而在需要能力之前保持上下文简洁:
import { createDeepAgent } from "deepagents";
const agent = createDeepAgent({
memory: ["/memories/AGENTS.md"],
skills: ["/skills/"],
// ...后端配置
});
有关定义、组织和使用技能的完整指南,请参阅技能文档。
组织级记忆
组织级记忆遵循与用户作用域记忆相同的模式,但使用共享命名空间而不是每个用户的命名空间。将其用于应适用于所有用户和智能体的策略或知识。
组织记忆通常是只读的,以防止通过共享状态进行提示词注入。有关详细信息,请参阅只读与可写记忆。
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";
const agent = createDeepAgent({
memory: [
"/memories/preferences.md",
"/policies/compliance.md",
],
backend: new CompositeBackend(
new StateBackend(),
{
"/memories/": new StoreBackend({
namespace: (ctx) => [ctx.runtime.context.userId],
}),
"/policies/": new StoreBackend({
namespace: (ctx) => [],
}),
},
),
});
从你的应用程序代码填充组织记忆:
import { Client } from "@langchain/langgraph-sdk";
import { createFileData } from "deepagents";
const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });
await client.store.putItem(
[],
"/compliance.md",
createFileData(`## 合规政策
- 绝不披露内部定价
- 财务建议必须包含免责声明
`),
);
使用策略钩子来强制执行组织级记忆为只读。
后台整合
默认情况下,智能体在对话期间写入记忆(热路径)。另一种方法是在对话之间作为后台任务处理记忆,有时称为休眠时间计算。一个独立的深度智能体审查最近的对话,提取关键事实,并将其与现有记忆合并。
| 方法 | 优点 | 缺点 |
|---|
| 热路径(对话期间) | 记忆立即可用,对用户透明 | 增加延迟,智能体必须多任务处理 |
| 后台(对话之间) | 无用户侧延迟,可以跨多个对话综合 | 记忆直到下次对话才可用,需要第二个智能体 |
对于大多数应用,热路径已足够。当你需要减少延迟或提高跨多次对话的记忆质量时,可以添加后台整合。
所有三种方法都部署一个整合智能体与你的主智能体一起运行:一个深度智能体,读取对话历史,提取关键事实,并将其合并到记忆存储中。
| 触发器 | 何时运行 | 最适合 |
|---|
| Cron | 固定计划(例如,每6小时) | 跨多次对话批处理,综合趋势 |
| 计划运行 | 在同一线程上延迟后运行 | 客户端控制整合触发时间的简单设置 |
所有三种方法都使用相同的整合智能体和部署配置。定义一次,然后选择触发器。
整合智能体
整合智能体读取最近的对话历史,并将关键事实合并到记忆存储中。在 langgraph.json 中将其与你的主智能体一起注册:
src/consolidation-agent.ts
import { createDeepAgent } from "deepagents";
import { Client } from "@langchain/langgraph-sdk";
import { getConfig } from "@langchain/langgraph";
import { tool } from "@langchain/core/tools";
const sdkClient = new Client({ apiUrl: "<DEPLOYMENT_URL>" });
const searchRecentConversations = tool(
async ({ query }: { query: string }) => {
const config = getConfig();
const userId = config.configurable.langgraph_auth_user_id;
const since = new Date(Date.now() - 6 * 60 * 60 * 1000).toISOString();
const threads = await sdkClient.threads.search({
metadata: { userId },
updatedAfter: since,
limit: 20,
});
const conversations = [];
for (const thread of threads) {
const history = await sdkClient.threads.getHistory(thread.threadId);
conversations.push(history.values.messages);
}
return JSON.stringify(conversations);
},
{
name: "search_recent_conversations",
description: "搜索该用户最近6小时内更新的对话。",
}
);
const agent = createDeepAgent({
model: "claude-sonnet-4-6",
systemPrompt: `审查最近的对话并更新用户的记忆文件。
合并新事实,删除过时信息,并保持简洁。`,
tools: [searchRecentConversations],
});
export { agent };
{
"dependencies": ["."],
"graphs": {
"agent": "./src/agent.ts:agent",
"consolidation_agent": "./src/consolidation-agent.ts:agent"
},
"env": ".env"
}
Cron
一个cron 任务按固定计划运行整合智能体。智能体搜索所有最近的对话,并将其综合到记忆中。
使用 cron 任务调度整合智能体:
import { Client } from "@langchain/langgraph-sdk";
const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });
const cronJob = await client.crons.create(
"consolidation_agent",
{
schedule: "0 */6 * * *",
input: { messages: [{ role: "user", content: "整合最近的记忆。" }] },
},
);
所有 cron 计划都基于 UTC 时间解释。有关管理和删除 cron 任务的详细信息,请参阅cron 任务。
计划运行
使用 after_seconds 参数在每次对话后调度运行。这是活动驱动的:只有当用户活跃时才会触发整合。传递相同的 thread_id,以便整合智能体可以访问对话上下文。
延迟为对话完成提供了时间(用户可能会发送后续消息),因此整合智能体可以看到完整的交流。
如果用户在延迟期间发送了另一条消息,新运行的 multitask_strategy 控制待处理的整合运行会发生什么。在新运行上使用 rollback 删除过时的整合运行,然后在下次响应后调度一个新的整合运行:
import { Client } from "@langchain/langgraph-sdk";
const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });
// 在对话后30分钟调度整合
await client.runs.create(
thread.thread_id,
"consolidation_agent",
{
input: { messages: [{ role: "user", content: "整合最近的记忆。" }] },
afterSeconds: 1800,
},
);
// 如果用户发送另一条消息,在新运行上使用 rollback
// 在开始前删除待处理的整合运行
const stream = client.runs.stream(
thread.thread_id,
"agent",
{
input: { messages: [{ role: "user", content: newMessage }] },
multitaskStrategy: "rollback",
},
);
有关 multitask_strategy 如何控制同一线程上并发运行的行为的详细信息,请参阅双重发送。
有关部署具有后台进程的智能体的更多信息,请参阅投入生产。
只读与可写记忆
默认情况下,智能体可以读取和写入记忆文件。对于共享状态,如组织策略或合规规则,你可能希望将记忆设置为只读,以便智能体可以引用但不能修改它。这可以防止通过共享记忆进行提示词注入,并确保只有你的应用程序代码控制文件内容。
| 权限 | 使用场景 | 工作原理 |
|---|
| 读写(默认) | 用户偏好、智能体自我改进、学习的技能 | 智能体通过 edit_file 工具更新文件 |
| 只读 | 组织策略、合规规则、共享知识库、开发者定义的技能 | 通过应用程序代码或存储 API 填充。使用策略钩子阻止智能体写入。 |
安全考虑: 如果一个用户可以写入另一个用户读取的记忆,恶意用户可能会向共享状态注入指令。为了缓解这种情况:
- 默认使用用户作用域
(user_id),除非有特定原因需要共享
- 对共享策略使用只读记忆(通过应用程序代码填充,而不是智能体)
- 在智能体写入敏感路径之前添加人在回路验证。使用中断要求人工批准对敏感路径的写入。
要强制执行只读记忆,请在后端使用策略钩子来拒绝对特定路径的写入操作。
并发写入
多个线程可以并行写入记忆,但对同一文件的并发写入可能导致最后写入胜出的冲突。对于用户作用域记忆,这种情况很少见,因为用户通常一次只有一个活跃对话。对于智能体作用域或组织作用域记忆,可以考虑使用后台整合来序列化写入,或者将记忆结构化为每个主题的单独文件以减少争用。
实际上,如果由于冲突导致写入失败,LLM 通常足够智能,能够重试或优雅地恢复,因此单次丢失的写入并不致命。
同一部署中的多个智能体
要在共享部署中为每个智能体提供其专属的记忆,请将 assistant_id 添加到命名空间:
new StoreBackend({
namespace: (ctx) => {
const config = getConfig();
return [config.metadata.assistantId, ctx.runtime.context.userId];
},
})
如果只需要按智能体隔离而不需要按用户限定作用域,可以单独使用 (assistant_id,)。