Skip to content

Agent Memory

Memory is what makes agents stateful — it's how they remember past interactions, learn from experience, and maintain context across sessions. Without memory, every conversation starts from zero. This covers the memory types, storage patterns, and implementation strategies.

Table of Contents

Installation / Setup

pip install langchain-core langchain-community

Core Syntax

Memory types

Type What it stores Example
Short-term Current conversation Chat history (messages list)
Long-term Persistent facts across sessions User preferences, learned facts
Episodic Past experiences "Last time I solved X, I used Y"
Semantic General knowledge Facts, concepts, relationships
graph TD
    A[Agent Memory] --> B[Short-term]
    A --> C[Long-term]
    B --> D[Conversation history]
    B --> E[Working memory]
    C --> F[User preferences]
    C --> G[Facts & knowledge]
    C --> H[Episodic memories]

Short-term memory (conversation history)

# simplest form: maintain a messages list
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
]

def chat(user_input, llm):
    messages.append({"role": "user", "content": user_input})
    response = llm.invoke(messages)
    messages.append({"role": "assistant", "content": response.content})
    return response.content

Conversation windowing (prevent context overflow)

def chat_with_window(user_input, llm, max_messages=20):
    messages.append({"role": "user", "content": user_input})

    # keep only the last N messages
    trimmed = messages[-max_messages:]
    response = llm.invoke(trimmed)
    messages.append({"role": "assistant", "content": response.content})
    return response.content

Summarization memory (compress old messages)

def summarize_old_messages(messages, llm, keep_last=10):
    if len(messages) <= keep_last:
        return messages

    old = messages[:-keep_last]
    recent = messages[-keep_last:]

    summary = llm.invoke([
        {"role": "system", "content": "Summarize this conversation in 2-3 sentences."},
        {"role": "user", "content": str(old)},
    ]).content

    return [{"role": "system", "content": f"Previous conversation summary: {summary}"}] + recent

Long-term memory (persistent)

import json

class LongTermMemory:
    def __init__(self, path="memory.json"):
        self.path = path
        self.facts = self._load()

    def _load(self):
        try:
            return json.load(open(self.path))
        except FileNotFoundError:
            return {}

    def _save(self):
        json.dump(self.facts, open(self.path, "w"), indent=2)

    def remember(self, key, value):
        self.facts[key] = value
        self._save()

    def recall(self, key):
        return self.facts.get(key)

    def search(self, query):
        """Simple keyword search over facts."""
        results = {}
        for k, v in self.facts.items():
            if query.lower() in str(k).lower() or query.lower() in str(v).lower():
                results[k] = v
        return results

Common Operations

Vector-based long-term memory

from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

class VectorMemory:
    def __init__(self):
        self.texts = []
        self.vectorstore = None

    def add(self, text, metadata=None):
        self.texts.append({"text": text, "metadata": metadata or {}})
        if self.vectorstore is None:
            self.vectorstore = FAISS.from_texts([text], OpenAIEmbeddings())
        else:
            self.vectorstore.add_texts([text], metadatas=[metadata])

    def search(self, query, k=5):
        if self.vectorstore is None:
            return []
        return self.vectorstore.similarity_search(query, k=k)

Graph-based memory (knowledge graph)

class GraphMemory:
    def __init__(self):
        self.entities = {}    # name -> properties
        self.relations = []   # (subject, predicate, object)

    def add_entity(self, name, properties=None):
        self.entities[name] = properties or {}

    def add_relation(self, subject, predicate, obj):
        self.relations.append((subject, predicate, obj))

    def query(self, entity):
        """Get all facts about an entity."""
        facts = []
        for s, p, o in self.relations:
            if s == entity:
                facts.append(f"{s} {p} {o}")
            elif o == entity:
                facts.append(f"{o} is {p} of {s}")
        return facts

Practical Examples

Example 1 — Agent with conversation + long-term memory

class AgentWithMemory:
    def __init__(self, llm):
        self.llm = llm
        self.conversation = []
        self.long_term = LongTermMemory()

    def chat(self, user_input):
        # check for facts to recall
        relevant_facts = self.long_term.search(user_input)
        context = "\n".join(f"- {k}: {v}" for k, v in relevant_facts.items())

        # build prompt with context
        system = "You are a helpful assistant."
        if context:
            system += f"\n\nKnown facts:\n{context}"

        self.conversation.append({"role": "user", "content": user_input})
        messages = [{"role": "system", "content": system}] + self.conversation[-20:]

        response = self.llm.invoke(messages)
        self.conversation.append({"role": "assistant", "content": response.content})

        # extract facts to remember
        if "my name is" in user_input.lower():
            name = user_input.split("my name is")[-1].strip()
            self.long_term.remember("user_name", name)

        return response.content

Example 2 — Episodic memory for task learning

class EpisodicMemory:
    def __init__(self):
        self.episodes = []

    def record(self, task, action, result, success):
        self.episodes.append({
            "task": task, "action": action,
            "result": result, "success": success,
        })

    def search_similar(self, task, k=3):
        """Find past episodes with similar tasks."""
        scored = []
        for ep in self.episodes:
            # simple word overlap scoring
            overlap = len(set(task.lower().split()) & set(ep["task"].lower().split()))
            scored.append((overlap, ep))
        scored.sort(reverse=True)
        return [ep for _, ep in scored[:k]]

Example 3 — Summarization memory with LangChain

from langchain_core.messages import SystemMessage

def smart_history(messages, llm, max_tokens=4000):
    """Summarize history when it gets too long."""
    # rough token estimate
    total = sum(len(str(m)) // 4 for m in messages)
    if total < max_tokens:
        return messages

    # summarize old messages
    old = messages[1:-10]   # skip system, keep last 10
    summary = llm.invoke(
        [SystemMessage(content="Summarize this conversation concisely.")] + old
    ).content

    return [messages[0], SystemMessage(content=f"Summary: {summary}")] + messages[-10:]

Common Patterns

Pattern: memory retrieval before response

def agent_with_memory(query, llm, memory):
    # retrieve relevant memories
    relevant = memory.search(query, k=3)
    context = "\n".join(str(m) for m in relevant)

    # include in prompt
    system = f"You are a helpful assistant.\nRelevant memories:\n{context}"
    messages = [{"role": "system", "content": system}, {"role": "user", "content": query}]

    response = llm.invoke(messages)

    # store new memory
    memory.add(f"User asked: {query}; I answered: {response.content}")

    return response.content

Pattern: memory decay

def decay_memory(memories, decay_rate=0.9):
    """Reduce relevance of older memories."""
    for i, mem in enumerate(memories):
        mem["relevance"] *= decay_rate ** i
    return sorted(memories, key=lambda x: x["relevance"], reverse=True)

Best Practices

  • Start with conversation history — it's the most important memory type.
  • Window old messages — keep last 10–20; summarize the rest.
  • Persist long-term memory — save to disk/database.
  • Use vector search for semantic memory retrieval.
  • Extract facts automatically — don't rely on users to "tell" the agent.
  • Memory should be queryable — not just append-only.
  • Limit memory size — unbounded growth = slower responses.

Common Mistakes

Mistake Fix
Storing entire conversation forever Window or summarize old messages
No persistent memory Save long-term facts to disk/database
Memory retrieval too slow Use vector search, not keyword scan
Ignoring memory relevance Rank by similarity, not just recency
Memory leaks between users Use separate memory stores per user

Troubleshooting

Symptom Cause / Fix
Context length exceeded Too many messages — window or summarize
Agent forgets user info No long-term memory — add persistent storage
Slow memory retrieval Use vector search; limit results
Memory confusion Separate short-term and long-term stores

Official Documentation

Last Updated

2026-09-06 — Initial version.