Skip to main content
The MemoryBuilder provides a simplified, unified interface for managing conversation memory, working memory, and session state in ADK agents.

Overview

Unified Memory provides:
  • Simplified API - Single interface for sessions, memory, and state
  • Working memory - Maintain evolving context across conversations
  • Message format - Simple message objects instead of complex Content types
  • Session management - Create, list, and manage user sessions
  • Memory search - Search across conversation history
  • Auto-persistence - Automatically sync to memory services

Quick Start

import { MemoryBuilder } from "@iqai/adk";

// Create memory builder
const memory = MemoryBuilder.create({
  appName: "my-app",
  userId: "user-123",
  lastMessages: 10, // Keep last 10 messages in context
  workingMemory: {
    enabled: true,
    template: "Current context: none",
  },
});

// Create a session
const session = await memory.createSession();

// Add messages
await memory.addMessage(session, {
  role: "user",
  content: "Hello!",
});

await memory.addMessage(session, {
  role: "assistant",
  content: "Hi! How can I help?",
});

// Recall conversation
const messages = await memory.recall(session.id);
console.log(messages);
// [
//   { role: "user", content: "Hello!" },
//   { role: "assistant", content: "Hi! How can I help?" }
// ]

Configuration

UnifiedMemoryConfig

interface UnifiedMemoryConfig {
  /** Application name */
  appName: string;
  
  /** User ID */
  userId: string;
  
  /** Number of recent messages to include (false = all messages) */
  lastMessages?: number | false;
  
  /** Working memory configuration */
  workingMemory?: WorkingMemoryConfig;
}

interface WorkingMemoryConfig {
  /** Enable working memory */
  enabled: boolean;
  
  /** Initial template for working memory */
  template?: string;
}

Default Configuration

const defaultConfig = {
  lastMessages: 10, // Last 10 messages
  workingMemory: {
    enabled: false,
  },
};

Simple Message Format

Unified Memory uses simple message objects:
interface SimpleMessage {
  role: "user" | "assistant" | "system";
  content: string;
}
This is much simpler than ADK’s native Content type, making it easier to work with conversation history.

Session Management

Creating Sessions

// Create new session
const session = await memory.createSession();

// Create with initial state
const sessionWithState = await memory.createSession({
  userName: "Alice",
  preferences: { theme: "dark" },
});

// Create with specific ID
const specificSession = await memory.createSession(
  { userName: "Bob" },
  "session-123"
);

Retrieving Sessions

// Get specific session
const session = await memory.getSession("session-123");

if (session) {
  console.log("Session state:", session.state);
}

// List all sessions
const { sessions, cursor } = await memory.listSessions();
console.log("User has", sessions.length, "sessions");

Deleting Sessions

await memory.deleteSession("session-123");

Ending Sessions

// Mark session as ended and persist to memory
const endedSession = await memory.endSession("session-123");

Adding Messages

Simple Messages

await memory.addMessage(session, {
  role: "user",
  content: "What's the weather?",
});

await memory.addMessage(session, {
  role: "assistant",
  content: "It's sunny and 72°F.",
});

ADK Events

You can also add full ADK Event objects:
import { Event } from "@iqai/adk";

const event = new Event({
  author: "my-agent",
  content: {
    role: "model",
    parts: [{ text: "Here's my response..." }],
  },
});

await memory.addEvent(session, event);

Recalling Conversations

Recall as Simple Messages

const messages = await memory.recall("session-123");

// Returns SimpleMessage[]
messages.forEach(msg => {
  console.log(`${msg.role}: ${msg.content}`);
});

Recall as Events

const events = await memory.recallEvents("session-123");

// Returns Event[]
events.forEach(event => {
  console.log(event.author, event.text);
});
Both methods respect the lastMessages configuration.

Working Memory

Working memory is a special piece of context that evolves over time:
const memory = MemoryBuilder.create({
  appName: "my-app",
  userId: "user-123",
  workingMemory: {
    enabled: true,
    template: "User preferences: none set yet",
  },
});

const session = await memory.createSession();

// Get current working memory
const current = await memory.getWorkingMemory(session.id);
console.log(current); // "User preferences: none set yet"

// Update working memory
await memory.updateWorkingMemory(
  session,
  "User preferences: dark mode, notifications enabled"
);

// Next time it's retrieved, it has the updated value
const updated = await memory.getWorkingMemory(session.id);
console.log(updated); // "User preferences: dark mode, notifications enabled"

Use Cases for Working Memory

1

User Preferences

await memory.updateWorkingMemory(
  session,
  "User: Alice. Preferences: concise responses, code examples preferred"
);
2

Current Task Context

await memory.updateWorkingMemory(
  session,
  "Working on: Building a REST API. Stack: Node.js + Express + PostgreSQL"
);
3

Conversation Summary

await memory.updateWorkingMemory(
  session,
  "Summary: User is planning a trip to Japan in March. Interested in temples and food."
);
Search across all conversations (requires a MemoryService):
import { VertexAIMemoryService } from "@iqai/adk";

const memoryService = new VertexAIMemoryService({
  corpusName: "my-corpus",
  projectId: process.env.GCP_PROJECT_ID!,
  location: "us-central1",
});

const memory = MemoryBuilder.create({
  appName: "my-app",
  userId: "user-123",
})
  .withMemoryService(memoryService);

// Search across conversations
const results = await memory.search("trip to Japan");

results.forEach(result => {
  console.log("Found:", result.content);
  console.log("Relevance:", result.score);
});

Custom Services

Use custom session and memory services:
import { 
  PostgresSessionService,
  VertexAIMemoryService 
} from "@iqai/adk";

const memory = MemoryBuilder.create({
  appName: "my-app",
  userId: "user-123",
})
  .withSessionService(
    new PostgresSessionService({ connectionString: "..." })
  )
  .withMemoryService(
    new VertexAIMemoryService({ corpusName: "my-corpus" })
  );

Integration with Agents

While MemoryBuilder simplifies memory management, agents still use the native Session and Event types. Convert between formats:
import { AgentBuilder, MemoryBuilder } from "@iqai/adk";

const memory = MemoryBuilder.create({
  appName: "my-app",
  userId: "user-123",
  lastMessages: 5,
});

// Use the session service from MemoryBuilder
const { runner } = await AgentBuilder
  .create("assistant")
  .withModel("gpt-4")
  .withSessionService(memory.getSessionService())
  .build();

// Create or get session
const session = await memory.createSession();

// Add user message via MemoryBuilder
await memory.addMessage(session, {
  role: "user",
  content: "Hello!",
});

// Run agent (it will see the conversation history)
const events = await runner.run({
  userId: "user-123",
  sessionId: session.id,
  newMessage: { parts: [{ text: "What can you help with?" }] },
});

// Store agent response
const lastEvent = events[events.length - 1];
await memory.addEvent(session, lastEvent);

// Recall as simple messages
const messages = await memory.recall(session.id);
console.log("Conversation:", messages);

Advanced Example: Chatbot with Working Memory

import { AgentBuilder, MemoryBuilder } from "@iqai/adk";

const memory = MemoryBuilder.create({
  appName: "chatbot",
  userId: "user-123",
  lastMessages: 10,
  workingMemory: {
    enabled: true,
    template: "No context yet.",
  },
});

const { runner } = await AgentBuilder
  .create("chatbot")
  .withModel("gpt-4")
  .withSessionService(memory.getSessionService())
  .withInstruction(`
    You are a helpful assistant.
    
    Working Memory (context that persists):
    {{workingMemory}}
    
    Update working memory when you learn important information about the user.
  `)
  .build();

async function chat(sessionId: string, userMessage: string) {
  const session = await memory.getSession(sessionId)
    || await memory.createSession();
  
  // Get working memory
  const workingMemory = await memory.getWorkingMemory(session.id) || "";
  
  // Add user message
  await memory.addMessage(session, {
    role: "user",
    content: userMessage,
  });
  
  // Run agent with working memory in context
  const events = await runner.run({
    userId: "user-123",
    sessionId: session.id,
    newMessage: { 
      parts: [{ text: userMessage }] 
    },
    state: { workingMemory },
  });
  
  const response = events[events.length - 1].text || "";
  
  // Store assistant response
  await memory.addMessage(session, {
    role: "assistant",
    content: response,
  });
  
  // Check if agent wants to update working memory
  // (You could use structured output or parse the response)
  if (response.includes("[UPDATE_MEMORY:")) {
    const match = response.match(/\[UPDATE_MEMORY: (.+?)\]/);
    if (match) {
      await memory.updateWorkingMemory(session, match[1]);
    }
  }
  
  return response;
}

// Usage
await chat("session-1", "My name is Alice");
// Agent responds and updates working memory: "User name: Alice"

await chat("session-1", "I prefer dark mode");
// Agent responds and updates: "User name: Alice. Preferences: dark mode"

await chat("session-1", "What's my name?");
// Agent responds: "Your name is Alice" (from working memory)

Comparison with Standard Memory

FeatureMemoryBuilderStandard Memory
API ComplexitySimple, high-levelMore granular control
Message FormatSimpleMessageContent + Parts
Working MemoryBuilt-inManual implementation
Session ManagementSimplifiedDirect session service
Use CaseQuick prototyping, simple appsComplex memory patterns

Best Practices

Set reasonable lastMessages - Don’t load entire conversation history. Use 5-20 recent messages for context.
Update working memory strategically - Only update when important context changes (user preferences, current task, etc.).
Use memory search sparingly - Memory search can be slow. Cache results or use for specific queries only.
End sessions properly - Call endSession() to persist final state to memory services.
Combine with standard memory - MemoryBuilder is built on top of standard memory services. Mix and match as needed.

See Also

Build docs developers (and LLMs) love