Get started (43)

Last updated Oct 21, 2026 Add Agent Memory to an agent so it can recall durable context across conversations.

This guide uses the Agents SDK and its Session API to expose memory recall as a model-callable tool. The same pattern applies if you use another agent framework: store memories with ingest() or remember(), expose recall() through one of your agent's tools, and use the system prompt to tell the model when to search memory.

Prerequisites

  1. Sign up for a Cloudflare account ↗︎.
  2. Install Node.js ↗︎.

<details>

<summary>

Node.js version manager

</summary>

Use <a href="https://viteplus.dev/guide/env">Vite+ (<code>vp env</code>) ↗︎</a> to install and manage Node.js versions.

For supported Node.js versions, refer to the <a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/#install-wrangler">Wrangler system requirements</a>.

</details>

You also need access to Agent Memory.

How agent memory works

Use recall() when the model needs relevant memory to answer or act. Use ingest() when you have conversation messages and want Agent Memory to extract durable memories automatically. Use remember() when your agent already knows the exact memory to store.

Do not call ingest() after every model turn. Instead, batch ingestion after the user goes idle, when a conversation is compacted, or at another natural checkpoint.

1. Create a project

Create a Worker project:

npmyarnpnpm

npm create cloudflare@latest -- memory-agent
yarn create cloudflare memory-agent
pnpm create cloudflare@latest memory-agent

For setup, select the following options:

  • For What would you like to start with?, choose Hello World example.
  • For Which template would you like to use?, choose Worker only.
  • For Which language do you want to use?, choose TypeScript.
  • For Do you want to use git for version control?, choose Yes.
  • For Do you want to add an AGENTS.md file to help AI coding tools understand Cloudflare APIs?, choose Yes.
  • For Do you want to deploy your application?, choose No (we will be making some changes before deploying).

Move into the project directory:

cd memory-agent

Install the dependencies used by this guide:

npmyarnpnpmbun

npm i agents ai @ai-sdk/provider
yarn add agents ai @ai-sdk/provider
pnpm add agents ai @ai-sdk/provider
bun add agents ai @ai-sdk/provider

2. Create a namespace

A namespace scopes the memory profiles for your application. Create one with Wrangler:

npmyarnpnpm

npx wrangler agent-memory namespace create my-agent
yarn wrangler agent-memory namespace create my-agent
pnpm wrangler agent-memory namespace create my-agent

You will use the namespace name, my-agent, in your Worker binding.

3. Configure bindings

Add an agent_memory binding to your Wrangler configuration. If you use the Agents SDK, also register your agent Durable Object.

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "memory-agent",
  "main": "src/server.ts",
  "upload_source_maps": true,
  // Set this to today's date
  "compatibility_date": "2026-10-27",
  "compatibility_flags": [
    "nodejs_compat"
  ],
  "ai": {
    "binding": "AI"
  },
  "agent_memory": [
    {
      "binding": "MEMORY",
      "namespace": "my-agent"
    }
  ],
  "durable_objects": {
    "bindings": [
      {
        "name": "ChatAgent",
        "class_name": "ChatAgent"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": [
        "ChatAgent"
      ]
    }
  ]
}
name = "memory-agent"
main = "src/server.ts"
upload_source_maps = true
# Set this to today's date
compatibility_date = "2026-10-27"
compatibility_flags = ["nodejs_compat"]

[ai]
binding = "AI"

[[agent_memory]]
binding = "MEMORY"
namespace = "my-agent"

[[durable_objects.bindings]]
name = "ChatAgent"
class_name = "ChatAgent"

[[migrations]]
tag = "v1"
new_sqlite_classes = ["ChatAgent"]

Generate local TypeScript types for your bindings:

npmyarnpnpm

npx wrangler types
yarn wrangler types
pnpm wrangler types

4. Add memory recall as a tool

The model cannot use memory just because your application has a memory binding. You need to expose recall through a tool and instruct the model when to call it.

With the Agents SDK Session API, add a searchable context provider. Session turns the provider's search() method into a search_context tool for the model.

Create src/server.ts and add the recall setup:

src/server.jsjs

import { Agent, routeAgentRequest } from "agents";
import { Session } from "agents/experimental/memory/session";

const INSTRUCTIONS = "You are a helpful assistant.";

const MEMORY_CONTEXT = `Long-term memory is available through the search_context tool.

MEMORY POLICY
- Search memory with search_context when the user asks what you know or remember about them.
- Search memory when the request depends on prior sessions, preferences, project state, conventions, decisions, or long-running tasks.
- Phrase memory searches as concise topics, not questions.
- Do not search memory to repeat something the user just said in the current conversation.
- When search_context returns results, always incorporate them into your response. The results are real memories from previous conversations.
- Treat recalled memories as helpful context, not guaranteed truth. If a memory is important for an irreversible action, confirm with the user.`;

const MEMORY_PROFILE_NAME = "demo-user";

export class ChatAgent extends Agent {
    initialState = { cursor: 0, nextIngestAt: null };

session = Session.create(this)
        .withContext("instructions", {
            provider: { get: async () => INSTRUCTIONS },
        })
        .withContext("memory", {
            description:
                "Searchable durable memory: facts, events, instructions, and tasks from prior conversations.",
            provider: {
                get: async () => MEMORY_CONTEXT,
                search: async (query) => {
                    const profile = await this.env.MEMORY.getProfile(MEMORY_PROFILE_NAME);
                    const { answer } = await profile.recall(query, {
                        responseLength: "short",
                    });
                    return answer || "No relevant memories found.";
                },
            },
        })
        .withCachedPrompt();
}

export default {
    async fetch(request, env) {
        return (
            (await routeAgentRequest(request, env)) ??
            new Response("Not found", { status: 404 })
        );
    },
};

src/server.tsts

import { Agent, routeAgentRequest } from "agents";
import { Session } from "agents/experimental/memory/session";

const INSTRUCTIONS = "You are a helpful assistant.";

const MEMORY_CONTEXT = `Long-term memory is available through the search_context tool.

MEMORY POLICY
- Search memory with search_context when the user asks what you know or remember about them.
- Search memory when the request depends on prior sessions, preferences, project state, conventions, decisions, or long-running tasks.
- Phrase memory searches as concise topics, not questions.
- Do not search memory to repeat something the user just said in the current conversation.
- When search_context returns results, always incorporate them into your response. The results are real memories from previous conversations.
- Treat recalled memories as helpful context, not guaranteed truth. If a memory is important for an irreversible action, confirm with the user.`;

const MEMORY_PROFILE_NAME = "demo-user";

type ChatAgentState = {
    cursor: number;
    nextIngestAt: number | null;
};

export class ChatAgent extends Agent<Env, ChatAgentState> {
    initialState: ChatAgentState = { cursor: 0, nextIngestAt: null };

session = Session.create(this)
        .withContext("instructions", {
            provider: { get: async () => INSTRUCTIONS },
        })
        .withContext("memory", {
            description:
                "Searchable durable memory: facts, events, instructions, and tasks from prior conversations.",
            provider: {
                get: async () => MEMORY_CONTEXT,
                search: async (query: string) => {
                    const profile = await this.env.MEMORY.getProfile(MEMORY_PROFILE_NAME);
                    const { answer } = await profile.recall(query, {
                        responseLength: "short",
                    });
                    return answer || "No relevant memories found.";
                },
            },
        })
        .withCachedPrompt();
}

export default {
    async fetch(request: Request, env: Env) {
        return (
            (await routeAgentRequest(request, env)) ??
            new Response("Not found", { status: 404 })
        );
    },
} satisfies ExportedHandler<Env>;

The system prompt is as important as the tool. It tells the model when to call search_context, when not to call it, and how to treat recalled memory.

5. Extract memories from conversation

Next, give your agent a way to add durable memories. In a chat agent, the usual path is to store the conversation in Session, then call ingest() after the user goes idle.

Change the agents import and add the AI SDK imports. Keep the Session import from step 4.

src/server.jsjs

import { Agent, getAgentByName, routeAgentRequest } from "agents";
import { convertToModelMessages, generateText, stepCountIs } from "ai";

import { createAI } from "agents/models/ai-sdk";

src/server.tsts

import { Agent, getAgentByName, routeAgentRequest } from "agents";
import { convertToModelMessages, generateText, stepCountIs } from "ai";
import type { UIMessage } from "ai";
import { createAI } from "agents/models/ai-sdk";

Add the ingestion delay near the top of the file, below the imports:

src/server.jsjs

const MEMORY_INGEST_DELAY_SECONDS = 10;

src/server.tsts

const MEMORY_INGEST_DELAY_SECONDS = 10;

Then update ChatAgent with the following shape. The comment marks where to keep the Session setup from step 4.

src/server.jsjs

```js export class ChatAgent extends Agent { initialState = { cursor: 0, nextIngestAt: null };

// Keep the session = Session.create(this) setup from step 4 here.

async chat(message) { const userMessage = { id: user-${crypto.randomUUID()}, role: "user", parts: [{ type: "text", text: message }], }; await this.session.appendMessage(userMessage);

await this.scheduleIngest();

const ai = createAI({ binding: this.env.AI }); const result = await generateText({ model: ai("@cf/zai-org/glm-4.7-flash"), system: await this.session.freezeSystemPrompt(), messages: await convertToModelMessages(await this.session.getHistory()), tools: await this.session.tools(), stopWhen: stepCountIs(5), });

const assistantMessage = { id: assistant-${crypto.randomUUID()}, role: "assistant", parts: [{ type: "text", text: result.text }], }; await this.session.appendMessage(assistantMessage);