Get started (43)·续

return result.text; }

async ingestScheduledMemory() { await this.runIngest(); }

async scheduleIngest() { await this.cancelPendingIngest(); await this.schedule( MEMORY_INGEST_DELAY_SECONDS, "ingestScheduledMemory", {}, ); this.setState({ ...this.state, nextIngestAt: Date.now() + MEMORY_INGEST_DELAY_SECONDS * 1000, }); }

async cancelPendingIngest() { const pending = await this.listSchedules(); for (const schedule of pending) { if (schedule.callback === "ingestScheduledMemory") { await this.cancelSchedule(schedule.id); } } }

async runIngest() { const history = await this.session.getHistory(); const messages = history .slice(this.state.cursor) .filter( (message) => message.role === "user" || message.role === "assistant", ) .map((message) => ({ role: message.role, content: message.parts .map((part) => (part.type === "text" ? part.text : "")) .filter(Boolean) .join("\n\n"), })) .filter((message) => message.content);

if (messages.length === 0) { this.setState({ ...this.state, nextIngestAt: null }); return { ingested: 0 }; }

const profile = await this.env.MEMORY.getProfile(MEMORY_PROFILE_NAME); await profile.ingest(messages, { sessionId: this.name });

this.setState({ ...this.state, cursor: history.length, nextIngestAt: null, });

return { ingested: messages.length }; } }


*src/server.tsts*

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

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

async chat(message: string): Promise<string> {
        const userMessage: UIMessage = {
            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()) as UIMessage[],
            ),
            tools: await this.session.tools(),
            stopWhen: stepCountIs(5),
        });

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

return result.text;
    }

async ingestScheduledMemory() {
        await this.runIngest();
    }

private async scheduleIngest() {
        await this.cancelPendingIngest();
        await this.schedule(
            MEMORY_INGEST_DELAY_SECONDS,
            "ingestScheduledMemory",
            {},
        );
        this.setState({
            ...this.state,
            nextIngestAt: Date.now() + MEMORY_INGEST_DELAY_SECONDS * 1000,
        });
    }

private async cancelPendingIngest() {
        const pending = await this.listSchedules();
        for (const schedule of pending) {
            if (schedule.callback === "ingestScheduledMemory") {
                await this.cancelSchedule(schedule.id);
            }
        }
    }

private async runIngest(): Promise<{ ingested: number }> {
        const history = (await this.session.getHistory()) as UIMessage[];
        const messages = history
            .slice(this.state.cursor)
            .filter(
                (message) => message.role === "user" || message.role === "assistant",
            )
            .map((message) => ({
                role: message.role,
                content: message.parts
                    .map((part) => (part.type === "text" ? part.text : ""))
                    .filter(Boolean)
                    .join("\n\n"),
            }))
            .filter((message) => message.content);

if (messages.length === 0) {
            this.setState({ ...this.state, nextIngestAt: null });
            return { ingested: 0 };
        }

const profile = await this.env.MEMORY.getProfile(MEMORY_PROFILE_NAME);
        await profile.ingest(messages, { sessionId: this.name });

this.setState({
            ...this.state,
            cursor: history.length,
            nextIngestAt: null,
        });

return { ingested: messages.length };
    }
}

Replace the default export with a small test endpoint. Each conversationId maps to a separate Agent instance with its own Session history.

src/server.jsjs

export default {
    async fetch(request, env) {
        const url = new URL(request.url);

if (request.method === "POST" && url.pathname === "/chat") {
            const { message, conversationId = "default" } = await request.json();

if (!message) {
                return Response.json({ error: "Missing message" }, { status: 400 });
            }

const agent = await getAgentByName(env.ChatAgent, conversationId);
            const response = await agent.chat(message);
            return Response.json({ response });
        }

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

src/server.tsts

export default {
    async fetch(request: Request, env: Env) {
        const url = new URL(request.url);

if (request.method === "POST" && url.pathname === "/chat") {
            const { message, conversationId = "default" } =
                (await request.json()) as {
                    message?: string;
                    conversationId?: string;
                };
            if (!message) {
                return Response.json({ error: "Missing message" }, { status: 400 });
            }

const agent = await getAgentByName(env.ChatAgent, conversationId);
            const response = await agent.chat(message);
            return Response.json({ response });
        }

return (
            (await routeAgentRequest(request, env)) ??
            new Response("Not found", { status: 404 })
        );
    },
} satisfies ExportedHandler&lt;Env>;

The ingestion path is scheduleIngest() and runIngest(). Each user message cancels the previous pending ingest and schedules a new one, so Agent Memory processes the conversation after the user goes idle instead of after every agent turn.

runIngest() uses a cursor so each batch only includes messages that have not already been ingested. The sessionId groups memories by conversation inside the shared memory profile.

This demo uses one Agent Memory profile, demo-user, across multiple conversations. In production, choose profile names that match your application scope, such as users, teams, tenants, or organizations.

You can also call the ingestion logic from a Session compaction hook. The important constraint is to ingest in batches at natural checkpoints, not after every agent turn. In production, choose an ingest delay that matches your application's user experience.

6. (Optional) Store explicit memories when needed

Automatic ingestion is enough for most apps. If you want the model to store a specific memory immediately, add a server-side tool whose execute function calls remember().

Use this when the agent already knows the exact memory to store. For example, the model might call a rememberMemory tool after the user says: "Remember that I prefer concise answers."

If the model can call a memory-write tool, add system prompt instructions that define what is worth remembering and when to ask for confirmation. For many agents, automatic conversation ingestion is simpler and safer than giving the model a direct memory-write tool.

7. Test the app

Start local development:

npmyarnpnpm

npx wrangler dev
yarn wrangler dev
pnpm wrangler dev

Ask the first conversation to remember a durable preference:

curl -X POST "http://localhost:8787/chat" \
  -H "Content-Type: application/json" \
  -d '{"conversationId":"first-chat","message":"I prefer TypeScript examples and concise answers."}'

Wait at least 30 seconds before sending the next request. The code schedules ingestion to run 10 seconds after the user goes idle, and Agent Memory then needs additional time to extract, classify, and index the memories before they are available for recall.

Ask a different conversation a question that depends on durable memory. This request uses a different Session history but the same Agent Memory profile.

curl -X POST "http://localhost:8787/chat" \
  -H "Content-Type: application/json" \
  -d '{"conversationId":"second-chat","message":"What do you know or remember about me and my preferences?"}'

The model should call search_context, receive recalled memory from Agent Memory, and use that context in its response. The second conversation has no shared Session history with the first, so any knowledge of user preferences comes from Agent Memory.

Next steps

How Agent Memory works

Learn how Agent Memory extracts, stores, and retrieves durable memories.

Namespaces and profiles

Design memory scopes for users, teams, tenants, or organizations.

Workers API

Use `ingest()`, `remember()`, `recall()`, and `getSummary()` from Workers.

Was this helpful?

YesNo

On this page

Get started (43)·续 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agent-memory/get-started/#page","headline":"Get started","description":"Add durable memory recall and ingestion to an agent.","url":"https://developers.cloudflare.com/agent-memory/get-started/","inLanguage":"en","image":"https://developers.cloudflare.com/agent-memory/get-started/og.png?v=2a514aad211b708c","dateModified":"2026-10-21","publisher":{"@type":"Organization","name":"Cloudflare","description":"One platform for your apps, agents, and workforce. Build, secure, and scale without managing infrastructure","url":"https://www.cloudflare.com/","sameAs":["https://github.com/cloudflare","https://www.linkedin.com/company/cloudflare","https://x.com/cloudflare"],"logo":{"@type":"ImageObject","url":"https://developers.cloudflare.com/logo.svg"},"address":{"@type":"PostalAddress","streetAddress":"101 Townsend St","addressLocality":"San Francisco","addressRegion":"CA","postalCode":"94107","addressCountry":"US"},"contactPoint":[{"@type":"ContactPoint","contactType":"Customer Support","url":"https://support.cloudflare.com/","availableLanguage":["English"]},{"@type":"ContactPoint","contactType":"Sales","url":"https://www.cloudflare.com/contact/","availableLanguage":["English"]}]},"isPartOf":{"@type":"WebSite","@id":"https://developers.cloudflare.com/#website","name":"Cloudflare Docs","url":"https://developers.cloudflare.com/"}}


description: A high-level overview of how Agent Memory extracts, stores, and retrieves knowledge from conversations. title: How Agent Memory works image: https://developers.cloudflare.com/agent-memory/concepts/how-agent-memory-works/og.png?v=c1c0d1ef7e2df84e


Skip to content

Documentation Index
Fetch the complete documentation index at: https://developers.cloudflare.com/agent-memory/llms.txt
Use this file to discover all available pages before exploring further.