INTEGRITY Dokumentace

Začínáme

Přidejte do agenta Agent Memory, aby mohl napříč konverzacemi vyhledávat trvalý kontext.

Tento návod používá Agents SDK a jeho Session API pro zpřístupnění vyvolání paměti jako nástroje volatelného modelem. Stejný postup platí, pokud používáte jiný framework pro agenty: ukládejte záznamy paměti pomocí ingest() nebo remember(), zpřístupněte recall() prostřednictvím jednoho z nástrojů vašeho agenta a pomocí systémového promptu řekněte modelu, kdy má prohledávat paměť.

Předpoklady

  1. Zaregistrujte si účet Cloudflare ↗.
  2. Nainstalujte Node.js ↗.

Správce verzí Node.js

Použijte správce verzí Node, jako je Volta ↗ nebo nvm ↗ abyste se vyhnuli problémům s oprávněními a mohli měnit verze Node.js. Wrangler, o kterém se dozvíte dále v této příručce, vyžaduje verzi Node 16.17.0 nebo novější.

Budete také potřebovat přístup k Agent Memory.

Jak funguje agent memory

Použijte recall() když model potřebuje k odpovědi nebo jednání relevantní obsah paměti. Použijte ingest() když máte zprávy z konverzace a chcete, aby Agent Memory automaticky extrahoval trvalé záznamy paměti. Použijte remember() když váš agent již přesně ví, jaký záznam paměti uložit.

Nevolejte ingest() po každém tahu modelu. Místo toho spouštějte dávkový import po nečinnosti uživatele, při kompaktaci konverzace nebo v jiném přirozeném kontrolním bodě.

1. Vytvořte projekt

Vytvořte projekt Workeru:

npm create cloudflare@latest -- memory-agent

Při nastavení vyberte následující možnosti:

Přejděte do adresáře projektu:

cd memory-agent

Nainstalujte závislosti použité v tomto průvodci:

npm i agents ai workers-ai-provider

2. Vytvoření jmenného prostoru

A namespace vymezuje profily paměti pro vaši aplikaci. Vytvořte jej pomocí Wrangleru:

npx wrangler agent-memory namespace create my-agent

Použijete název namespace, my-agent, v bindingu vašeho Workeru.

3. Konfigurace bindingů

Přidejte agent_memory binding do konfigurace Wrangler. Pokud používáte Agents SDK, zaregistrujte také Durable Object svého agenta.

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "memory-agent",
  "main": "src/server.ts",
  // Set this to today's date
  "compatibility_date": "2026-08-28",
  "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"
# Set this to today's date
compatibility_date = "2026-08-28"
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"]

Vygenerujte lokální typy TypeScriptu pro své bindingy:

npx wrangler types

4. Přidání vyvolávání paměti jako nástroje

Model nemůže paměť využívat jen proto, že vaše aplikace má memory binding. Vyhledávání (recall) musíte zpřístupnit prostřednictvím nástroje a modelu sdělit, kdy jej má volat.

S Agents SDK Session API, přidejte prohledávatelného poskytovatele kontextu. Session převede search() metodu na search_context nástroj pro model.

Vytvořit src/server.ts a přidejte nastavení vyvolávání paměti:

src/server.js
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.ts
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>;

Systémový prompt je stejně důležitý jako samotný nástroj. Říká modelu, kdy má zavolat search_context, kdy jej nevolat a jak zacházet s vyvolaným obsahem paměti.

5. Extrakce záznamů paměti z konverzace

Dále dejte agentovi možnost ukládat trvalé paměťové záznamy. U chatovacího agenta se konverzace obvykle uloží do relace a poté se zavolá ingest() po nečinnosti uživatele.

Změňte agents import a přidejte importy AI SDK. Ponechte Session import z kroku 4.

src/server.js
import { Agent, getAgentByName, routeAgentRequest } from "agents";
import { convertToModelMessages, generateText, stepCountIs } from "ai";

import { createWorkersAI } from "workers-ai-provider";
src/server.ts
import { Agent, getAgentByName, routeAgentRequest } from "agents";
import { convertToModelMessages, generateText, stepCountIs } from "ai";
import type { UIMessage } from "ai";
import { createWorkersAI } from "workers-ai-provider";

Přidejte zpoždění příjmu blízko začátku souboru, pod importy:

src/server.js
const MEMORY_INGEST_DELAY_SECONDS = 10;
src/server.ts
const MEMORY_INGEST_DELAY_SECONDS = 10;

Poté upravte ChatAgent s následující strukturou. Komentář označuje místo, kde zachovat nastavení Session z kroku 4.

src/server.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 workersai = createWorkersAI({ binding: this.env.AI });
		const result = await generateText({
			model: workersai("@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);

		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.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 workersai = createWorkersAI({ binding: this.env.AI });
		const result = await generateText({
			model: workersai("@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 };
	}
}

Nahraďte výchozí export malým testovacím endpointem. Každý conversationId se mapuje na samostatnou instanci agenta s vlastní historií Session.

src/server.js
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.ts
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<Env>;

Cesta zpracování dat je scheduleIngest() a runIngest(). Každá zpráva uživatele zruší předchozí čekající import a naplánuje nový, takže Agent Memory zpracuje konverzaci až po nečinnosti uživatele, a ne po každém tahu agenta.

runIngest() používá kurzor, takže každá dávka obsahuje pouze zprávy, které ještě nebyly importovány. Parametr sessionId seskupuje záznamy paměti podle konverzace v rámci sdíleného profilu paměti.

Tato ukázka používá jeden profil Agent Memory, demo-user, napříč více konverzacemi. V produkčním prostředí volte názvy profilů podle rozsahu vaší aplikace, například podle uživatelů, týmů, nájemců nebo organizací.

Logiku ingestování můžete zavolat také z hooku Session compaction. Důležitým omezením je ingestovat dávkově v přirozených kontrolních bodech, ne po každém kroku agenta. V produkčním prostředí zvolte zpoždění ingestování tak, aby odpovídalo uživatelskému zážitku vaší aplikace.

6. (Volitelné) Ukládání explicitních záznamů paměti podle potřeby

Pro většinu aplikací postačí automatický příjem. Pokud chcete, aby model uložil konkrétní paměťový záznam okamžitě, přidejte serverový nástroj, jehož funkce execute volá remember().

Použijte v případě, že agent již přesně ví, jaký paměťový záznam má uložit. Model může například zavolat rememberMemory nástroj poté, co uživatel řekne: "Zapamatuj si, že preferuji stručné odpovědi."

Pokud model dokáže volat nástroj pro zápis do paměti, doplňte do systémového promptu instrukce, které určují, co stojí za zapamatování a kdy si vyžádat potvrzení. U řady agentů je automatický příjem konverzací jednodušší a bezpečnější než dát modelu přímý nástroj pro zápis do paměti.

7. Otestování aplikace

Spusťte lokální vývoj:

npx wrangler dev

Požádejte první konverzaci, aby si zapamatovala trvalou preferenci:

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

Před odesláním dalšího požadavku počkejte alespoň 30 sekund. Kód naplánuje spuštění ingestování na 10 sekund poté, co uživatel přejde do nečinnosti, a Agent Memory pak potřebuje další čas na extrakci, klasifikaci a indexaci záznamů, než budou k dispozici pro vyvolání.

Položte v jiné konverzaci otázku, jejíž odpověď závisí na trvalé paměti. Tento požadavek používá jinou historii relace, ale stejný profil Agent Memory.

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?"}'

Model by měl zavolat search_context, přijme vyvolaný obsah paměti z Agent Memory a použije tento kontext ve své odpovědi. Druhá konverzace nemá s první sdílenou historii Session, takže veškerá znalost preferencí uživatele pochází z Agent Memory.

Další kroky

Jak funguje Agent Memory

Zjistěte, jak Agent Memory extrahuje, ukládá a načítá trvalé paměťové záznamy.

Jmenné prostory a profily

Navrhněte paměťové rozsahy pro uživatele, týmy, tenanty nebo organizace.

Workers API

Metody `ingest()`, `remember()`, `recall()` a `getSummary()` používejte z Workerů.