Code Explanation: simple-agent-with-memory.js
This example extends the simple agent with persistent memory, enabling it to remember information across sessions while intelligently avoiding duplicate saves.
Key Components
1. MemoryManager Import
import {MemoryManager} from "./memory-manager.js";Custom class for persisting agent memories to JSON files with unified memory storage.
2. Initialize Memory Manager
const memoryManager = new MemoryManager('./agent-memory.json');
const memorySummary = await memoryManager.getMemorySummary();- Loads existing memories from file
- Generates formatted summary for system prompt
- Handles migration from old memory schemas
3. Memory-Aware System Prompt with Reasoning
const systemPrompt = `
You are a helpful assistant with long-term memory.
Before calling any function, always follow this reasoning process:
1. **Compare** new user statements against existing memories below.
2. **If the same key and value already exist**, do NOT call saveMemory again.
- Instead, simply acknowledge the known information.
- Example: if the user says "My name is Malua" and memory already says "user_name: Malua", reply "Yes, I remember your name is Malua."
3. **If the user provides an updated value** (e.g., "I actually prefer sushi now"),
then call saveMemory once to update the value.
4. **Only call saveMemory for genuinely new information.**
When saving new data, call saveMemory with structured fields:
- type: "fact" or "preference"
- key: short descriptive identifier (e.g., "user_name", "favorite_food")
- value: the specific information (e.g., "Malua", "chinua")
Examples:
saveMemory({ type: "fact", key: "user_name", value: "Malua" })
saveMemory({ type: "preference", key: "favorite_food", value: "chinua" })
${memorySummary}
`;What this does:
- Includes existing memories in the prompt
- Provides explicit reasoning guidelines to prevent duplicate saves
- Teaches the agent to compare before saving
- Instructs when to update vs. acknowledge existing data
4. saveMemory Function
const saveMemory = defineChatSessionFunction({
description: "Save important information to long-term memory (user preferences, facts, personal details)",
params: {
type: "object",
properties: {
type: {
type: "string",
enum: ["fact", "preference"]
},
key: { type: "string" },
value: { type: "string" }
},
required: ["type", "key", "value"]
},
async handler({ type, key, value }) {
await memoryManager.addMemory({ type, key, value });
return `Memory saved: ${key} = ${value}`;
}
});What it does:
- Uses structured key-value format for all memories
- Saves both facts and preferences with the same method
- Automatically handles duplicates (updates if value changes)
- Persists to JSON file
- Returns confirmation message
Parameter Structure:
type: Either “fact” or “preference”key: Short identifier (e.g., “user_name”, “favorite_food”)value: The actual information (e.g., “Alex”, “pizza”)
5. Example Conversation
const prompt1 = "Hi! My name is Alex and I love pizza.";
const response1 = await session.prompt(prompt1, {functions});
// Agent calls saveMemory twice:
// - saveMemory({ type: "fact", key: "user_name", value: "Alex" })
// - saveMemory({ type: "preference", key: "favorite_food", value: "pizza" })
const prompt2 = "What's my favorite food?";
const response2 = await session.prompt(prompt2, {functions});
// Agent recalls from memory: "Pizza"How Memory Works
Flow Diagram
Session 1:
User: "My name is Alex and I love pizza"
↓
Agent calls: saveMemory({ type: "fact", key: "user_name", value: "Alex" })
Agent calls: saveMemory({ type: "preference", key: "favorite_food", value: "pizza" })
↓
Saved to: agent-memory.json
Session 2 (after restart):
1. Load memories from agent-memory.json
2. Add to system prompt
3. Agent sees: "user_name: Alex" and "favorite_food: pizza"
4. Can use this information in responses
Session 3:
User: "My name is Alex"
↓
Agent compares: user_name already = "Alex"
↓
No function call! Just acknowledges: "Yes, I remember your name is Alex."
The MemoryManager Class
Located in memory-manager.js:
class MemoryManager {
async loadMemories() // Load from JSON (handles schema migration)
async saveMemories() // Write to JSON
async addMemory() // Unified method for all memory types
async getMemorySummary() // Format memories for system prompt
extractKey() // Helper for migration
extractValue() // Helper for migration
}Benefits:
- Single unified method for all memory types
- Automatic duplicate detection and prevention
- Automatic value updates when information changes
Key Concepts
1. Structured Memory Format
All memories now use a consistent structure:
{
type: "fact" | "preference",
key: "user_name", // Identifier
value: "Alex", // The actual data
source: "user", // Where it came from
timestamp: "2025-10-29..." // When it was saved/updated
}2. Intelligent Duplicate Prevention
The agent is trained to:
- Compare before saving
- Skip if data is identical
- Update if value changed
- Acknowledge existing memories instead of re-saving
3. Persistent State
- Memories survive script restarts
- Stored in JSON file with metadata
- Loaded at startup and injected into prompt
4. Memory Integration in System Prompt
Memories are automatically formatted and injected:
=== LONG-TERM MEMORY ===
Known Facts:
- user_name: Alex
- location: Paris
User Preferences:
- favorite_food: pizza
- preferred_language: French
Why This Matters
Without memory: Agent starts fresh every time, asks same questions repeatedly
With basic memory: Agent remembers, but may save duplicates wastefully
With smart memory: Agent remembers AND avoids redundant saves by reasoning first
This enables:
- Personalized responses based on user history
- Efficient memory usage (no duplicate entries)
- Natural conversations that feel continuous
- Stateful agents that maintain context
- Automatic updates when information changes
Expected Output
First run:
User: "Hi! My name is Alex and I love pizza."
AI: "Nice to meet you, Alex! I've noted that you love pizza."
[Calls saveMemory twice - new information saved]
Second run (after restart):
User: "What's my favorite food?"
AI: "Your favorite food is pizza! You mentioned that you love it."
[No function calls - recalls from loaded memory]
Third run (duplicate statement):
User: "My name is Alex."
AI: "Yes, I remember your name is Alex!"
[No function call - recognizes duplicate, just acknowledges]
Fourth run (updated information):
User: "I actually prefer sushi now."
AI: "Got it! I've updated your favorite food to sushi."
[Calls saveMemory once - updates existing value]
Reasoning Process
The system prompt explicitly guides the agent through this decision tree:
New user statement
↓
Compare to existing memories
↓
├─→ Exact match? → Acknowledge only (no save)
├─→ Updated value? → Save to update
└─→ New information? → Save as new
This reasoning-first approach makes the agent more intelligent and efficient with memory operations!