Agent Memory
Purpose and Scope
Agent memory is the practice of saving useful information from one interaction and reintroducing it when a later model call needs that context. In the AI SDK agent documentation, memory is framed as what prevents every conversation from starting fresh: the agent can remember previous interactions, adapt to a user, and build context over time. This page focuses on the public implementation patterns described by the agent memory docs, and on the prompt boundary where remembered facts become model input rather than hidden state. Sources: content/docs/03-agents/06-memory.mdx, content/docs/02-foundations/03-prompts.mdx
The main design choice is where memory lives and who decides when it is read or written. A provider-defined tool lets a model use a structured memory interface while your application supplies storage. A memory provider hides storage and retrieval behind a provider integration. A custom tool gives you full control over what is stored, how it is indexed, and when it is injected. These approaches all affect prompts because the model only acts on remembered information after it is exposed through instructions, messages, tools, or provider behavior. Sources: content/docs/03-agents/06-memory.mdx, content/docs/02-foundations/03-prompts.mdx
Relevant Source Files
- content/docs/03-agents/06-memory.mdx - Defines the memory page, the three supported approaches, the Anthropic memory tool example, and the Letta memory-provider example.
- content/docs/02-foundations/03-prompts.mdx - Defines text prompts, system instructions, message prompts, and the warning about system messages in message histories.
- packages/ai/src/ui/convert-to-model-messages.test.ts - Exercises the UI-to-model message conversion boundary that matters when persisted chat state or recalled memory is converted into model-facing messages.
Core Primitives
The memory documentation names three approaches with different tradeoffs: provider-defined tools, memory providers, and a custom tool. Provider-defined tools are low-effort and can perform well because the model has been trained to use the provider’s schema, but they create provider lock-in. Memory providers are also low-effort because the external service handles storage, retrieval, and injection. A custom tool is the highest-effort option, but it avoids provider lock-in and lets the application define its own persistence backend, ranking rules, privacy filters, and update policy. Sources: content/docs/03-agents/06-memory.mdx
Prompts are the other required primitive. The foundations docs define prompt input as text prompts, system instructions, and message prompts. System instructions belong in the instructions property, while conversational history belongs in messages. That distinction matters for memory: stable behavioral rules should not be mixed with user-editable history, and recalled facts should be placed deliberately where the model can use them without letting a user overwrite the system policy. The prompts docs also warn that allowing system messages inside message histories can create prompt-injection risk.
Sources: content/docs/02-foundations/03-prompts.mdx
System-to-Code Mapping
| Concept | Public shape | Source anchor |
|---|---|---|
| Memory documentation | Describes persistent memory and three implementation approaches | content/docs/03-agents/06-memory.mdx |
| Provider-defined memory | Anthropic memory tool with an execute callback and structured commands | content/docs/03-agents/06-memory.mdx |
| Memory provider | Letta provider configured through model and providerOptions | content/docs/03-agents/06-memory.mdx |
| Prompt injection boundary | instructions, prompt, messages, and allowSystemInMessages guidance | content/docs/02-foundations/03-prompts.mdx |
| UI history conversion | Tests for converting UI messages to model messages | packages/ai/src/ui/convert-to-model-messages.test.ts |
The Anthropic example shows the provider-defined-tool pattern in concrete terms. The application imports the Anthropic provider, creates anthropic.tools.memory_20250818, and supplies an execute function. The action passed into the function contains a command, a path, and additional command-specific fields. The memory docs list commands such as view, create, string replacement, insertion, deletion, and renaming, all scoped to a memories directory. The SDK supplies the agent loop and model call; your code maps those structured actions to a filesystem, database, object store, or other persistence layer.
Sources: content/docs/03-agents/06-memory.mdx
import { anthropic } from '@ai-sdk/anthropic';
import { ToolLoopAgent } from 'ai';
const memory = anthropic.tools.memory_20250818({
execute: async action => {
// Map the action command and path to your storage backend.
return 'stored';
},
});
const agent = new ToolLoopAgent({
model: 'anthropic/claude-haiku-4.5',
tools: { memory },
});Execution Flow
A typical memory-enabled turn starts with the application choosing the memory strategy before the user prompt is sent. With a provider-defined tool, the model may decide to call the memory tool during the loop, and the application persists or retrieves data through the supplied executor. With a memory provider, the provider integration is responsible for memory management around the call. With a custom tool, your agent prompt and tool descriptions must teach the model when to save, search, update, and ignore memory, because the tool contract is application-defined. Sources: content/docs/03-agents/06-memory.mdx
The Letta example illustrates the memory-provider flow. The application installs the Letta provider package, imports lettaCloud, and constructs a ToolLoopAgent whose model is the Letta provider instance. The agent identifier is passed under provider options, so the external Letta runtime can apply its own long-term memory model. The memory docs describe Letta as handling core memory, archival memory, and recall. This pattern is useful when you want memory behavior without designing storage schemas or retrieval heuristics inside your own agent loop.
Sources: content/docs/03-agents/06-memory.mdx
import { lettaCloud } from '@letta-ai/vercel-ai-sdk-provider';
import { ToolLoopAgent } from 'ai';
const agent = new ToolLoopAgent({
model: lettaCloud(),
providerOptions: {
letta: {
agent: { id: 'your-agent-id' },
},
},
});Prompt and Message Boundaries
Memory is not useful until it becomes model context. The prompt foundations page explains that text prompts are simple strings, system prompts are initial behavior instructions set with instructions, and message prompts are arrays of user, assistant, and tool messages. For agents, this means durable policy should usually be instructions, recent conversation should be messages, and recalled memory should be inserted in a controlled form. If remembered information is appended as ordinary user text, the model may treat it differently than if it is provided as a tool result or carefully authored context section.
Sources: content/docs/02-foundations/03-prompts.mdx
The UI message conversion boundary is important for chat applications. A user interface often stores rich UI messages, while the model call needs model messages. The requested test path for converting UI messages to model messages signals that this boundary is covered by repository tests. When memory is derived from persisted chats, keep the conversion step explicit: decide which messages are retained, which tool results are safe to replay, and which recalled facts should be summarized. That prevents memory from becoming an accidental dump of untrusted or irrelevant prior state. Sources: packages/ai/src/ui/convert-to-model-messages.test.ts, content/docs/02-foundations/03-prompts.mdx
Choosing an Approach
Use provider-defined memory when you already target a provider whose model understands a built-in memory interface and you want quick integration. Use a memory provider when the external runtime’s memory system is the product you want, especially if it already manages recall and archival storage. Use a custom tool when memory is part of your domain model, requires strict governance, or must work consistently across providers. The memory docs present this as a tradeoff among effort, flexibility, and provider lock-in, so choose the simplest approach that still satisfies your data and portability requirements. Sources: content/docs/03-agents/06-memory.mdx
Before shipping memory, define retention and recall rules as deliberately as you define tool execution. Decide what can be stored, who can delete it, how stale memories are handled, and whether sensitive facts require confirmation before persistence. Then test complete turns rather than only storage functions: a good memory implementation proves that the agent saves the right information, retrieves it at the right time, and injects it through the correct prompt or message channel. Next, read the tool-calling, prompts, and UI message pages to align memory with the rest of the agent loop. Sources: content/docs/03-agents/06-memory.mdx, content/docs/02-foundations/03-prompts.mdx, packages/ai/src/ui/convert-to-model-messages.test.ts