Memory 让你的 agent 可以跨 conversations 学习和改进。Deep Agents 通过 filesystem-backed memory 将 memory 作为 first-class capability:agent 以 files 的形式 read 和 write memory,你使用 backends 控制这些 files 存储在哪里。
本页介绍 long-term memory:跨 conversations 持久化的 memory。Short-term memory(单个 session 内的 conversation history 和 scratch files)请参阅 context engineering guide。Short-term memory 会作为 agent state 的一部分自动管理。Short-term memory is scoped to a single thread via checkpoints; long-term memory persists across threads via the store

How memory works

  1. 将 agent 指向 memory files。 创建 agent 时将 file paths 传给 memory=。你也可以通过 skills= 传入 skills,用于 procedural memory(告诉 agent 如何 执行 task 的 reusable instructions)。backend 控制 files 存储在哪里,以及谁可以访问。
  2. Agent 读取 memory。 Agent 可以在 startup 时将 memory files 加载到 system prompt 中,也可以在 conversation 期间按需读取它们。例如,skills 使用 on-demand loading:agent 在 startup 时只读取 skill descriptions,然后只在 skill 匹配 task 时读取 full skill file。这样在 capability 被需要前,context 会保持精简。
  3. Agent 更新 memory(可选)。 当 agent 学到新 information 时,它可以使用内置 edit_file tool 更新 memory files。Updates 可以在 conversation 期间发生(默认),也可以通过 background consolidation 在 conversations 之间后台发生。Changes 会被 persisted,并在下一次 conversation 中可用。并非所有 memory 都是 writable:developer-defined skillsorganization policies 通常是 read-only。详情请参阅 read-only vs writable memory
最常见的两种 patterns 是 agent-scoped memory(所有 users 共享)和 user-scoped memory(按 user 隔离)。

Scoped memory

Agent memory 可以 scoped,使同一组 memory files 对所有使用 agent 的人可访问,也可以让每个 user 拥有各自的 memory files。

Agent-scoped memory

为 agent 提供随时间演化的 persistent identity。Agent-scoped memory 在所有 users 之间共享,因此 agent 会通过每次 conversation 积累自己的 persona、knowledge 和 learned preferences。随着它与 users 交互,它会发展 expertise、refine approach,并记住哪些做法有效。如果它拥有 write access,也可以学习并更新 skills 关键是 backend namespace:将其设置为 (assistant_id,) 意味着该 agent 的每个 conversation 都会 read 和 write 同一个 memory file。
访问 rt.serverInfo 需要 deepagents>=1.9.0。在更旧版本中,请改为从 getConfig().metadata.assistantId 读取 assistant 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],
      }),
    },
  ),
});
用 initial memories 填充 store,然后跨两个 threads invoke agent,观察它如何记住并更新学到的内容。
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.com/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: crypto.randomUUID() } };
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: crypto.randomUUID() } };
await agent.invoke({
  messages: [{ role: "user", content: "Explain how transformers work." }],
}, config2);

User-scoped memory

为每个 user 提供自己的 memory file。Agent 会按 user 记住 preferences、context 和 history,同时 core agent instructions 保持固定。如果 skills 存储在 user-scoped backend 中,users 也可以拥有 per-user skills Namespace 使用 (user_id,),因此每个 user 都获得 isolated copy 的 memory file。User A 的 preferences 永远不会泄漏到 User B 的 conversations。
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],
      }),
    },
  ),
});
Seed per-user memories,并以两个不同 users 身份 invoke agent。每个 user 只会看到自己的 preferences。
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.com/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: crypto.randomUUID() } },
);

Advanced usage

在 memory paths 和 scope 的 basic configuration options 之上,你还可以为 memory 配置更高级的 parameters:
DimensionQuestion it answersOptions
Duration它持续多久?Short-term(single conversation)或 long-term(across conversations)
Information type它是什么类型的信息?Episodic(past experiences)、procedural(instructions 和 skills)或 semantic(facts)
Scope谁可以 see 和 modify 它?Useragentorganization
Update strategyMemories 何时写入?Conversation 期间(默认)或 between conversations
RetrievalMemories 如何读取?加载到 prompt(默认)或 on demand(例如 skills
Agent permissionsAgent 是否可以 write memory?Read-write(默认)或 read-only(用于 shared policies)

Episodic memory

Episodic memory 存储 past experiences 的 records:发生了什么、顺序如何,以及结果是什么。不同于 semantic memory(存储在 AGENTS.md 等 files 中的 facts 和 preferences),episodic memory 会保留完整 conversational context,因此 agent 可以回忆问题是 如何 解决的,而不仅是从中学到了 什么 Deep Agents 已经使用 checkpointers,这是支持 episodic memory 的机制:每个 conversation 都会作为 checkpointed thread 持久化。 若要让 past conversations 可搜索,请将 thread search 包装成 tool。user_id 从 runtime context 拉取,而不是作为 parameter 传入:
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.",
  }
);
你可以通过调整 metadata filter,按 user 或 organization scope thread search:
// 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,
});
这对执行 complex multi-step tasks 的 agents 很有用。例如,coding agent 可以回看 past debugging session,并直接跳到可能的 root cause。

Organization-level memory

Organization-level memory 与 user-scoped memory 遵循相同 pattern,但使用 organization-wide namespace,而不是 per-user namespace。将其用于应适用于 organization 内所有 users 和 agents 的 policies 或 knowledge。 Organization memory 通常是 read-only,以防通过 shared state 发生 prompt injection。详情请参阅 read-only vs writable memory
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],
      }),
    },
  ),
});
从 application code 填充 organization memory:
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 enforce org-level memory 为 read-only,或使用 policy hooks 实现 custom validation logic。

Background consolidation

默认情况下,agent 会在 conversation 期间写入 memories(hot path)。另一种方式是将 memories 作为 background task 在 between conversations 处理,有时称为 sleep time compute。一个单独的 deep agent 会 review recent conversations、extract key facts,并将其与 existing memories 合并。
ApproachProsCons
Hot path(conversation 期间)Memories 立即可用,对 user 透明增加 latency,agent 必须 multitask
Background(between conversations)无 user-facing latency,可以跨多个 conversations synthesizeMemories 要到下一次 conversation 才可用,需要 second agent
对于大多数 applications,hot path 已足够。当你需要减少 latency 或提升跨许多 conversations 的 memory quality 时,再添加 background consolidation。 推荐 pattern 是在 main agent 旁部署一个 consolidation agent,即一个读取 recent conversation history、extract key facts,并 merge 到 memory store 的 deep agent,然后按 cron schedule 触发它。请选择反映 users 实际交互频率的 cadence:日常稳定使用的 chat product 可以每几个小时 consolidate 一次,而每周只用几次的 tool 只需要 nightly 或 weekly 运行。比 users conversation 频率高很多的 consolidation 只会在 no-op runs 上消耗 tokens。

Consolidation agent

Consolidation agent 读取 recent conversation history,并将 key facts 合并进 memory store。在 langgraph.json 中将其与 main agent 一起注册:
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.5-flash",
  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 会按固定 schedule 运行 consolidation agent。Agent 会搜索 recent conversations,并将其 synthesize 到 memory 中。请将 schedule 与你的 usage patterns 匹配,让 consolidation 大致跟随真实 activity。 使用 cron job schedule consolidation agent:
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." }] },
  },
);
所有 cron schedules 都按 UTC 解释。管理和删除 cron jobs 的 details 请参阅 cron jobs
Cron interval 必须匹配 consolidation agent 内的 lookback window。上方 example 每 6 小时运行一次(0 */6 * * *),agent 的 search_recent_conversations tool 会回看 timedelta(hours=6),请保持二者同步。如果 cron 运行频率高于 lookback,你会重复处理相同 conversations;如果运行频率低于 lookback,你会丢掉 window 外的 memories。
有关使用 background processes 部署 agents 的更多信息,请参阅 going to production

Read-only vs writable memory

默认情况下,agent 可以 read 和 write memory files。对于 organization policies 或 compliance rules 等 shared state,你可能希望将 memory 设为 read-only,让 agent 可以 reference 它,但不能 modify 它。这可以防止 shared memory 中的 prompt injection,并确保只有你的 application code 控制 file 内容。
PermissionUse caseHow it works
Read-write(默认)User preferences、agent self-improvement、learned skillsAgent 通过 edit_file tool 更新 files
Read-onlyOrganization policies、compliance rules、shared knowledge bases、developer-defined skills通过 application code 或 Store API 填充。使用 permissions deny writes to specific paths,或使用 policy hooks 实现 custom validation logic。
Security considerations: 如果一个 user 可以写入另一个 user 会读取的 memory,malicious user 可能会将 instructions 注入 shared state。为缓解这一点:
  • 默认使用 user scope (user_id),除非你有具体理由共享
  • 对 shared policies 使用 read-only memory(通过 application code 填充,而不是 agent)
  • 在 agent 写入 shared memory 前添加 human-in-the-loop validation。使用 interrupt 要求 human approval 后再写入 sensitive paths。
若要 enforce read-only memory,请使用 permissions declaratively deny writes to specific paths。对于 custom validation logic(rate limiting、audit logging、content inspection),请使用 backend policy hooks

Concurrent writes

多个 threads 可以并行写入 memory,但并发写入 同一个 file 可能导致 last-write-wins conflicts。对于 user-scoped memory,这通常很少见,因为 users 通常一次只有一个 active conversation。对于 agent-scoped 或 organization-scoped memory,请考虑使用 background consolidation serialize writes,或将 memory 组织为按 topic 分离的 files,以减少 contention。 实践中,如果 write 因 conflict 失败,LLM 通常足够智能,可以 retry 或 graceful recover,因此单个 lost write 通常不是灾难性问题。

Multiple agents in the same deployment

若要在 shared deployment 中为每个 agent 提供自己的 memory,请将 assistant_id 添加到 namespace:
new StoreBackend({
  namespace: (rt) => [
    rt.serverInfo.assistantId,
    rt.serverInfo.user.identity,
  ],
})
如果你只需要 per-agent isolation,而不需要 per-user scoping,请只使用 assistant_id
使用 LangSmith tracing audit agent 写入 memory 的内容。每次 file write 都会在 trace 中显示为 tool call。