跳到主要内容

文档索引

在以下地址获取完整的文档索引:https://docs.langchain.org.cn/llms.txt

在进一步探索之前,请使用此文件发现所有可用页面。

记忆功能让你的代理能够在跨对话过程中进行学习和改进。Deep Agents 通过文件系统支持的记忆,将记忆提升为一等公民:代理将记忆作为文件进行读写,你可以通过 后端 (backends) 控制这些文件的存储位置。
本页面介绍长期记忆:即跨对话持久存在的记忆。有关短期记忆(单次会话内的对话历史和草稿文件),请参阅 上下文工程 (context engineering) 指南。短期记忆作为代理 状态 (state) 的一部分被自动管理。短期记忆通过检查点限定在单个线程中;长期记忆通过存储跨线程持久存在

记忆如何工作

  1. 将代理指向记忆文件。 创建代理时,通过 memory= 传入文件路径。你还可以通过 skills= 传入 技能 (skills) 以获取程序化记忆(即告诉代理如何执行任务的可重用指令)。后端 用于控制文件的存储位置及其访问权限。
  2. 代理读取记忆。 代理可以在启动时将记忆文件加载到系统提示词中,或者在对话过程中按需读取。例如,技能 使用按需加载:代理在启动时只读取技能描述,仅当匹配到任务时才读取完整的技能文件。这在需要某种能力之前保持了上下文的精简。
  3. 代理更新记忆(可选)。 当代理学到新信息时,可以使用内置的 edit_file 工具更新记忆文件。更新可以在对话过程中(默认),也可以通过 后台整合 在对话间隙进行。变更会被持久化,并在下一次对话中可用。并非所有记忆都是可写的:开发者定义的 技能组织策略 通常是只读的。详情请参阅 只读 vs 可写记忆
最常见的两种模式是 代理作用域记忆(在所有用户间共享)和 用户作用域记忆(每个用户独立)。

作用域记忆

代理记忆可以被设定作用域,使得同一组记忆文件可供所有使用该代理的用户访问,或者记忆文件也可以对每个用户保持独立。

代理作用域记忆

赋予代理随时间演变的独特持久身份。代理作用域记忆在所有用户间共享,因此代理通过每一次对话积累起自己的人格、知识和已习得的偏好。随着与用户的互动,它会发展出专业知识、优化处理方法,并记住哪些做法有效。如果拥有写入权限,它还可以学习和更新 技能 关键在于后端命名空间:将其设置为 (assistant_id,) 意味着该代理的每一次对话都会读取和写入同一个记忆文件。
访问 rt.serverInfo 需要 deepagents>=1.9.0。旧版本请改用 getConfig().metadata.assistantId 来读取助手 ID。
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";

const agent = createDeepAgent({
  memory: ["/memories/AGENTS.md"],
  skills: ["/skills/"],
  backend: new CompositeBackend(
    new StateBackend(),
    {
      "/memories/": new StoreBackend({
        namespace: (rt) => [rt.serverInfo.assistantId],
      }),
      "/skills/": new StoreBackend({
        namespace: (rt) => [rt.serverInfo.assistantId],
      }),
    },
  ),
});
使用初始记忆填充存储,然后在两个线程中调用代理,观察它如何记住并更新所学内容。
import { v4 as uuidv4 } from "uuid";
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend, createFileData } from "deepagents";
import { InMemoryStore } from "@langchain/langgraph";

const store = new InMemoryStore();  // Use platform store when deploying to LangSmith

// Seed the memory file
await store.put(
  ["my-agent"],
  "/memories/AGENTS.md",
  createFileData(`## Response style
- Keep responses concise
- Use code examples where possible
`),
);

// Seed a skill
await store.put(
  ["my-agent"],
  "/skills/langgraph-docs/SKILL.md",
  createFileData(`---
name: langgraph-docs
description: Fetch relevant LangGraph documentation to provide accurate guidance.
---

# langgraph-docs

Use the fetch_url tool to read https://docs.langchain.org.cn/llms.txt, then fetch relevant pages.
`),
);

const agent = createDeepAgent({
  memory: ["/memories/AGENTS.md"],
  skills: ["/skills/"],
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),
    {
      "/memories/": new StoreBackend(rt, {
        namespace: (rt) => ["my-agent"],
      }),
      "/skills/": new StoreBackend(rt, {
        namespace: (rt) => ["my-agent"],
      }),
    },
  ),
  store,
});

// Thread 1: the agent learns a new preference and saves it to memory
const config1 = { configurable: { thread_id: uuidv4() } };
await agent.invoke({
  messages: [{ role: "user", content: "I prefer detailed explanations. Remember that." }],
}, config1);

// Thread 2: the agent reads memory and applies the preference
const config2 = { configurable: { thread_id: uuidv4() } };
await agent.invoke({
  messages: [{ role: "user", content: "Explain how transformers work." }],
}, config2);

用户作用域记忆

赋予每个用户各自的记忆文件。代理在保持核心指令固定的同时,按用户记住偏好、上下文和历史记录。如果存储在用户作用域的后端中,用户还可以拥有针对个人的 技能 命名空间使用 (user_id,),因此每个用户都拥有记忆文件的独立副本。用户 A 的偏好永远不会泄漏到用户 B 的对话中。
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend } from "deepagents";

const agent = createDeepAgent({
  memory: ["/memories/preferences.md"],
  skills: ["/skills/"],
  backend: new CompositeBackend(
    new StateBackend(),
    {
      "/memories/": new StoreBackend({
        namespace: (rt) => [rt.serverInfo.user.identity],
      }),
      "/skills/": new StoreBackend({
        namespace: (rt) => [rt.serverInfo.user.identity],
      }),
    },
  ),
});
播种每个用户的记忆,并以两个不同用户的身份调用代理。每个用户只能看到自己的偏好。
import { v4 as uuidv4 } from "uuid";
import { createDeepAgent, CompositeBackend, StateBackend, StoreBackend, createFileData } from "deepagents";
import { InMemoryStore } from "@langchain/langgraph";

const store = new InMemoryStore();  // Use platform store when deploying to LangSmith

// Seed preferences for two users
await store.put(
  ["user-alice"],
  "/memories/preferences.md",
  createFileData(`## Preferences
- Likes concise bullet points
- Prefers Python examples
`),
);
await store.put(
  ["user-bob"],
  "/memories/preferences.md",
  createFileData(`## Preferences
- Likes detailed explanations
- Prefers TypeScript examples
`),
);

// Seed a skill for Alice
await store.put(
  ["user-alice"],
  "/skills/langgraph-docs/SKILL.md",
  createFileData(`---
name: langgraph-docs
description: Fetch relevant LangGraph documentation to provide accurate guidance.
---

# langgraph-docs

Use the fetch_url tool to read https://docs.langchain.org.cn/llms.txt, then fetch relevant pages.
`),
);

const agent = createDeepAgent({
  memory: ["/memories/preferences.md"],
  skills: ["/skills/"],
  backend: (rt) => new CompositeBackend(
    new StateBackend(rt),
    {
      "/memories/": new StoreBackend(rt, {
        namespace: (rt) => [rt.serverInfo.user.identity],
      }),
      "/skills/": new StoreBackend(rt, {
        namespace: (rt) => [rt.serverInfo.user.identity],
      }),
    },
  ),
  store,
});

// When deployed, each authenticated request resolves
// `rt.serverInfo.user.identity` to the calling user, so Alice and Bob
// automatically see only their own preferences.
await agent.invoke(
  { messages: [{ role: "user", content: "How do I read a CSV file?" }] },
  { configurable: { thread_id: uuidv4() } },
);

高级用法

除了记忆路径和作用域的基本配置选项外,你还可以配置更多高级参数
维度回答的问题选项
持续时间它能持续多久?短期(单次对话)或 长期(跨对话)
信息类型这是什么类型的信息?情景记忆(过往经历)、程序化记忆(指令和技能)或 语义记忆(事实)
范围谁可以查看和修改它?用户代理组织
更新策略何时写入记忆?对话过程中(默认)或 对话之间
检索如何读取记忆?加载到提示词中(默认)或按需(例如 技能
代理权限代理可以写入记忆吗?读写(默认)或 只读(用于共享策略)

情景记忆

情景记忆存储过往经历的记录:发生了什么、发生顺序以及结果如何。与语义记忆(存储在 AGENTS.md 等文件中的事实和偏好)不同,情景记忆保留了完整的对话上下文,以便代理能够回忆起问题是如何解决的,而不仅仅是从中学到了什么 Deep Agents 已经使用了支持情景记忆的机制:检查点 (checkpoints),即每一次对话都作为检查点线程持久化。 要使过往对话可搜索,请将线程搜索封装在一个工具中。user_id 从运行时上下文获取,而不是作为参数传递:
import { Client } from "@langchain/langgraph-sdk";
import { tool } from "@langchain/core/tools";

const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });

const searchPastConversations = tool(
  async ({ query }, runtime) => {
    const userId = runtime.serverInfo.user.identity;
    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: "Search past conversations for relevant context.",
  }
);
你可以通过调整元数据过滤器,按用户或组织来限定线程搜索范围。
// Search conversations for a specific user
const userThreads = await client.threads.search({
  metadata: { userId },
  limit: 5,
});

// Search conversations across an organization
const orgThreads = await client.threads.search({
  metadata: { orgId },
  limit: 5,
});
这对于执行复杂、多步骤任务的代理非常有用。例如,编码代理可以回顾过去的调试会话,直接跳到可能的根本原因。

组织级记忆

组织级记忆遵循与用户作用域记忆相同的模式,但使用组织范围的命名空间而不是用户个人命名空间。将其用于应适用于组织内所有用户和代理的策略或知识。 组织记忆通常是只读的,以防止通过共享状态进行提示词注入。详情请参阅 只读 vs 可写记忆
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: (rt) => [rt.serverInfo.user.identity],
      }),
      "/policies/": new StoreBackend({
        namespace: (rt) => [rt.context.orgId],
      }),
    },
  ),
});
通过应用程序代码填充组织记忆
import { Client } from "@langchain/langgraph-sdk";
import { createFileData } from "deepagents";

const client = new Client({ apiUrl: "<DEPLOYMENT_URL>" });

await client.store.putItem(
  [orgId],
  "/compliance.md",
  createFileData(`## Compliance policies
- Never disclose internal pricing
- Always include disclaimers on financial advice
`),
);
使用 权限 (permissions) 强制组织级记忆为只读,或使用 策略钩子 (policy hooks) 实现自定义验证逻辑。

后台整合

默认情况下,代理在对话期间(热路径)写入记忆。另一种选择是作为后台任务在对话之间处理记忆,有时称为休眠时间计算。一个独立的深度代理审查最近的对话,提取关键事实,并将它们与现有记忆合并。
方法优点缺点
热路径(对话期间)记忆即时可用,对用户透明增加延迟,代理必须执行多任务
后台(对话之间)无面向用户的延迟,可以跨多个对话进行合成记忆直到下一次对话才可用,需要第二个代理
对于大多数应用程序,热路径已经足够。当你需要减少延迟或改善跨多次对话的记忆质量时,可以添加后台整合。 建议的模式是与主代理一起部署一个整合代理——一个读取最近对话历史、提取关键事实并将其合并到记忆存储中的深度代理——并按 定时任务计划 触发它。选择一个反映用户实际与代理互动频率的节奏:拥有稳定每日流量的聊天产品可能每几小时整合一次,而每周仅使用几次的工具只需每晚或每周运行一次。整合频率远高于用户对话频率只会浪费 Token 运行无效任务。

整合代理

整合代理读取最近的对话历史并将关键事实合并到记忆存储中。在 langgraph.json 中将其与主代理一起注册。
src/consolidation-agent.ts
import { createDeepAgent } from "deepagents";
import { Client } from "@langchain/langgraph-sdk";
import { tool } from "@langchain/core/tools";

const sdkClient = new Client({ apiUrl: "<DEPLOYMENT_URL>" });

const searchRecentConversations = tool(
  async ({ query }, runtime) => {
    const userId = runtime.serverInfo.user.identity;

    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: "Search this user's conversations updated in the last 6 hours.",
  }
);

const agent = createDeepAgent({
  model: "google_genai:gemini-3.1-pro-preview",
  systemPrompt: `Review recent conversations and update the user's memory file.
Merge new facts, remove outdated information, and keep it concise.`,
  tools: [searchRecentConversations],
});

export { agent };
langgraph.json
{
  "dependencies": ["."],
  "graphs": {
    "agent": "./src/agent.ts:agent",
    "consolidation_agent": "./src/consolidation-agent.ts:agent"
  },
  "env": ".env"
}

定时任务 (Cron)

定时任务 (cron job) 按固定的时间表运行整合代理。该代理搜索最近的对话并将其合成为记忆。将计划与你的使用模式相匹配,以便整合运行大致跟踪实际活动。 使用定时任务调度整合代理
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: "Consolidate recent memories." }] },
  },
);
所有定时任务计划均以 UTC 时间解释。有关管理和删除定时任务的详细信息,请参阅 定时任务
定时任务的间隔必须与整合代理内部的回溯窗口匹配。上述示例每 6 小时运行一次 (0 */6 * * *),且代理的 search_recent_conversations 工具向后查找 timedelta(hours=6) ——请保持两者同步。如果定时任务运行频率高于回溯时间,你将重复处理相同的对话;如果频率较低,你将丢失落在此窗口之外的记忆。
有关部署带有后台进程的代理的更多信息,请参阅 投入生产

只读 vs 可写记忆

默认情况下,代理既可以读取也可以写入记忆文件。对于组织策略或合规规则等共享状态,你可能希望将记忆设为只读,以便代理可以引用但不能修改它。这可以防止通过共享记忆进行提示词注入,并确保只有你的应用程序代码能控制文件中的内容。
权限用例工作原理
读写(默认)用户偏好、代理自我完善、已习得的 技能代理通过 edit_file 工具更新文件
只读组织策略、合规规则、共享知识库、开发者定义的 技能通过应用程序代码或 存储 API 填充。使用 权限 拒绝写入特定路径,或使用 策略钩子 实现自定义验证逻辑。
安全注意事项: 如果一个用户可以写入另一个用户读取的记忆,恶意用户可能会将指令注入到共享状态中。为了缓解这一点:
  • 默认为用户作用域 (user_id),除非你有特定理由需要共享。
  • 对共享策略使用只读记忆(通过应用程序代码填充,而不是通过代理)。
  • 在代理写入共享记忆之前添加人工参与 (human-in-the-loop) 验证。使用 中断 (interrupt) 来要求人类对敏感路径的写入进行批准。
要强制执行只读记忆,请使用 权限 以声明方式拒绝向特定路径写入。对于自定义验证逻辑(速率限制、审计日志、内容检查),请使用 后端策略钩子

并发写入

多个线程可以并行写入记忆,但对同一个文件的并发写入可能导致“最后写入获胜”冲突。对于用户作用域记忆,这种情况很少见,因为用户通常一次只有一个活跃对话。对于代理作用域或组织作用域记忆,请考虑使用 后台整合 来串行化写入,或将记忆结构化为按主题划分的独立文件以减少争用。 在实践中,如果因为冲突导致写入失败,大语言模型通常足够聪明,可以进行重试或平稳恢复,因此丢失单次写入并非灾难性事件。

同一部署中的多个代理

要在共享部署中为每个代理提供其专属记忆,请将 assistant_id 添加到命名空间中。
new StoreBackend({
  namespace: (rt) => [
    rt.serverInfo.assistantId,
    rt.serverInfo.user.identity,
  ],
})
如果你只需要代理间隔离而不需要用户作用域,请单独使用 assistant_id
使用 LangSmith 追踪 来审计代理写入记忆的内容。每一次文件写入在追踪中都显示为一个工具调用。

© . This site is unofficial and not affiliated with LangChain, Inc.