Welcome back!

Resume reading from where you left off?

AI Memory cover image
AI/ML

AI Memory

Imagine meeting a brilliant coworker who can instantly draft a marketing strategy, debug your code, or translate ancient Greek

By Pankaj Sonani from aigrama.net • 8 min read
#architecture#ai#ml

Introduction to AI Memory: How AI Learns to Remember Across Chats

Imagine meeting a brilliant coworker who can instantly draft a marketing strategy, debug your code, or translate ancient Greek. You have a spectacular, two-hour brainstorming session. But the moment you step out of the room to grab a coffee and walk back in, they look at you with a blank stare and say, “Hello, stranger. Who are you, and what are we doing?”

That would be incredibly frustrating, right?

Yet, this is exactly how modern Artificial Intelligence naturally works. When you chat with an AI, it often feels human, warm, and deeply intuitive. But behind the curtain, base AI models are stateless. They suffer from total, permanent amnesia. Every time you press “Send,” the AI views your message with a completely blank mind. It has no natural concept of who you are, what you like, or what you discussed two minutes ago.

To fix this flaw, software engineers build AI Memory Systems. In this guide, we will break down exactly how AI learns to remember, step-by-step, using stories, pictures, and simple code you can build yourself.

For more deep dives into making complex AI technology simple, always visit aigrama.net.


The AI Memory Architecture at a Glance

Before we dive into the nuts and bolts, let’s look at how data moves through an AI system. Think of this as the digital nervous system of your AI agent:

[User Input / Prompt] 


[Short-Term State / Context Window] 


  {Memory Manager} ◄───► [Graph Memory: Connected Nodes]

       ├─► (Read / Write) ─► [Long-Term Memory Store]
       │                         ├── Episodic: What Happened
       │                         ├── Semantic: What is True
       │                         └── Procedural: How to Do It

[Memory Evals: Testing Quality]

1. Context vs. Memory

Many beginners mix up Context and Memory. While they sound identical, they serve entirely different purposes.

💡 The Desk vs. The Diary Analogy Imagine sitting at a tiny wooden desk.

  • Context is the single sheet of paper lying directly in front of you. You can see it, read it, and edit it instantly. But your desk is small; you can’t pile up infinite pages. Once you walk away from the desk (close the chat session), that paper is crumpled up and thrown in the trash.

  • Memory is a leather-bound diary stored in a filing cabinet behind you. It contains years of notes. When you sit back down at your desk for a brand-new project, you can open that diary, pull out an old note, and place it onto your sheet of paper.

  • When to use Context: Use it for immediate, quick actions (e.g., asking an AI to fix a single typo in a paragraph).
  • When to use Memory: Use it when you want long-term personalization (e.g., keeping track of a user’s coding preferences over several months).

🛠️ Sample Project: The Remembering Chatbot

Let’s build a foundational system. We will save user preferences to a permanent file and automatically load them into the AI’s hidden prompt every time a new chat starts.

Python Implementation

# Save user preferences to a simple text file
def save_profile(name, topic):
    with open("user_profile.txt", "w") as file:
        file.write(f"{name},{topic}")

# Read the file to inject into the AI context
def load_context():
    try:
        with open("user_profile.txt", "r") as file:
            data = file.read().split(",")
            return f"System Prompt: You are talking to {data[0]}, who loves {data[1]}."
    except FileNotFoundError:
        return "System Prompt: You are talking to a new user."

# Simulate starting a chat
save_profile("Alice", "Machine Learning")
print(load_context())

Java Implementation

import java.io.*;

public class MemorySystem {
    public static void saveProfile(String name, String topic) {
        try (PrintWriter out = new PrintWriter(new FileWriter("user_profile.txt"))) {
            out.print(name + "," + topic);
        } catch (IOException e) {
            System.out.println("Error saving profile.");
        }
    }

    public static String loadContext() {
        try (BufferedReader br = new BufferedReader(new FileReader("user_profile.txt"))) {
            String line = br.readLine();
            if (line != null) {
                String[] data = line.split(",");
                return "System Prompt: You are talking to " + data[0] + ", who loves " + data[1] + ".";
            }
        } catch (IOException e) {
            // File not found or unreadable
        }
        return "System Prompt: You are talking to a new user.";
    }

    public static void main(String[] args) {
        saveProfile("Alice", "Machine Learning");
        System.out.println(loadContext());
    }
}

👉 Next Step: Run this code on your local computer. Try changing the name and topic manually to see how the system dynamic adjusts its “background knowledge” before a chat even begins.


2. Short-Term State

Short-Term State (often called session memory) tracks clues and details during a single, active conversation thread.

If you tell an AI, “I’m planning a trip to Tokyo,” and two sentences later say, “What clothes should I pack?”, the AI uses short-term state to realize “clothes” refers to your Tokyo trip, not a winter trek in Antarctica.

  • When to use: Crucial for multi-step customer service applications, checkout workflows, and conversational forms.

🛠️ Sample Project: The Multi-Step Order Bot

Let’s build a clean text loop where an application collects information step-by-step and retains it throughout the session.

Python Implementation

class ShortTermBot:
    def __init__(self):
        self.session_data = {}

    public_chat(self):
        self.session_data['order_id'] = input("What is your Order Number? ")
        self.session_data['email'] = input("What is your Email Address? ")
        self.session_data['issue'] = input("Describe your issue: ")
        
        print("\n--- Session Summary ---")
        print(f"Resolving issue for Order #{self.session_data['order_id']} linked to {self.session_data['email']}.")
        print(f"Issue recorded: '{self.session_data['issue']}'")

bot = ShortTermBot()
bot.public_chat()

Java Implementation

import java.util.HashMap;
import java.util.Scanner;

public class ShortTermBot {
    private HashMap<String, String> sessionData = new HashMap<>();

    public void startChat() {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("What is your Order Number? ");
        sessionData.put("order_id", scanner.nextLine());
        
        System.out.print("What is your Email Address? ");
        sessionData.put("email", scanner.nextLine());
        
        System.out.print("Describe your issue: ");
        sessionData.put("issue", scanner.nextLine());
        
        System.out.println("\n--- Session Summary ---");
        System.out.println("Resolving issue for Order #" + sessionData.get("order_id") + " linked to " + sessionData.get("email") + ".");
        System.out.println("Issue recorded: '" + sessionData.get("issue") + "'");
    }

    public static void main(String[] args) {
        new ShortTermBot().startChat();
    }
}

👉 Next Step: Modify this script by adding a fourth prompt step (like refund_requested_boolean). Make sure the final print statement successfully tracks all four variables cleanly.


3. Long-Term Memory (LTM)

Long-Term Memory allows an AI to remember personal attributes, core rules, and historical facts across entirely distinct chat windows weeks or months apart.

If you tell a coding assistant in January that you only write software in Python, and you log back in during March to start an empty chat, an LTM-enabled assistant will immediately write Python code without needing a reminder.

  • When to use: Essential for fitness coaches, medical checkup tools, financial advisors, and productivity companions.

🛠️ Sample Project: Your AI Diary Companion

We will simulate storing persistent entries to look back at trends over time.

Python Implementation

import json

def save_diary_entry(date, mood_score, note):
    try:
        with open("diary.json", "r") as f:
            history = json.load(f)
    except FileNotFoundError:
        history = []
        
    history.append({"date": date, "mood": mood_score, "note": note})
    
    with open("diary.json", "w") as f:
        json.dump(history, f, indent=4)

def read_trends():
    with open("diary.json", "r") as f:
        history = json.load(f)
    total_mood = sum([entry['mood'] for entry in history])
    print(f"Analyzing {len(history)} entries... Average mood score: {total_mood / len(history)}/10")

save_diary_entry("2026-07-05", 8, "Great productive day.")
save_diary_entry("2026-07-06", 6, "Feeling a bit tired.")
read_trends()

Java Implementation

// Uses a clean string format to simulate JSON tracking safely without bulky external libraries
import java.io.*;
import java.nio.file.*;
import java.util.List;

public class DiaryCompanion {
    private static final String FILE_PATH = "diary.txt";

    public static void saveDiaryEntry(String date, int moodScore, String note) throws IOException {
        String record = date + "|" + moodScore + "|" + note + "\n";
        Files.write(Paths.get(FILE_PATH), record.getBytes(), StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }

    public static void readTrends() throws IOException {
        List<String> lines = Files.readAllLines(Paths.get(FILE_PATH));
        double totalMood = 0;
        for (String line : lines) {
            String[] parts = line.split("\\|");
            totalMood += Integer.parseInt(parts[1]);
        }
        System.out.println("Analyzing " + lines.size() + " entries... Average mood score: " + (totalMood / lines.size()) + "/10");
    }

    public static void main(String[] args) throws IOException {
        saveDiaryEntry("2026-07-05", 8, "Great productive day.");
        saveDiaryEntry("2026-07-06", 6, "Feeling a bit tired.");
        readTrends();
    }
}

👉 Next Step: Add an extra entry with a low mood score (e.g., 2) to see how the system recalculates your overall long-term trend instantly.


4. Memory Types

To organize data like a human brain, advanced AI engineering divides memories into three unique buckets. You can easily remember them using the acronym E.S.P.

Memory TypeCore Question It AnswersReal-World Example
Episodic Memory”What specific events happened?""The user rejected our database design mockup during Tuesday’s meeting.”
Semantic Memory”What facts are consistently true?""The user lives in Paris and uses a Mac laptop.”
Procedural Memory”How should tasks be completed?""Always write tests first before writing the core application feature.”

💡 Tip: Splitting memory like this keeps your data clean. If an AI wants to change how it formats your code, it only needs to rewrite its Procedural memory, leaving your bio facts intact!

🛠️ Sample Project: The Persona Filter

Let’s look at how we can sort a messy statement into clean, categorized data blocks.

Python Implementation

def parse_raw_input(text):
    # Simulating structural categorization logic
    memory_structure = {"episodic": [], "semantic": [], "procedural": []}
    
    if "rejected" in text or "meeting" in text:
        memory_structure["episodic"].append(text)
    if "lives in" in text or "prefers" in text:
        memory_structure["semantic"].append(text)
    if "always" in text or "steps" in text:
        memory_structure["procedural"].append(text)
        
    return memory_structure

print(parse_raw_input("The user lives in Paris and prefers dark mode layout."))

Java Implementation

import java.util.ArrayList;
import java.util.HashMap;

public class PersonaFilter {
    public static HashMap<String, ArrayList<String>> parseRawInput(String text) {
        HashMap<String, ArrayList<String>> memoryStructure = new HashMap<>();
        memoryStructure.put("episodic", new ArrayList<>());
        memoryStructure.put("semantic", new ArrayList<>());
        memoryStructure.put("procedural", new ArrayList<>());

        if (text.contains("rejected") || text.contains("meeting")) {
            memoryStructure.get("episodic").add(text);
        }
        if (text.contains("lives in") || text.contains("prefers")) {
            memoryStructure.get("semantic").add(text);
        }
        if (text.contains("always") || text.contains("steps")) {
            memoryStructure.get("procedural").add(text);
        }
        return memoryStructure;
    }

    public static void main(String[] args) {
        System.out.println(parseRawInput("The user lives in Paris and prefers dark mode layout."));
    }
}

👉 Next Step: Run this script with a procedural sentence like: “Always review the security guidelines before deploying code.” Ensure the system captures it properly under the procedural key.


5. Memory Patterns

AI models have hard limits on how much text they can process at once (known as Token Limits). If you feed an AI an entire dictionary of past history, it will break, slow down, or become incredibly expensive to run.

To bypass this roadblock, developers use structural Memory Patterns:

  1. Summarization Pattern: When a chat grows too long, a background script condenses the text into a tiny summary paragraph and deletes the massive original chat logs.
  2. Vector Search Pattern: Sentences are converted into long strings of math values (vectors). When a user asks a question, the system searches the database only for memories that match the mathematical meaning of that question.

🛠️ Sample Project: The Chat Shrinker

Let’s build a programmatic simulation of the Summarization Pattern. When the history gets too heavy, we condense it.

Python Implementation

def monitor_chat_length(messages):
    if len(messages) > 4:
        print("⚠️ Log size threshold reached! Compressing history...")
        # Simulating a background summarization request
        compressed_summary = ["Summary: User asked about coding and requested dark themes."]
        return compressed_summary
    return messages

chat_history = ["Hi", "I want to code", "Use dark mode", "Make it fast", "Add a button"]
print("Before:", chat_history)
print("After: ", monitor_chat_length(chat_history))

Java Implementation

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ChatShrinker {
    public static List<String> monitorChatLength(List<String> messages) {
        if (messages.size() > 4) {
            System.out.println("⚠️ Log size threshold reached! Compressing history...");
            return Arrays.asList("Summary: User asked about coding and requested dark themes.");
        }
        return messages;
    }

    public static void main(String[] args) {
        List<String> chatHistory = new ArrayList<>(Arrays.asList("Hi", "I want to code", "Use dark mode", "Make it fast", "Add a button"));
        System.out.println("Before: " + chatHistory);
        System.out.println("After:  " + monitorChatLength(chatHistory));
    }
}

👉 Next Step: Change the logic so compression triggers at exactly 10 items instead of 4, mimicking real-world production constraints.


6. Memory Implementation

You don’t always have to write these memory management algorithms completely from scratch! The open-source development community has created powerful, pre-built frameworks that act as a universal memory layer for your code.

  • Mem0: A highly popular memory engine that remembers user preferences across platforms, constantly updating user profiles automatically.
  • LangMem: Formulated by the LangChain engineering team, this tool analyzes long threads to capture key insights and rules.

💡 Trick: Instead of managing flat text files, using a tool like Mem0 lets you write a single line of code to recall historical context seamlessly.

# Conceptual layout of a production-level framework integration
# pip install mem0ai
from mem0 import Memory

def run_production_memory():
    # Initialize the memory core engine
    memo = Memory()
    
    # Store information dynamically
    memo.add("The user prefers using Python over Java for backend microservices.", user_id="user_123")
    
    # Retrieve relevant data instantly with standard natural language queries
    result = memo.search("What coding options does my user like?", user_id="user_123")
    print(result)

👉 Next Step: Check out the official documentation for Mem0 or LangMem to see how seamlessly they connect with popular database engines like PostgreSQL or SQLite.


7. Graph Memory

Sometimes, flat text arrays or mathematical vector lists aren’t smart enough to link intricate details. That’s where Graph Memory (Knowledge Graphs) comes into play.

Instead of writing long paragraphs, Graph Memory chops data down into distinct Entities (People, Places, or Things) linked together by Edges (Relationships).

Standard Text: “Alice manages Bob, and Bob works at Acme Corp.” Graph Representation: [Alice] ──(MANAGES)──► [Bob] ──(WORKS_AT)──► [Acme Corp]

  • When to use: Crucial for tracking organizational charts, maps, family trees, or mapping drug interactions in medical software.

🛠️ Sample Project: The Relationship Network Maker

Python Implementation

# Simple Graph representation via nested dictionaries
knowledge_graph = {
    "Alice": {"relationship": "manages", "target": "Bob"},
    "Bob": {"relationship": "works_at", "target": "Acme Corp"}
}

def search_graph(person):
    if person in knowledge_graph:
        rel = knowledge_graph[person]["relationship"]
        tgt = knowledge_graph[person]["target"]
        print(f"{person} {rel} {tgt}.")
    else:
        print("Entity not found.")

search_graph("Alice")

Java Implementation

import java.util.HashMap;

class NodeConnection {
    String relationship;
    String target;
    
    NodeConnection(String relationship, String target) {
        this.relationship = relationship;
        this.target = target;
    }
}

public class RelationshipNetwork {
    public static void main(String[] args) {
        HashMap<String, NodeConnection> knowledgeGraph = new HashMap<>();
        knowledgeGraph.put("Alice", new NodeConnection("manages", "Bob"));
        knowledgeGraph.put("Bob", new NodeConnection("works_at", "Acme Corp"));

        NodeConnection connection = knowledgeGraph.get("Alice");
        if (connection != null) {
            System.out.println("Alice " + connection.relationship + " " + connection.target + ".");
        }
    }
}

👉 Next Step: Add a third node connection: Acme Corp ──(located_in)──► Paris. See if you can modify the search function to look up where Alice’s employee works!


8. Memory Evals (Evaluations)

How do you guarantee that your AI memory layer is working safely and efficiently? You perform Memory Evals.

Evals are automated quality checks. They confirm the AI is successfully pulling the correct information out of storage, while ensuring it safely discards irrelevant, low-value data (like a user casually typing, “Wow, I am tired of this rain today”).

🛠️ Sample Project: The Memory Pop Quiz

Python Implementation

def run_pop_quiz():
    # Mock data store
    stored_fact = "User's favorite color is Emerald Green"
    
    # Simulating a mock test response from an AI agent
    ai_answer = "The user prefers dark emerald green tones."
    
    # Simple semantic evaluation rule
    if "emerald green" in ai_answer.lower():
        print("✅ Evaluation Passed: AI correctly remembered the user's preference.")
        return 100
    else:
        print("❌ Evaluation Failed: AI missed the memory detail.")
        return 0

run_pop_quiz()

Java Implementation

public class MemoryQuiz {
    public static int runPopQuiz() {
        String storedFact = "User's favorite color is Emerald Green";
        String aiAnswer = "The user prefers dark emerald green tones.";

        if (aiAnswer.toLowerCase().contains("emerald green")) {
            System.out.println("✅ Evaluation Passed: AI correctly remembered the user's preference.");
            return 100;
        } else {
            System.out.println("❌ Evaluation Failed: AI missed the memory detail.");
            return 0;
        }
    }

    public static void main(String[] args) {
        runPopQuiz();
    }
}

👉 Next Step: Intentionally break the evaluation by changing ai_answer to “The user wants a red car.” Run it again to watch your evaluation pipeline flag the memory failure accurately.


Conclusion

Building persistent memory is the secret ingredient that transforms a generic, one-off chatbot into a context-aware, autonomous AI teammate. By mastering how to structure context, balance long-term profiles, and weave knowledge graphs, you gain the skills needed to build production-grade AI systems.

To keep expanding your AI engineering skills with crystal-clear tutorials, make sure to visit us regularly at aigrama.net!

Enjoyed this article?

Subscribe & Follow

Get notified of new technical articles on AI/ML, Java, Python, and system architecture.