← Vytvářejte agenty na Cloudflare / agents / runtime / lifecycle
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
Builder API (doporučeno)
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.
Hledat
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.
session_search
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
- Ochrana začátku : prvních N zpráv se nikdy nekomprimuje (výchozí hodnota 3)
- Ochrana konce : prochází zpětně od konce a shromažďuje tokeny až do stanoveného rozpočtu (výchozí hodnota 20K tokenů)
- Zarovnání hranic : posouvá hranice, aby nedošlo k rozdělení párů volání a výsledku nástroje
- 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)
- Překryv úložiště : uloženo v
assistant_compactionstabulka, klíčovaná podlefromMessageIdatoMessageId - 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í:
.compactAfter(threshold, { tokenCounter })řídí když se po zápisech spouští automatická komprimace.createCompactFunction({ tokenCounter })řídí které posledních zpráv je chráněno před souhrnem. Použijte to v případech, kdy jsou historie s velkým počtem volání nástrojů mnohem větší, než dokáže odhadnout heuristika bezpečná pro Workers.
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 === sessionIdconst forked = await manager.fork(sessionId, atMessageId, "Forked Chat");
// forked.parent_session_id === sessionIdPomocné 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);Vyhledávání napříč relacemi
// 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 pgimport { 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:
- Kontextové bloky vyžadují explicitní providery. Každý
withContext()volání, které má uchovat data, potřebujeprovidermožnost. withCachedPrompt()vyžaduje explicitního poskytovatele. PředejtePostgresContextProviderk trvalému uložení zmrazeného systémového promptu.- Metody Session jsou asynchronní. Použijte
awaitpro čtení a zápis, takže stejný kód funguje jak s lokální SQLite, tak s externím úložištěm. - Podpora Broadcasteru se přeskočí. Vysílání stavu WebSocket pro události Session funguje pouze u relací podporovaných 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í
- Stromově strukturované zprávy Session jsou inspirovány Pi ↗.
- Kontextové bloky jsou inspirovány Paměťové bloky Letta AI ↗.
- Formátování bloků je inspirováno Hermes Agent ↗.
Související
- Think : účelově navržený chatovací agent, který pro ukládání konverzace využívá Session prostřednictvím
configureSession() - Chat agenti,
AIChatAgents vlastní vrstvou perzistence zpráv - Ukládání a synchronizace stavu,
setState()pro jednodušší perzistenci klíč-hodnota