Agent Memory

Working memory, long-term memory, episodic memory — how agents remember and learn

Types of Memory

AI Agents need different types of memory, just like the human brain:

Working Memory

Information right now — the agent’s current context.

const messages: Message[] = [
  { role: "system", content: systemPrompt },
  { role: "user", content: "Book a flight to Tokyo" },
  { role: "assistant", content: "I'll search flights for you..." },
  { role: "tool", content: JSON.stringify(flightResults) }
];

Limitation: Erased when session ends, limited by context window.

Short-Term Memory

Information within one conversation, allowing context understanding:

// User: "Find flights to Tokyo"
// Agent searches...
// User: "How much is it there?" ← "there" = Tokyo (short-term memory)

Long-Term Memory

Information across sessions — preferences, facts, history.

import { createClient } from "@supabase/supabase-js";

const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);

// Long-term memory schema
// create table agent_memories (
//   id uuid primary key default gen_random_uuid(),
//   user_id text not null,
//   type text not null,
//   content text not null,
//   metadata jsonb,
//   embedding vector(1536),
//   created_at timestamptz default now(),
//   last_accessed timestamptz default now()
// );

async function saveMemory(userId: string, content: string, type: string) {
  const embedding = await getEmbedding(content);

  await supabase.from("agent_memories").insert({
    user_id: userId,
    type,
    content,
    embedding,
    metadata: { source: "conversation" }
  });
}

async function recallMemories(userId: string, query: string, limit = 5) {
  const queryEmbedding = await getEmbedding(query);

  const { data } = await supabase.rpc("match_memories", {
    query_embedding: queryEmbedding,
    target_user_id: userId,
    match_count: limit
  });

  return data;
}

Persona Memory

Memory that shapes the agent’s personality:

const personaMemory = {
  role: "Senior Travel Advisor",
  expertise: ["Adventure travel", "Budget optimization", "Asian destinations"],
  communicationStyle: "Friendly, detailed, uses emojis",
  preferences: {
    alwaysSuggestLocalFood: true,
    avoidTouristTraps: true,
    prioritizeSafety: true
  }
};

Episodic Memory

Agent remembers sequences of actions in complex tasks:

interface Episode {
  taskId: string;
  steps: {
    action: string;
    result: "success" | "failure";
    duration: number;
    notes: string;
  }[];
  outcome: "completed" | "failed" | "partial";
  lessonsLearned: string[];
}

async function consultEpisodicMemory(currentTask: string) {
  const similarEpisodes = await recallMemories(userId, currentTask);

  const failedApproaches = similarEpisodes
    .filter(ep => ep.outcome === "failed")
    .map(ep => ep.lessonsLearned);

  return {
    avoid: failedApproaches,
    successful: similarEpisodes.filter(ep => ep.outcome === "completed")
  };
}

Integrating Memory Into Agents

import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const memorize = tool({
  description: "Save important information to long-term memory",
  parameters: z.object({
    content: z.string(),
    type: z.enum(["preference", "fact", "experience"]),
    importance: z.enum(["low", "medium", "high"])
  }),
  execute: async ({ content, type }) => {
    await saveMemory(userId, content, type);
    return { saved: true, type };
  }
});

const recall = tool({
  description: "Search for saved information in memory",
  parameters: z.object({
    query: z.string(),
    type: z.enum(["all", "preference", "fact", "experience"]).optional()
  }),
  execute: async ({ query }) => {
    const memories = await recallMemories(userId, query);
    return { memories };
  }
});

async function agentWithMemory(userId: string, message: string) {
  const memories = await recallMemories(userId, message);

  const result = await generateText({
    model: openai("gpt-4o"),
    system: `You are a personal assistant with long-term memory.

Memories about this user:
${memories.map(m => `- [${m.type}] ${m.content}`).join("\n")}

Use memories to personalize responses.
If the user shares important new information, save it to memory.`,
    tools: { memorize, recall, searchWeb },
    maxSteps: 5,
    prompt: message
  });

  return result.text;
}

Knowledge Agent Pattern

A specialized agent observes conversations and automatically saves knowledge:

async function knowledgeAgent(conversationHistory: Message[]) {
  const { object } = await generateObject({
    model: openai("gpt-4o-mini"),
    schema: z.object({
      valuableInsights: z.array(z.object({
        content: z.string(),
        type: z.enum(["preference", "fact", "experience"]),
        shouldSave: z.boolean(),
        reason: z.string()
      }))
    }),
    prompt: `Analyze the conversation. Find information worth saving:
- User preferences (likes, dislikes)
- Facts about the user (name, job, hobbies)
- Experiences (successful/failed tasks, lessons learned)

Conversation:
${conversationHistory.map(m => `${m.role}: ${m.content}`).join("\n")}`
  });

  for (const insight of object.valuableInsights) {
    if (insight.shouldSave) {
      await saveMemory(userId, insight.content, insight.type);
    }
  }
}

Memory Lifecycle

Input → Short-term (session) → Knowledge Agent evaluates
                                     ↓
                              Worth saving? 
                              ├── Yes → Long-term (Supabase)
                              └── No → Discard

New session → Recall relevant long-term memories → Inject into context

Summary

  • Working/Short-term: Current context, lost when session ends
  • Long-term: Stored in Supabase, persists across sessions
  • Episodic: Remembers action sequences, learns from past
  • Persona: Fixed agent personality
  • Knowledge Agent: Automatically extracts and saves important information