Conversation state and memory·续

async load(key) { const row = await this.db .prepare("SELECT content FROM skills WHERE key = ?") .bind(key) .first(); return row ? row.content : null; }

async set(key, content, description) { await this.db .prepare( "INSERT INTO skills (key, content, description) VALUES (?, ?, ?) " + "ON CONFLICT(key) DO UPDATE SET content = ?, description = ?", ) .bind(key, content, description ?? null, content, description ?? null) .run(); } }


```ts
import type { SkillProvider } from "agents/experimental/memory/session";

class DatabaseSkillProvider implements SkillProvider {
    private db: D1Database;

constructor(db: D1Database) {
        this.db = db;
    }

async get(): Promise<string | null> {
        const rows = await this.db
            .prepare("SELECT key, description FROM skills ORDER BY key")
            .all();
        if (rows.results.length === 0) return null;
        return rows.results
            .map((r) => `- ${r.key}${r.description ? `: ${r.description}` : ""}`)
            .join("\n");
    }

async load(key: string): Promise<string | null> {
        const row = await this.db
            .prepare("SELECT content FROM skills WHERE key = ?")
            .bind(key)
            .first();
        return row ? (row.content as string) : null;
    }

async set(key: string, content: string, description?: string): Promise<void> {
        await this.db
            .prepare(
                "INSERT INTO skills (key, content, description) VALUES (?, ?, ?) " +
                    "ON CONFLICT(key) DO UPDATE SET content = ?, description = ?",
            )
            .bind(key, content, description ?? null, content, description ?? null)
            .run();
    }
}

The Session detects the load() method via duck-typing and generates the appropriate tools automatically.

Skills vs other memory types

Aspect Skills Writable context Searchable context
In system prompt Metadata listing only Full content Summary count
Access pattern Load whole document by key Always visible Search by query
Best for Large documents, reference material Short notes, preferences Large collections of small entries
Context cost Low (until loaded) Proportional to content Low (until searched)
Agent writes? Optional (if set implemented) Yes (via set_context) Yes (via set_context)

The key distinction: skills are lazy. They cost nearly nothing in the system prompt until the agent decides it needs one. This makes them ideal for large reference material where only a subset is relevant to any given conversation.

How agents interact with memory

The Session automatically generates tools based on the provider types of your context blocks. You pass these tools to your LLM alongside your own application-specific tools:

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

const result = streamText({
    model: myModel,
    system: await session.freezeSystemPrompt(),
    messages: await convertToModelMessages(await session.getHistory()),
    tools: allTools,
});
const sessionTools = await session.tools();
const allTools = { ...sessionTools, ...myApplicationTools };

const result = streamText({
    model: myModel,
    system: await session.freezeSystemPrompt(),
    messages: await convertToModelMessages(await session.getHistory()),
    tools: allTools,
});

Generated tools

The Session generates tools dynamically based on what provider types are present:

Tool Generated when What it does
set_context Any writable, skill, or search block exists Writes content to a named block. For writable blocks, replaces or appends. For skill/search blocks, writes a keyed entry.
load_context Any skill block exists Loads full content of a document by key into the agent's context.
unload_context Any skill block exists Frees context space by removing a previously loaded document. The document remains available for re-loading.
search_context Any search block exists Full-text search within a searchable block. Returns the top results ranked by relevance.
session_search Using SessionManager Searches across all sessions (cross-conversation search).

The tools include descriptions and parameter schemas that tell the LLM which blocks are available and what they are for. The agent decides when and how to use them based on the conversation.

For the full tool signatures and all Session methods, refer to the Session API reference.

The system prompt

Context blocks are assembled into a structured system prompt with clear headers and metadata. Each block gets a labeled section with tags indicating its type and capacity:

══════════════════════════════════════════════
SOUL (Identity) [readonly]
══════════════════════════════════════════════
You are a helpful coding assistant who speaks concisely.

══════════════════════════════════════════════
MEMORY (Important facts) [45% — 495/1100 tokens] [writable]
══════════════════════════════════════════════
User prefers dark mode.
User's project uses React and TypeScript.

══════════════════════════════════════════════
KNOWLEDGE (Searchable knowledge base) [searchable]
══════════════════════════════════════════════
12 entries indexed.

══════════════════════════════════════════════
SKILLS [loadable]
══════════════════════════════════════════════
- api-ref: API Reference documentation
- style-guide: Company style guide

The tags ([readonly], [writable], [searchable], [loadable]) tell the LLM what kind of interaction is possible with each block. Token budgets show the agent how much space remains in writable blocks, helping it manage its own memory.

Gotchas

Prompt caching

LLM providers (Anthropic, OpenAI, and others) cache the system prompt prefix. When consecutive requests share the same system prompt, the provider can skip re-processing that prefix, reducing both latency and cost. Breaking the cache (by changing the system prompt) throws away this benefit.

The Session API is designed to work with prompt caching:

  • freezeSystemPrompt() renders the system prompt from all context blocks on the first call, then returns the cached value on subsequent calls. The prompt does not change between turns, even when the agent writes to memory via set_context.
  • withCachedPrompt() persists the frozen prompt to storage, so it survives Durable Object hibernation and eviction. When the agent wakes up, it loads the same prompt without re-fetching from all providers.

When the agent uses set_context to update a writable block, the underlying provider is updated immediately (the data is saved), but the frozen system prompt is not re-rendered. The LLM sees the update on its next turn only if you explicitly call refreshSystemPrompt(), which you typically do between conversation turns, not mid-turn.

This means the system prompt stays stable throughout a multi-step tool-use turn, preserving the provider's prefix cache across every step.

const session = Session.create(this)
    .withContext("soul", {
        provider: { get: async () => "You are a helpful assistant." },
    })
    .withContext("memory", { description: "Learned facts", maxTokens: 1100 })
    .withCachedPrompt(); // Persist the frozen prompt across hibernation

// During a conversation turn:
const system = await session.freezeSystemPrompt(); // Same value every call
const tools = await session.tools();

// ... agent calls set_context to update memory ...
// The frozen prompt is NOT changed, prefix cache stays warm

// Between turns (optional, if you want the agent to see its own updates):
await session.refreshSystemPrompt();
const session = Session.create(this)
    .withContext("soul", {
        provider: { get: async () => "You are a helpful assistant." },
    })
    .withContext("memory", { description: "Learned facts", maxTokens: 1100 })
    .withCachedPrompt(); // Persist the frozen prompt across hibernation

// During a conversation turn:
const system = await session.freezeSystemPrompt(); // Same value every call
const tools = await session.tools();

// ... agent calls set_context to update memory ...
// The frozen prompt is NOT changed, prefix cache stays warm

// Between turns (optional, if you want the agent to see its own updates):
await session.refreshSystemPrompt();

Compaction

Long conversations eventually exceed the LLM's context window. Compaction addresses this at two levels: macro-compaction summarizes ranges of older messages, and micro-compaction truncates individual messages that are too large.

Macro-compaction

Macro-compaction summarizes older messages, but it never deletes the originals.

It uses overlays: a summary is stored in a separate table, keyed by the message range it covers. When getHistory() is called, overlays are applied transparently at read time. The compacted range is replaced by a synthetic summary message. The underlying messages remain in SQLite, preserving the full conversation for audit, search, and branching.

Messages:  [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]
                    ↓ compaction ↓
Overlay:   [1] [2] [SUMMARY of 3-7]           [8] [9] [10]
                                                ↑ tail protected

The key points:

  • Non-destructive, original messages are never deleted. The full conversation is always available in the database.
  • Iterative, when the conversation grows again and triggers another compaction, the existing summary is passed to the LLM to update, not replaced from scratch.
  • Boundary-aware, compaction boundaries are shifted to avoid splitting tool call / tool result pairs.
  • Configurable, protectHead preserves the first N messages (usually the system context), and tailTokenBudget keeps the most recent messages intact.
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,
        }),
    )
    .compactAfter(100_000); // Auto-compact when token estimate exceeds threshold
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,
        }),
    )
    .compactAfter(100_000); // Auto-compact when token estimate exceeds threshold

Auto-compaction triggers after appendMessage() when the estimated token count exceeds the threshold. Compaction failure is non-fatal, the message is already saved.

Micro-compaction

Micro-compaction works at the individual message level rather than across ranges. It handles two problems:

Read-time truncation: truncateOlderMessages() shortens tool outputs and long text in older messages before sending them to the LLM. Recent messages (last 4 by default) are kept intact. This operates on a copy, stored messages are not mutated.

import { truncateOlderMessages } from "agents/experimental/memory/utils";

const history = await session.getHistory();
const truncated = truncateOlderMessages(history);
// Pass truncated history to the LLM
import { truncateOlderMessages } from "agents/experimental/memory/utils";

const history = await session.getHistory();
const truncated = truncateOlderMessages(history);
// Pass truncated history to the LLM

Row size enforcement: when a message is persisted (typically an assistant message with large tool outputs), it is checked against the SQLite row size limit. Oversized tool outputs are replaced with a preview and a note suggesting the tool be re-run. This prevents individual messages from exceeding storage limits while preserving the conversation flow.

Session API reference

Full API reference for Session, covering messages, context blocks, compaction, search, tools, and custom providers.

Store and sync state

setState() for simpler key-value persistence and real-time sync.

Think

Opinionated chat agent with built-in Session integration via configureSession().

Was this helpful?

YesNo

On this page

Conversation state and memory·续 - 图1Docs

{"@context":"https://schema.org","@type":"TechArticle","@id":"https://developers.cloudflare.com/agents/concepts/conversation-state-and-memory/#page","headline":"Conversation state and memory","description":"How agents store and recall information, including read-only context, writable short-form memory, searchable knowledge, and on-demand skills.","url":"https://developers.cloudflare.com/agents/concepts/conversation-state-and-memory/","inLanguage":"en","image":"https://developers.cloudflare.com/agents/concepts/conversation-state-and-memory/og.png?v=52ce156c479b4e08","dateModified":"2026-06-03","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/"},"keywords":["AI"]}


description: Understand how tools are exposed to models, where tools execute, and how MCP connects external tools to Cloudflare Agents. title: Tools image: https://developers.cloudflare.com/agents/concepts/tools/og.png?v=95e31e7f8e5f6bfc


Skip to content

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