INTEGRITY Dokumentace

Relace

Session API poskytuje agentům trvalé úložiště konverzací se zprávami uspořádanými do stromové struktury (inspirované Pi ↗), kontextové bloky, komprimaci, fulltextové vyhledávání a nástroje ovladatelné AI. Ve výchozím nastavení používá Durable Object SQLite. Pro aplikace, které potřebují sdílený přístup k databázi, analytiku nebo dotazy napříč Durable Objects, je k dispozici také externí úložiště Postgres.

Rychlý start

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

class MyAgent extends Agent {
	session = Session.create(this)
		.withContext("soul", {
			provider: { get: async () => "You are a helpful assistant." },
		})
		.withContext("memory", {
			description: "Learned facts about the user",
			maxTokens: 1100,
		})
		.withCachedPrompt();

	async onMessage(message) {
		await this.session.appendMessage(message);
		const history = await this.session.getHistory();
		const system = await this.session.freezeSystemPrompt();
		const tools = await this.session.tools();
		// Pass history, system prompt, and tools to your LLM
	}
}
import { Agent } from "agents";
import { Session } from "agents/experimental/memory/session";

class MyAgent extends Agent {
	session = Session.create(this)
		.withContext("soul", {
			provider: { get: async () => "You are a helpful assistant." },
		})
		.withContext("memory", {
			description: "Learned facts about the user",
			maxTokens: 1100,
		})
		.withCachedPrompt();

	async onMessage(message: unknown) {
		await this.session.appendMessage(message);
		const history = await this.session.getHistory();
		const system = await this.session.freezeSystemPrompt();
		const tools = await this.session.tools();
		// Pass history, system prompt, and tools to your LLM
	}
}

Vytvoření relace

Použijte Session.create(agent) s řetězitelným builderem. Poskytovatelé kontextu bez explicitního provider možnosti se automaticky propojí se SQLite.

const session = Session.create(this)
	.withContext("soul", { provider: { get: async () => "You are helpful." } })
	.withContext("memory", { description: "Learned facts", maxTokens: 1100 })
	.withCachedPrompt()
	.onCompaction(myCompactFn)
	.compactAfter(100_000);
const session = Session.create(this)
	.withContext("soul", { provider: { get: async () => "You are helpful." } })
	.withContext("memory", { description: "Learned facts", maxTokens: 1100 })
	.withCachedPrompt()
	.onCompaction(myCompactFn)
	.compactAfter(100_000);

Přímý konstruktor

Pro plnou kontrolu nad poskytovateli:

import {
	Session,
	AgentSessionProvider,
	AgentContextProvider,
} from "agents/experimental/memory/session";

const session = new Session(new AgentSessionProvider(this), {
	context: [
		{
			label: "memory",
			description: "Notes",
			maxTokens: 500,
			provider: new AgentContextProvider(this, "memory"),
		},
		{ label: "soul", provider: { get: async () => "You are helpful." } },
	],
});
import {
	Session,
	AgentSessionProvider,
	AgentContextProvider,
} from "agents/experimental/memory/session";

const session = new Session(new AgentSessionProvider(this), {
	context: [
		{
			label: "memory",
			description: "Notes",
			maxTokens: 500,
			provider: new AgentContextProvider(this, "memory"),
		},
		{ label: "soul", provider: { get: async () => "You are helpful." } },
	],
});

Metody builderu

Všechny metody builderu vracejí this pro řetězení. Na pořadí nezáleží, poskytovatelé se přiřazují líně až při prvním použití.

Metoda Popis
Session.create(agent) Statická továrna. agent je libovolný objekt s sql metoda tagged template (váš Agent nebo Durable Object).
.forSession(sessionId) Relaci zařaďte do jmenného prostoru podle ID. Vyžadováno pro izolaci mezi více relacemi, pokud nepoužíváte SessionManager.
.withContext(label, options?) Přidejte kontextový blok. Viz Kontextové bloky.
.withCachedPrompt(provider?) Povolte perzistenci systémového promptu. Prompt se zmrazí při prvním použití a přežije hibernaci i vyřazení z paměti.
.onCompaction(fn) Zaregistrujte kompaktovací funkci. Viz Komprimace.
.compactAfter(tokenThreshold, options?) Automaticky komprimuje, jakmile odhadovaný počet tokenů překročí práh. Vyžaduje .onCompaction(). Předejte { tokenCounter } pro řízení způsobu měření prahové hodnoty.
.onCompactionError(handler) Zpracovávejte chyby z automatické komprimace. Selhání handleru se potlačí, takže zápisy zpráv zůstávají nefatální.

Zprávy

Zprávy používají SessionMessage typ: minimální tvar s id, role, parts, a volitelné createdAt. AI SDK má vlastní UIMessage je strukturně kompatibilní a lze ji předat přímo. Relace ukládá zprávy ve stromové struktuře pomocí parent_id, což umožňuje větvení konverzací.

// Append — auto-parents to the latest leaf unless parentId is specified
await session.appendMessage(message);
await session.appendMessage(message, parentId);

// Update an existing message (matched by message.id)
await session.updateMessage(message);

// Delete specific messages
await session.deleteMessages(["msg-1", "msg-2"]);

// Clear all messages and skill state
await session.clearMessages();
// Append — auto-parents to the latest leaf unless parentId is specified
await session.appendMessage(message);
await session.appendMessage(message, parentId);

// Update an existing message (matched by message.id)
await session.updateMessage(message);

// Delete specific messages
await session.deleteMessages(["msg-1", "msg-2"]);

// Clear all messages and skill state
await session.clearMessages();

Čtení historie

// Linear history from root to the latest leaf
const messages = await session.getHistory();

// History to a specific leaf (for branching)
const branch = await session.getHistory(leafId);

// Get a single message
const msg = await session.getMessage("msg-1");

// Get the newest message
const latest = await session.getLatestLeaf();

// Count messages in path
const count = await session.getPathLength();
// Linear history from root to the latest leaf
const messages = await session.getHistory();

// History to a specific leaf (for branching)
const branch = await session.getHistory(leafId);

// Get a single message
const msg = await session.getMessage("msg-1");

// Get the newest message
const latest = await session.getLatestLeaf();

// Count messages in path
const count = await session.getPathLength();

Větvení

Zprávy tvoří strom. Když appendMessage s parentId která už má potomky, vytvoříte větev. Použijte getBranches() k získání všech podřízených zpráv větvících se z daného bodu:

// Get all child messages that branch from messageId
const branches = await session.getBranches(messageId);
// Get all child messages that branch from messageId
const branches = await session.getBranches(messageId);

Na tom stojí funkce jako regenerace odpovědi: předáním ID uživatelské zprávy získáte původní i regenerovanou odpověď. getHistory(leafId) prochází zvolenou cestu.

Fulltextové vyhledávání v historii konverzace pomocí SQLite FTS5:

const results = await session.search("deployment Friday", { limit: 10 });
// Returns: Array<{ id, role, content, createdAt? }>
const results = await session.search("deployment Friday", { limit: 10 });
// Returns: Array<{ id, role, content, createdAt? }>

Session se zálohou v SQLite používají FTS5 s porterovým stemmingem a tokenizací Unicode. Session se zálohou v Postgres používají fulltextový index Postgres poskytovatele. search() vyvolá výjimku, pokud poskytovatel relace nepodporuje vyhledávání.

Kontextové bloky

Kontextové bloky jsou trvalé sekce typu klíč-hodnota vkládané do systémového promptu. Každý blok má popisek, volitelné popis, a poskytovatel které určuje jeho chování.

Typy poskytovatelů

Existují čtyři typy poskytovatelů, rozpoznávané pomocí duck-typing:

Poskytovatel Rozhraní Chování AI nástroj
ContextProvider get() Blok pouze pro čtení v systémovém promptu ,
WritableContextProvider get() + set() Zapisovatelné přes AI set_context
SkillProvider get() + load() + set?() Dokumenty s klíčem na vyžádání. get() vrátí výpis metadat; load(key) načte celý obsah. load_context, unload_context, set_context
SearchProvider get() + search() + set?() Fulltextově prohledávatelné položky. get() vrátí souhrn; search(query) spouští FTS5. search_context, set_context

Vestavění poskytovatelé

AgentContextProvider : Zapisovatelný kontext podporovaný SQLite. Toto je výchozí nastavení při použití builderu bez explicitního poskytovatele.

import { AgentContextProvider } from "agents/experimental/memory/session";

new AgentContextProvider(this, "memory");
import { AgentContextProvider } from "agents/experimental/memory/session";

new AgentContextProvider(this, "memory");

R2SkillProvider : Bucket Cloudflare R2 pro načítání dokumentů na vyžádání. Skills jsou v systémovém promptu uvedeny jako metadata; celý obsah model na vyžádání načte přes load_context.

import { R2SkillProvider } from "agents/experimental/memory/session";

Session.create(this).withContext("skills", {
	provider: new R2SkillProvider(env.SKILLS_BUCKET, { prefix: "skills/" }),
});
import { R2SkillProvider } from "agents/experimental/memory/session";

Session.create(this).withContext("skills", {
	provider: new R2SkillProvider(env.SKILLS_BUCKET, { prefix: "skills/" }),
});

AgentSearchProvider : Kontext prohledávatelný pomocí SQLite FTS5. Položky jsou indexovány a model je může vyhledávat pomocí search_context.

import { AgentSearchProvider } from "agents/experimental/memory/session";

Session.create(this).withContext("knowledge", {
	description: "Searchable knowledge base",
	provider: new AgentSearchProvider(this),
});
import { AgentSearchProvider } from "agents/experimental/memory/session";

Session.create(this).withContext("knowledge", {
	description: "Searchable knowledge base",
	provider: new AgentSearchProvider(this),
});

Přidávání a odebírání kontextu za běhu

Bloky lze po inicializaci dynamicky přidávat a odebírat:

// Add a new block (auto-wires to SQLite if no provider given)
await session.addContext("extension-notes", {
	description: "From extension X",
	maxTokens: 500,
});

// Remove it
session.removeContext("extension-notes");

// Rebuild the system prompt to reflect changes
await session.refreshSystemPrompt();
// Add a new block (auto-wires to SQLite if no provider given)
await session.addContext("extension-notes", {
	description: "From extension X",
	maxTokens: 500,
});

// Remove it
session.removeContext("extension-notes");

// Rebuild the system prompt to reflect changes
await session.refreshSystemPrompt();

Čtení a zápis kontextu

// Read a single block
const block = session.getContextBlock("memory");
// { label, description?, content, tokens, maxTokens?, writable, isSkill, isSearchable }

// Read all blocks
const blocks = session.getContextBlocks();

// Replace content entirely
await session.replaceContextBlock("memory", "User likes coffee.");

// Append content
await session.appendContextBlock("memory", "\nUser prefers dark roast.");
// Read a single block
const block = session.getContextBlock("memory");
// { label, description?, content, tokens, maxTokens?, writable, isSkill, isSearchable }

// Read all blocks
const blocks = session.getContextBlocks();

// Replace content entirely
await session.replaceContextBlock("memory", "User likes coffee.");

// Append content
await session.appendContextBlock("memory", "\nUser prefers dark roast.");

Systémový prompt

Systémový prompt se sestavuje ze všech kontextových bloků s hlavičkami a metadaty:

══════════════════════════════════════════════
SOUL (Identity) [readonly]
══════════════════════════════════════════════
You are a helpful assistant.

══════════════════════════════════════════════
MEMORY (Learned facts) [45% — 495/1100 tokens]
══════════════════════════════════════════════
User likes coffee.
User prefers dark roast.
// Freeze — first call renders and persists; subsequent calls return cached value
const prompt = await session.freezeSystemPrompt();

// Refresh — re-render from current block state and persist
const updated = await session.refreshSystemPrompt();
// Freeze — first call renders and persists; subsequent calls return cached value
const prompt = await session.freezeSystemPrompt();

// Refresh — re-render from current block state and persist
const updated = await session.refreshSystemPrompt();

Zamrzlý prompt přežije hibernaci Durable Object i vyřazení z paměti, když withCachedPrompt() je zapnuté.

AI nástroje

Session automaticky generuje nástroje na základě typů poskytovatelů vašich kontextových bloků. Předejte je svému LLM společně s vlastními nástroji.

const tools = await session.tools();
const allTools = { ...tools, ...myTools };
const tools = await session.tools();
const allTools = { ...tools, ...myTools };

set_context

Generováno, když existuje libovolný zapisovatelný blok. Zapisuje do běžných bloků, bloků typu skill (s klíčem) nebo bloků typu search (s klíčem). Vynucuje maxTokens limity.

load_context

Generováno, když existuje libovolný blok typu skill. Načte úplný obsah podle klíče z SkillProvider.

unload_context

Generováno společně s load_context. Uvolní prostor v kontextu odebráním dříve načtené dovednosti. Dovednost zůstává k dispozici pro opětovné načtení.

search_context

Generováno, když existuje libovolný blok typu search. Fulltextové vyhledávání v prohledávatelném kontextovém bloku. Vrací 10 nejlepších výsledků podle pořadí FTS5.

Dostupné na SessionManager pouze. Hledá napříč všemi relacemi.

Komprimace

Komprimace shrnuje starší zprávy, aby konverzace zůstaly v limitu počtu tokenů. Původní zprávy se zachovávají v SQLite, souhrn je nedestruktivní překryv uplatňovaný při čtení.

Nastavení

import { createCompactFunction } from "agents/experimental/memory/utils/compaction-helpers";

const session = Session.create(this)
	.withContext("memory", { maxTokens: 1100 })
	.onCompaction(
		createCompactFunction({
			summarize: (prompt) =>
				generateText({ model: myModel, prompt }).then((r) => r.text),
			protectHead: 3,
			tailTokenBudget: 20000,
			minTailMessages: 2,
			tokenCounter: async (messages) => estimateWithYourTokenizer({ messages }),
		}),
	)
	.compactAfter(100_000);
import { createCompactFunction } from "agents/experimental/memory/utils/compaction-helpers";

const session = Session.create(this)
	.withContext("memory", { maxTokens: 1100 })
	.onCompaction(
		createCompactFunction({
			summarize: (prompt) =>
				generateText({ model: myModel, prompt }).then((r) => r.text),
			protectHead: 3,
			tailTokenBudget: 20000,
			minTailMessages: 2,
			tokenCounter: async (messages) => estimateWithYourTokenizer({ messages }),
		}),
	)
	.compactAfter(100_000);

Jak funguje komprimace

  1. Ochrana začátku : prvních N zpráv se nikdy nekomprimuje (výchozí hodnota 3)
  2. Ochrana konce : prochází zpětně od konce a shromažďuje tokeny až do stanoveného rozpočtu (výchozí hodnota 20K tokenů)
  3. Zarovnání hranic : posouvá hranice, aby nedošlo k rozdělení párů volání a výsledku nástroje
  4. Shrnutí středu : odešle prostřední část do LLM ve strukturovaném formátu (téma, klíčové body, aktuální stav, otevřené položky)
  5. Překryv úložiště : uloženo v assistant_compactions tabulka, klíčovaná podle fromMessageId a toMessageId
  6. Iterativní : při dalších komprimacích se stávající souhrn předá LLM k aktualizaci, nikoli k nahrazení

Když getHistory() se volá, transparentně se použijí překryvy komprese: komprimovaný rozsah se nahradí syntetickou souhrnnou zprávou.

Ruční komprimace

const result = await session.compact();

// Or manage overlays directly
await session.addCompaction("Summary of messages 1-50", "msg-1", "msg-50");
const overlays = await session.getCompactions();
const result = await session.compact();

// Or manage overlays directly
await session.addCompaction("Summary of messages 1-50", "msg-1", "msg-50");
const overlays = await session.getCompactions();

Automatická komprimace

Když .compactAfter(threshold) je nastaveno, appendMessage() po každém zápisu zkontroluje odhadovaný počet tokenů. Pokud překročí prahovou hodnotu, compact() se volá automaticky. Selhání automatické komprese není kritické: zpráva je již uložena.

Ve výchozím nastavení odhad zahrnuje uložené části zpráv a zamrzlý systémový prompt spravovaný Session, takže kontextové bloky a uložené prompty spravované Session se započítávají do prahové hodnoty. Nezahrnuje doplňky promptu specifické pro framework ani serializaci schématu nástroje, ke které dochází mimo Session.

Počítání tokenů zahrnuje dvě rozhodnutí:

Obvykle stačí nakonfigurovat jen jeden čítač. .compactAfter() počítadlo také přechází do createCompactFunction: procházení hranice (přes CompactContext) pokud není zadáno žádné explicitní createCompactFunction({ tokenCounter }) je zadán, takže jediný čítač určuje jak „máme komprimovat?“, tak „co máme komprimovat?“.

Použijte vlastní počítadlo, pokud máte využití hlášené modelem nebo vlastní tokenizer:

const session = Session.create(this)
	.onCompaction(myCompactFn)
	.compactAfter(100_000, {
		tokenCounter: async ({ messages, systemPrompt, contextBlocks }) => {
			return estimateWithYourTokenizer({
				messages,
				systemPrompt,
				contextBlocks,
			});
		},
	})
	.onCompactionError((err) => {
		console.warn("Auto-compaction failed", err);
	});
const session = Session.create(this)
	.onCompaction(myCompactFn)
	.compactAfter(100_000, {
		tokenCounter: async ({ messages, systemPrompt, contextBlocks }) => {
			return estimateWithYourTokenizer({
				messages,
				systemPrompt,
				contextBlocks,
			});
		},
	})
	.onCompactionError((err) => {
		console.warn("Auto-compaction failed", err);
	});

SessionManager

SessionManager je registr pro více pojmenovaných relací v rámci jednoho Durable Object. Poskytuje správu životního cyklu, pomocné metody a vyhledávání napříč relacemi.

Vytvoření SessionManager

import { SessionManager } from "agents/experimental/memory/session";

const manager = SessionManager.create(this)
	.withContext("soul", { provider: { get: async () => "You are helpful." } })
	.withContext("memory", { description: "Learned facts", maxTokens: 1100 })
	.withCachedPrompt()
	.onCompaction(myCompactFn)
	.compactAfter(100_000)
	.withSearchableHistory("history");
import { SessionManager } from "agents/experimental/memory/session";

const manager = SessionManager.create(this)
	.withContext("soul", { provider: { get: async () => "You are helpful." } })
	.withContext("memory", { description: "Learned facts", maxTokens: 1100 })
	.withCachedPrompt()
	.onCompaction(myCompactFn)
	.compactAfter(100_000)
	.withSearchableHistory("history");

Nastavení kontextových bloků, prompt cachingu a komprimace se propaguje do všech relací vytvořených prostřednictvím manageru. Klíče providerů se automaticky rozdělují do jmenných prostorů podle ID relace.

Metody builderu

Metoda Popis
SessionManager.create(agent) Statická továrna.
.withContext(label, options?) Přidejte šablonu kontextového bloku pro všechny relace.
.withCachedPrompt(provider?) Povolte perzistenci promptu pro všechny relace.
.onCompaction(fn) Zaregistrujte kompaktovací funkci pro všechny relace.
.compactAfter(tokenThreshold, options?) Práh automatické komprimace pro všechny relace. Podporuje stejné tokenCounter možnost jako Session.
.onCompactionError(handler) Zpracovávejte chyby automatické komprimace pro spravované relace.
.withSearchableHistory(label) Přidejte prohledávatelný blok historie napříč relacemi. Model může vyhledávat v minulých konverzacích z libovolné relace.

Životní cyklus relace

// Create a new session
const info = await manager.create("My Chat");

// Create with metadata
const info2 = await manager.create("My Chat", {
	parentSessionId: "parent-id",
	model: "claude-sonnet-4-20250514",
	source: "web",
});

// Get session metadata (null if not found)
const session = await manager.get(sessionId);

// List all sessions (ordered by updated_at DESC)
const sessions = await manager.list();

// Rename
await manager.rename(sessionId, "New Name");

// Delete (clears messages too)
await manager.delete(sessionId);
// Create a new session
const info = await manager.create("My Chat");

// Create with metadata
const info2 = await manager.create("My Chat", {
	parentSessionId: "parent-id",
	model: "claude-sonnet-4-20250514",
	source: "web",
});

// Get session metadata (null if not found)
const session = await manager.get(sessionId);

// List all sessions (ordered by updated_at DESC)
const sessions = await manager.list();

// Rename
await manager.rename(sessionId, "New Name");

// Delete (clears messages too)
await manager.delete(sessionId);

Přístup k relacím

// Get or create the Session instance for an ID
// Lazy — creates on first access, caches for subsequent calls
const session = manager.getSession(sessionId);
// Get or create the Session instance for an ID
// Lazy — creates on first access, caches for subsequent calls
const session = manager.getSession(sessionId);

Pomocné metody pro zprávy

Tyto metody delegují na podkladovou Session a aktualizují u relace updated_at časové razítko:

// Append a single message
await manager.append(sessionId, message, parentId);

// Add or update (upsert)
await manager.upsert(sessionId, message, parentId);

// Batch append (auto-chains parent IDs)
await manager.appendAll(sessionId, messages, parentId);

// Read history
const history = await manager.getHistory(sessionId, leafId);

// Message count
const count = await manager.getMessageCount(sessionId);

// Clear messages
await manager.clearMessages(sessionId);

// Delete specific messages
await manager.deleteMessages(sessionId, ["msg-1"]);
// Append a single message
await manager.append(sessionId, message, parentId);

// Add or update (upsert)
await manager.upsert(sessionId, message, parentId);

// Batch append (auto-chains parent IDs)
await manager.appendAll(sessionId, messages, parentId);

// Read history
const history = await manager.getHistory(sessionId, leafId);

// Message count
const count = await manager.getMessageCount(sessionId);

// Clear messages
await manager.clearMessages(sessionId);

// Delete specific messages
await manager.deleteMessages(sessionId, ["msg-1"]);

Rozvětvení

Rozvětvení relace v konkrétní zprávě zkopíruje historii až po daný bod do nové relace:

const forked = await manager.fork(sessionId, atMessageId, "Forked Chat");
// forked.parent_session_id === sessionId
const forked = await manager.fork(sessionId, atMessageId, "Forked Chat");
// forked.parent_session_id === sessionId

Pomocné funkce pro komprimaci

// Add a compaction overlay
await manager.addCompaction(sessionId, summary, fromId, toId);

// Get overlays
const compactions = await manager.getCompactions(sessionId);

// Compact and split — marks old session as ended, creates a continuation
const continuation = await manager.compactAndSplit(
	sessionId,
	summary,
	"Continued Chat",
);
// Add a compaction overlay
await manager.addCompaction(sessionId, summary, fromId, toId);

// Get overlays
const compactions = await manager.getCompactions(sessionId);

// Compact and split — marks old session as ended, creates a continuation
const continuation = await manager.compactAndSplit(
	sessionId,
	summary,
	"Continued Chat",
);

compactAndSplit() vytvoří novou relaci se souhrnnou zprávou místo překrytí na místě. Původní relace se označí end_reason: "compaction".

Sledování využití

await manager.addUsage(sessionId, inputTokens, outputTokens, cost);
await manager.addUsage(sessionId, inputTokens, outputTokens, cost);
// Search across all sessions (FTS5)
const results = await manager.search("deployment Friday", { limit: 20 });

// Get tools for the model (includes session_search)
const tools = await manager.tools();
// Search across all sessions (FTS5)
const results = await manager.search("deployment Friday", { limit: 20 });

// Get tools for the model (includes session_search)
const tools = await manager.tools();

Vlastní poskytovatelé

Implementujte libovolné ze čtyř rozhraní poskytovatele, abyste mohli připojit vlastní úložiště:

// Read-only context
const myProvider = {
	get: async () => "Static content here",
};

// Writable context (enables set_context tool)
const myWritable = {
	get: async () => fetchFromMyDB(),
	set: async (content) => saveToMyDB(content),
};

// Skill provider (enables load_context tool)
const mySkills = {
	get: async () => "- api-ref: API Reference\n- guide: User Guide",
	load: async (key) => fetchDocument(key),
	set: async (key, content, description) =>
		saveDocument(key, content, description),
};

// Search provider (enables search_context tool)
const mySearch = {
	get: async () => "42 entries indexed",
	search: async (query) => searchMyIndex(query),
	set: async (key, content) => indexContent(key, content),
};
// Read-only context
const myProvider: ContextProvider = {
	get: async () => "Static content here",
};

// Writable context (enables set_context tool)
const myWritable: WritableContextProvider = {
	get: async () => fetchFromMyDB(),
	set: async (content) => saveToMyDB(content),
};

// Skill provider (enables load_context tool)
const mySkills: SkillProvider = {
	get: async () => "- api-ref: API Reference\n- guide: User Guide",
	load: async (key) => fetchDocument(key),
	set: async (key, content, description) =>
		saveDocument(key, content, description),
};

// Search provider (enables search_context tool)
const mySearch: SearchProvider = {
	get: async () => "42 entries indexed",
	search: async (query) => searchMyIndex(query),
	set: async (key, content) => indexContent(key, content),
};

Můžete také implementovat SessionProvider k úplnému nahrazení úložiště SQLite:

const myStorage = {
	async getMessage(id) {
		/* ... */
	},
	async getHistory(leafId) {
		/* ... */
	},
	async getLatestLeaf() {
		/* ... */
	},
	async getBranches(messageId) {
		/* ... */
	},
	async getPathLength(leafId) {
		/* ... */
	},
	async appendMessage(message, parentId) {
		/* ... */
	},
	async updateMessage(message) {
		/* ... */
	},
	async deleteMessages(messageIds) {
		/* ... */
	},
	async clearMessages() {
		/* ... */
	},
	async addCompaction(summary, fromId, toId) {
		/* ... */
	},
	async getCompactions() {
		/* ... */
	},
	async searchMessages(query, limit) {
		/* ... */
	},
};
const myStorage: SessionProvider = {
	async getMessage(id) {
		/* ... */
	},
	async getHistory(leafId?) {
		/* ... */
	},
	async getLatestLeaf() {
		/* ... */
	},
	async getBranches(messageId) {
		/* ... */
	},
	async getPathLength(leafId?) {
		/* ... */
	},
	async appendMessage(message, parentId?) {
		/* ... */
	},
	async updateMessage(message) {
		/* ... */
	},
	async deleteMessages(messageIds) {
		/* ... */
	},
	async clearMessages() {
		/* ... */
	},
	async addCompaction(summary, fromId, toId) {
		/* ... */
	},
	async getCompactions() {
		/* ... */
	},
	async searchMessages(query, limit) {
		/* ... */
	},
};

Poskytovatelé Postgres

Ve výchozím nastavení používá úložiště Session Durable Object SQLite a tabulky vytváří líně. Pokud potřebujete data relace v externí databázi Postgres pro dotazy napříč agenty, analýzu nebo sdílené úložiště, použijte PostgresSessionProvider, PostgresContextProvider, a PostgresSearchProvider.

Tito poskytovatelé pracují s databázemi kompatibilními s Postgres prostřednictvím Hyperdrive pro sdružování připojení.

1. Vytvořte konfiguraci Hyperdrive

Vytvořte konfiguraci Hyperdrive pro svou databázi Postgres:

npx wrangler hyperdrive create my-session-db \
	--connection-string="postgresql://user:password@host:port/dbname"

Poté přidejte binding Hyperdrive do wrangler.jsonc:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "compatibility_flags": [
    "nodejs_compat"
  ],
  "hyperdrive": [
    {
      "binding": "HYPERDRIVE",
      "id": "<your-hyperdrive-id>"
    }
  ],
  "placement": {
    "mode": "smart"
  }
}
compatibility_flags = ["nodejs_compat"]

[[hyperdrive]]
binding = "HYPERDRIVE"
id = "<your-hyperdrive-id>"

[placement]
mode = "smart"

Pokud znáte region své databáze, nastavte umístění blízko databáze, abyste snížili latenci dotazů.

2. Vytvořte tabulky

Uživatel Postgres nemusí mít oprávnění vytvářet tabulky za běhu. Spusťte schéma jednou v konzoli databáze:

CREATE TABLE IF NOT EXISTS assistant_messages (
	id TEXT NOT NULL,
	session_id TEXT NOT NULL DEFAULT '',
	parent_id TEXT,
	role TEXT NOT NULL,
	content TEXT NOT NULL,
	text_content TEXT NOT NULL DEFAULT '',
	created_at TIMESTAMPTZ DEFAULT NOW(),
	content_tsv TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', text_content)) STORED,
	PRIMARY KEY (session_id, id)
);

CREATE INDEX IF NOT EXISTS idx_assistant_msg_parent
	ON assistant_messages (parent_id);
CREATE INDEX IF NOT EXISTS idx_assistant_msg_session
	ON assistant_messages (session_id);
CREATE INDEX IF NOT EXISTS idx_assistant_msg_fts
	ON assistant_messages USING GIN (content_tsv);

CREATE TABLE IF NOT EXISTS assistant_compactions (
	id TEXT PRIMARY KEY,
	session_id TEXT NOT NULL DEFAULT '',
	summary TEXT NOT NULL,
	from_message_id TEXT NOT NULL,
	to_message_id TEXT NOT NULL,
	created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS cf_agents_context_blocks (
	label TEXT PRIMARY KEY,
	content TEXT NOT NULL,
	updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS cf_agents_search_entries (
	label TEXT NOT NULL,
	key TEXT NOT NULL,
	content TEXT NOT NULL,
	content_tsv TSVECTOR GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
	created_at TIMESTAMPTZ DEFAULT NOW(),
	updated_at TIMESTAMPTZ DEFAULT NOW(),
	PRIMARY KEY (label, key)
);

CREATE INDEX IF NOT EXISTS idx_search_entries_fts
	ON cf_agents_search_entries USING GIN (content_tsv);

3. Propojte to

Nainstalujte pg, poté z Hyperdrive connection stringu vytvořte klienta a předejte ho poskytovatelům Postgres:

npm i pg
import { Agent } from "agents";
import {
	PostgresContextProvider,
	PostgresSearchProvider,
	PostgresSessionProvider,
	Session,
} from "agents/experimental/memory/session";
import { Client } from "pg";

export class MyAgent extends Agent {
	session;
	pgClient;

	async onStart() {
		const client = new Client({
			connectionString: this.env.HYPERDRIVE.connectionString,
		});
		await client.connect();
		this.pgClient = client;

		const sessionId = this.ctx.id.toString();
		this.session = Session.create(
			new PostgresSessionProvider(client, sessionId),
		)
			.withContext("soul", {
				provider: {
					get: async () => "You are a helpful assistant.",
				},
			})
			.withContext("memory", {
				description: "Short facts",
				maxTokens: 1100,
				provider: new PostgresContextProvider(client, `memory_${sessionId}`),
			})
			.withContext("knowledge", {
				description: "Searchable knowledge base",
				provider: new PostgresSearchProvider(client),
			})
			.withCachedPrompt(
				new PostgresContextProvider(client, `_prompt_${sessionId}`),
			);
	}
}
import { Agent } from "agents";
import {
	PostgresContextProvider,
	PostgresSearchProvider,
	PostgresSessionProvider,
	Session,
} from "agents/experimental/memory/session";
import { Client } from "pg";

export class MyAgent extends Agent<Env> {
	private session?: Session;
	private pgClient?: Client;

	async onStart(): Promise<void> {
		const client = new Client({
			connectionString: this.env.HYPERDRIVE.connectionString,
		});
		await client.connect();
		this.pgClient = client;

		const sessionId = this.ctx.id.toString();
		this.session = Session.create(
			new PostgresSessionProvider(client, sessionId),
		)
			.withContext("soul", {
				provider: {
					get: async () => "You are a helpful assistant.",
				},
			})
			.withContext("memory", {
				description: "Short facts",
				maxTokens: 1100,
				provider: new PostgresContextProvider(client, `memory_${sessionId}`),
			})
			.withContext("knowledge", {
				description: "Searchable knowledge base",
				provider: new PostgresSearchProvider(client),
			})
			.withCachedPrompt(
				new PostgresContextProvider(client, `_prompt_${sessionId}`),
			);
	}
}

Rozdíly v chování

Když Session.create() přijímá SessionProvider místo poskytovatele podloženého SQLite přeskočí automatické propojení SQLite:

Životní cyklus systémového promptu

freezeSystemPrompt() vrátí uložený prompt z úložiště. Při prvním volání načte bloky kontextu od poskytovatelů, prompt vykreslí a uloží. Další volání vrátí uloženou hodnotu bez opětovného vykreslení.

Použijte refreshSystemPrompt() k vynucenému opětovnému načtení bloků kontextu, opětovnému vykreslení promptu a aktualizaci uložené hodnoty.

Tabulky úložiště

Ve výchozím nastavení je úložiště v Durable Object SQLite a tabulky se vytvářejí líně při prvním použití. Relace podporované Postgresem používají externí tabulky uvedené v části Poskytovatelé Postgres.

Tabulka Účel
assistant_messages Stromově strukturované zprávy s id, session_id, parent_id, role, content (JSON), created_at
assistant_compactions Překryvy komprimace s summary, from_message_id, to_message_id
assistant_fts Virtuální tabulka FTS5 pro vyhledávání zpráv (porterovské stemování, unicode tokenizace)
assistant_sessions Registr Session (pouze SessionManager) s name, parent_session_id, model, source, počítadla tokenů/nákladů
cf_agents_context_blocks Trvalé úložiště bloků kontextu (AgentContextProvider)
cf_agents_search_entries / cf_agents_search_fts Prohledávatelné položky kontextu a index FTS5 (AgentSearchProvider)

Poděkování