{"id":587,"date":"2026-08-21T09:00:00","date_gmt":"2026-08-21T00:00:00","guid":{"rendered":"https:\/\/www.theagenticprotocol.com\/?p=587"},"modified":"2026-08-20T12:14:05","modified_gmt":"2026-08-20T03:14:05","slug":"ai-agent-memory-python","status":"publish","type":"post","link":"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/","title":{"rendered":"AI Agent Memory: Build Agents That Remember Across Sessions"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">AI agent memory in 2026 has a critical default that trips every builder eventually: agents forget everything between sessions. The conversation history you built carefully in the <a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/claude-api-python\/\">Claude API Tutorial<\/a> exists only in RAM. Restart the script, and the agent has never met you before.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"576\" src=\"https:\/\/www.theagenticprotocol.com\/wp-content\/uploads\/2026\/08\/grok-image-3d0ba990-2905-418d-8d37-d64f84abb5cf-1024x576.jpg\" alt=\"AI agent memory build agents that remember across sessions Python 2026\" class=\"wp-image-588\" srcset=\"https:\/\/www.theagenticprotocol.com\/wp-content\/uploads\/2026\/08\/grok-image-3d0ba990-2905-418d-8d37-d64f84abb5cf-1024x576.jpg 1024w, https:\/\/www.theagenticprotocol.com\/wp-content\/uploads\/2026\/08\/grok-image-3d0ba990-2905-418d-8d37-d64f84abb5cf-300x169.jpg 300w, https:\/\/www.theagenticprotocol.com\/wp-content\/uploads\/2026\/08\/grok-image-3d0ba990-2905-418d-8d37-d64f84abb5cf-768x432.jpg 768w, https:\/\/www.theagenticprotocol.com\/wp-content\/uploads\/2026\/08\/grok-image-3d0ba990-2905-418d-8d37-d64f84abb5cf-1536x864.jpg 1536w, https:\/\/www.theagenticprotocol.com\/wp-content\/uploads\/2026\/08\/grok-image-3d0ba990-2905-418d-8d37-d64f84abb5cf.jpg 1792w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The production solution is a three-tier memory architecture. In-context memory is what the agent sees right now \u2014 the current conversation window. Semantic memory is facts and knowledge stored in a vector database, retrieved by similarity when relevant. Episodic memory is a persistent record of past interactions, stored in a database and summarized into future context. This guide implements all three tiers with working Python code, ordered from simplest to most powerful. You need the Claude API setup from the <a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/claude-api-python\/\">Claude API Python Tutorial<\/a> to follow along.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_86 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#Setup_Install_the_Memory_Stack\" >Setup: Install the Memory Stack<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#Tier_1_In-Context_Memory_With_LangGraph_Checkpointing\" >Tier 1: In-Context Memory With LangGraph Checkpointing<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#Tier_2_Semantic_Memory_With_ChromaDB\" >Tier 2: Semantic Memory With ChromaDB<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#Tier_3_Episodic_Memory_%E2%80%94_A_Log_of_What_Happened\" >Tier 3: Episodic Memory \u2014 A Log of What Happened<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#Which_Memory_Tier_to_Use_The_Decision_Guide\" >Which Memory Tier to Use: The Decision Guide<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#The_Builders_Takeaway\" >The Builder&#8217;s Takeaway<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/#Continue_in_This_Series\" >Continue in This Series<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Setup_Install_the_Memory_Stack\"><\/span>Setup: Install the Memory Stack<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install anthropic chromadb langgraph langchain-anthropic \\\n            sentence-transformers python-dotenv<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code># .env\nANTHROPIC_API_KEY=sk-ant-your-key-here<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Tier_1_In-Context_Memory_With_LangGraph_Checkpointing\"><\/span>Tier 1: In-Context Memory With LangGraph Checkpointing<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In-context memory is the conversation history the agent sees in its current context window. The 2026 standard \u2014 replacing the deprecated <code>ConversationBufferMemory<\/code> from LangChain&#8217;s classic API \u2014 is LangGraph checkpointing: the conversation state is saved to a store after every message and reloaded from that store when the conversation resumes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import os\nfrom dotenv import load_dotenv\nfrom langgraph.checkpoint.memory import MemorySaver\nfrom langgraph.graph import StateGraph, MessagesState, START, END\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_core.messages import HumanMessage, SystemMessage\n\nload_dotenv()\n\n# \u2500\u2500\u2500 Initialize Claude \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nllm = ChatAnthropic(\n    model=\"claude-sonnet-5\",\n    api_key=os.environ.get(\"ANTHROPIC_API_KEY\")\n)\n\n# \u2500\u2500\u2500 Build the agent graph \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ndef call_model(state: MessagesState):\n    \"\"\"The single agent node: call Claude with full message history.\"\"\"\n    system = SystemMessage(content=\"\"\"You are a helpful personal assistant.\n    You remember everything the user has told you across all our conversations.\n    Reference previous discussions naturally when relevant.\"\"\")\n\n    response = llm.invoke(&#91;system] + state&#91;\"messages\"])\n    return {\"messages\": &#91;response]}\n\n\n# Build a simple single-node graph\nbuilder = StateGraph(MessagesState)\nbuilder.add_node(\"agent\", call_model)\nbuilder.add_edge(START, \"agent\")\nbuilder.add_edge(\"agent\", END)\n\n# MemorySaver: in-process store (survives process restart only when using SqliteSaver)\n# For production: replace with SqliteSaver or PostgresSaver\nmemory = MemorySaver()\nagent = builder.compile(checkpointer=memory)\n\n\ndef chat_with_memory(user_input: str, session_id: str) -&gt; str:\n    \"\"\"\n    Chat with the agent. The same session_id preserves conversation history.\n    Different session_ids start fresh conversations.\n    \"\"\"\n    config = {\"configurable\": {\"thread_id\": session_id}}\n\n    result = agent.invoke(\n        {\"messages\": &#91;HumanMessage(content=user_input)]},\n        config=config\n    )\n\n    return result&#91;\"messages\"]&#91;-1].content\n\n\n# \u2500\u2500\u2500 Demo: Same session_id = agent remembers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nSESSION = \"user-alex-001\"\n\nprint(chat_with_memory(\"Hi! My name is Alex and I'm a Python developer.\", SESSION))\n# \u2192 \"Nice to meet you, Alex! ...\"\n\nprint(chat_with_memory(\"What programming language did I mention?\", SESSION))\n# \u2192 \"You mentioned Python...\" \u2190 agent remembers from same session\n\nprint(chat_with_memory(\"And what's my name?\", SESSION))\n# \u2192 \"Your name is Alex.\" \u2190 still remembers<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>thread_id<\/code> in the config is the session identifier. The same <code>thread_id<\/code> always loads the same conversation history. Different <code>thread_id<\/code> values start fresh. <strong>Important limitation:<\/strong> <code>MemorySaver<\/code> stores in RAM \u2014 the history is gone when the process restarts. For persistence across restarts, replace it with <code>SqliteSaver<\/code> (one-line change):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code\">from langgraph.checkpoint.sqlite import SqliteSaver\n# Persists to SQLite database file \u2014 survives process restarts\nmemory = SqliteSaver.from_conn_string(\"agent_memory.db\")\nagent = builder.compile(checkpointer=memory)\n# Everything else stays the same<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Tier_2_Semantic_Memory_With_ChromaDB\"><\/span>Tier 2: Semantic Memory With ChromaDB<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In-context memory has one hard constraint: the context window. A 200,000-token window sounds unlimited, but sending the entire history of a long-running assistant on every call is expensive and slow \u2014 and as the conversation grows, the older, less-relevant history dilutes the quality of the recent context. Semantic memory solves this by storing facts in a vector database and retrieving only the most relevant facts for each new message.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code\">import os\nimport anthropic\nimport chromadb\nfrom chromadb.utils import embedding_functions\nfrom datetime import datetime\nfrom dotenv import load_dotenv\nload_dotenv()\nclient = anthropic.Anthropic(api_key=os.environ.get(\"ANTHROPIC_API_KEY\"))\n# \u2500\u2500\u2500 Semantic Memory Store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nchroma_client = chromadb.PersistentClient(path=\".\/semantic_memory\")\n# Use a free local embedding model\nembed_fn = embedding_functions.SentenceTransformerEmbeddingFunction(\n    model_name=\"all-MiniLM-L6-v2\"\n)\ndef get_memory_collection(user_id: str):\n    \"\"\"Get or create a per-user memory collection.\"\"\"\n    return chroma_client.get_or_create_collection(\n        name=f\"memory_{user_id}\",\n        embedding_function=embed_fn\n    )\ndef save_memory(user_id: str, memory: str, metadata: dict = None) -> None:\n    \"\"\"\n    Save a fact or memory to the semantic store.\n    The memory is embedded and stored for later retrieval by similarity.\n    \"\"\"\n    collection = get_memory_collection(user_id)\n    memory_id = f\"mem_{datetime.now().timestamp()}\"\n    collection.add(\n        documents=[memory],\n        ids=[memory_id],\n        metadatas=[{\"timestamp\": datetime.now().isoformat(), **(metadata or {})}]\n    )\n    print(f\"[Memory saved] {memory}\")\ndef retrieve_relevant_memories(user_id: str, query: str, n: int = 3) -> list[str]:\n    \"\"\"\n    Retrieve the n most semantically relevant memories for a given query.\n    Returns an empty list if no memories exist yet.\n    \"\"\"\n    collection = get_memory_collection(user_id)\n    if collection.count() == 0:\n        return []\n    results = collection.query(\n        query_texts=[query],\n        n_results=min(n, collection.count())\n    )\n    return results[\"documents\"][0] if results[\"documents\"] else []\ndef extract_and_save_memories(user_id: str, conversation: str) -> None:\n    \"\"\"\n    Ask Claude to identify facts worth saving from a conversation.\n    Called at the end of each session to populate the semantic memory.\n    \"\"\"\n    response = client.messages.create(\n        model=\"claude-haiku-4-5-20251001\",  # Haiku: cheap for extraction\n        max_tokens=512,\n        messages=[{\n            \"role\": \"user\",\n            \"content\": f\"\"\"Extract 3-5 specific facts worth remembering from this conversation.\nReturn each fact as a single clear sentence. One per line. Facts only, no explanations.\nConversation:\n{conversation}\"\"\"\n        }]\n    )\n    facts = response.content[0].text.strip().split(\"\\n\")\n    for fact in facts:\n        fact = fact.strip().lstrip(\"\u2022-123456789. \")\n        if fact:\n            save_memory(user_id, fact)\ndef chat_with_semantic_memory(user_input: str, user_id: str,\n                               history: list) -> tuple[str, list]:\n    \"\"\"\n    Chat with the agent, injecting relevant semantic memories into context.\n    Returns the response and updated history.\n    \"\"\"\n    # Retrieve relevant memories for this input\n    memories = retrieve_relevant_memories(user_id, user_input)\n    memory_context = \"\"\n    if memories:\n        memory_context = \"\\n\\nWhat I remember about you:\\n\" + \"\\n\".join(\n            f\"- {m}\" for m in memories\n        )\n    system = f\"\"\"You are a helpful personal assistant with long-term memory.\n{memory_context}\nUse this context naturally in your responses when relevant.\nDo not mention \"memory\" or \"database\" \u2014 just reference what you know.\"\"\"\n    history.append({\"role\": \"user\", \"content\": user_input})\n    response = client.messages.create(\n        model=\"claude-sonnet-5\",\n        max_tokens=1024,\n        system=system,\n        messages=history\n    )\n    assistant_reply = response.content[0].text\n    history.append({\"role\": \"assistant\", \"content\": assistant_reply})\n    return assistant_reply, history\n# \u2500\u2500\u2500 Demo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nUSER_ID = \"alex\"\nsession_history = []\n# First session: user shares information\nreply, session_history = chat_with_semantic_memory(\n    \"I'm Alex, a backend developer who loves Python and hates JavaScript.\",\n    USER_ID, session_history\n)\nprint(reply)\nreply, session_history = chat_with_semantic_memory(\n    \"I'm building a RAG system for my startup's compliance tool.\",\n    USER_ID, session_history\n)\nprint(reply)\n# Extract and save facts at end of session\nconversation_text = \"\\n\".join(\n    f\"{m['role']}: {m['content']}\" for m in session_history\n)\nextract_and_save_memories(USER_ID, conversation_text)\nprint(\"Session memories saved.\")\n# --- New session: agent recalls from semantic memory ---\nsession_history_2 = []\nreply, _ = chat_with_semantic_memory(\n    \"What do you know about me?\",\n    USER_ID, session_history_2\n)\nprint(reply)\n# \u2192 \"You're Alex, a backend developer...\" \u2190 retrieved from ChromaDB<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Tier_3_Episodic_Memory_%E2%80%94_A_Log_of_What_Happened\"><\/span>Tier 3: Episodic Memory \u2014 A Log of What Happened<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Episodic memory is the simplest tier and often the most useful: a persistent log of past conversations that the agent can reference to understand patterns, past decisions, and the history of a relationship with a user or project. Unlike semantic memory (retrieved by similarity), episodic memory is retrieved by time or by session identifier \u2014 &#8220;what happened last Tuesday&#8221; rather than &#8220;what do I know about the project.&#8221;<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code\">import json\nimport sqlite3\nfrom datetime import datetime\nfrom pathlib import Path\nclass EpisodicMemory:\n    \"\"\"\n    Persistent log of agent sessions and their summaries.\n    Uses SQLite \u2014 no additional infrastructure required.\n    \"\"\"\n    def __init__(self, db_path: str = \"episodic_memory.db\"):\n        self.conn = sqlite3.connect(db_path)\n        self._init_db()\n    def _init_db(self) -> None:\n        self.conn.execute(\"\"\"\n            CREATE TABLE IF NOT EXISTS sessions (\n                id TEXT PRIMARY KEY,\n                user_id TEXT NOT NULL,\n                started_at TEXT NOT NULL,\n                ended_at TEXT,\n                summary TEXT,\n                message_count INTEGER DEFAULT 0,\n                raw_messages TEXT\n            )\n        \"\"\")\n        self.conn.commit()\n    def start_session(self, session_id: str, user_id: str) -> None:\n        \"\"\"Record the start of a new conversation session.\"\"\"\n        self.conn.execute(\"\"\"\n            INSERT OR IGNORE INTO sessions (id, user_id, started_at, raw_messages)\n            VALUES (?, ?, ?, ?)\n        \"\"\", (session_id, user_id, datetime.now().isoformat(), \"[]\"))\n        self.conn.commit()\n    def save_session(self, session_id: str, messages: list,\n                     summary: str) -> None:\n        \"\"\"Save the completed session with its summary.\"\"\"\n        self.conn.execute(\"\"\"\n            UPDATE sessions\n            SET ended_at = ?, summary = ?, message_count = ?, raw_messages = ?\n            WHERE id = ?\n        \"\"\", (\n            datetime.now().isoformat(),\n            summary,\n            len(messages),\n            json.dumps(messages),\n            session_id\n        ))\n        self.conn.commit()\n    def get_recent_sessions(self, user_id: str, limit: int = 5) -> list[dict]:\n        \"\"\"Retrieve the N most recent session summaries for a user.\"\"\"\n        cursor = self.conn.execute(\"\"\"\n            SELECT id, started_at, summary, message_count\n            FROM sessions\n            WHERE user_id = ? AND summary IS NOT NULL\n            ORDER BY started_at DESC\n            LIMIT ?\n        \"\"\", (user_id, limit))\n        return [\n            {\n                \"session_id\": row[0],\n                \"date\": row[1][:10],\n                \"summary\": row[2],\n                \"messages\": row[3]\n            }\n            for row in cursor.fetchall()\n        ]\n    def format_for_context(self, user_id: str, limit: int = 3) -> str:\n        \"\"\"Format recent sessions as context for the agent's system prompt.\"\"\"\n        sessions = self.get_recent_sessions(user_id, limit)\n        if not sessions:\n            return \"\"\n        lines = [\"Recent conversation history:\"]\n        for s in sessions:\n            lines.append(f\"\u2022 {s['date']}: {s['summary']}\")\n        return \"\\n\".join(lines)\ndef summarize_session(messages: list, client) -> str:\n    \"\"\"Ask Claude to summarize a completed session in one sentence.\"\"\"\n    conversation = \"\\n\".join(\n        f\"{m['role'].capitalize()}: {m['content'][:200]}\"\n        for m in messages\n    )\n    response = client.messages.create(\n        model=\"claude-haiku-4-5-20251001\",\n        max_tokens=100,\n        messages=[{\n            \"role\": \"user\",\n            \"content\": f\"Summarize this conversation in one sentence:\\n\\n{conversation}\"\n        }]\n    )\n    return response.content[0].text.strip()\n# \u2500\u2500\u2500 Combining all three tiers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\ndef run_memory_agent(user_id: str):\n    \"\"\"\n    A complete agent with all three memory tiers:\n    1. LangGraph checkpointing (in-context, per-session)\n    2. ChromaDB semantic memory (facts, cross-session)\n    3. SQLite episodic memory (session log, cross-session)\n    \"\"\"\n    episodic = EpisodicMemory()\n    session_id = f\"{user_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}\"\n    episodic.start_session(session_id, user_id)\n    # Build context from previous sessions\n    history_context = episodic.format_for_context(user_id)\n    semantic_memories = retrieve_relevant_memories(user_id, \"user background preferences\")\n    system_parts = [\"You are a helpful personal assistant with long-term memory.\"]\n    if semantic_memories:\n        system_parts.append(\"Facts I know: \" + \"; \".join(semantic_memories))\n    if history_context:\n        system_parts.append(history_context)\n    system = \"\\n\\n\".join(system_parts)\n    messages = []\n    print(f\"Session started: {session_id}\")\n    print(\"Type 'quit' to end the session.\\n\")\n    while True:\n        user_input = input(\"You: \").strip()\n        if user_input.lower() in (\"quit\", \"exit\"):\n            break\n        messages.append({\"role\": \"user\", \"content\": user_input})\n        response = client.messages.create(\n            model=\"claude-sonnet-5\",\n            max_tokens=1024,\n            system=system,\n            messages=messages\n        )\n        reply = response.content[0].text\n        messages.append({\"role\": \"assistant\", \"content\": reply})\n        print(f\"Agent: {reply}\\n\")\n    # Save session on exit\n    if messages:\n        summary = summarize_session(messages, client)\n        episodic.save_session(session_id, messages, summary)\n        extract_and_save_memories(user_id, \"\\n\".join(\n            f\"{m['role']}: {m['content']}\" for m in messages\n        ))\n        print(f\"Session saved: {summary}\")<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Which_Memory_Tier_to_Use_The_Decision_Guide\"><\/span>Which Memory Tier to Use: The Decision Guide<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Situation<\/th><th>Use<\/th><th>Why<\/th><\/tr><\/thead><tbody><tr><td>Single chat session, no persistence needed<\/td><td>Tier 1 only (MemorySaver)<\/td><td>Simplest, zero infrastructure<\/td><\/tr><tr><td>Multi-session chatbot, needs to persist<\/td><td>Tier 1 (SqliteSaver)<\/td><td>One-line change from MemorySaver<\/td><\/tr><tr><td>Agent needs to remember user facts across sessions<\/td><td>Tier 1 + Tier 2<\/td><td>Semantic retrieval for facts, in-context for current chat<\/td><\/tr><tr><td>Agent needs to reference past conversations<\/td><td>Tier 1 + Tier 3<\/td><td>Episodic log gives date-based history context<\/td><\/tr><tr><td>Production personal assistant or long-running agent<\/td><td>All three tiers<\/td><td>Full coverage: session + facts + history<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Start with Tier 1 (LangGraph + SqliteSaver). Add Tier 2 (ChromaDB semantic memory) when users complain the agent &#8220;forgets who they are&#8221; across sessions. Add Tier 3 (episodic log) when the agent needs to reference what happened in past interactions \u2014 customer support history, project decision logs, or ongoing research threads.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the complete agent memory architecture research, see <a href=\"https:\/\/devtoollab.com\/blog\/ai-agent-memory-architecture\" target=\"_blank\" rel=\"noopener\">DevToolLab&#8217;s AI agent memory architecture guide<\/a>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"The_Builders_Takeaway\"><\/span>The Builder&#8217;s Takeaway<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">AI agent memory in 2026 is three tiers, not one. In-context memory via LangGraph checkpointing handles the current conversation and survives restarts with SqliteSaver. Semantic memory via ChromaDB handles cross-session fact retrieval \u2014 the agent knows who you are because it retrieved that from a vector store, not from the context window. Episodic memory via SQLite handles the log of what happened across the relationship&#8217;s history. None of these tiers requires complex infrastructure to start: MemorySaver is a Python dict, SqliteSaver is a local file, ChromaDB is a local directory, SQLite is a single file. The decision table above tells you which combination to implement for your specific use case. Build the weekend project that uses all three and you&#8217;ll have the foundational architecture that every production AI agent with long-term user relationships is built on.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Continue_in_This_Series\"><\/span>Continue in This Series<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/how-to-build-ai-agent-python\/\">How to Build an AI Agent With Python<\/a> \u2014 the stateless agent loop this memory architecture makes persistent<\/li>\n\n\n\n<li><a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/rag-tutorial-python-2026\/\">RAG Tutorial Python 2026<\/a> \u2014 the ChromaDB vector store patterns Tier 2 semantic memory builds on<\/li>\n\n\n\n<li><a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/langchain-tutorial-2026\/\">LangChain Tutorial 2026<\/a> \u2014 the LangGraph foundation that Tier 1 checkpointing uses<\/li>\n\n\n\n<li><a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/how-to-build-ai-chatbot-python\/\">How to Build an AI Chatbot With Python<\/a> \u2014 the chatbot that becomes a truly personal assistant with these three memory tiers<\/li>\n\n\n\n<li><a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/how-to-deploy-ai-agents\/\">How to Deploy AI Agents<\/a> \u2014 the production checklist: memory persistence is a Layer 2 (Orchestration) requirement<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><em>This post is part of The Agentic Protocol&#8217;s Work series \u2014 the connective infrastructure layer beneath every autonomous pipeline. See also: <a href=\"https:\/\/www.theagenticprotocol.com\/index.php\/how-to-build-ai-agent-python\/\">How to Build an AI Agent With Python<\/a>.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>AI agent memory in 2026 has a critical default that trips every builder eventually: agents forget everything between sessions. The conversation history you built carefully in the Claude API Tutorial exists only in RAM. Restart the script, and the agent has never met you before. The production solution is a three-tier memory architecture. In-context memory &#8230; <a title=\"AI Agent Memory: Build Agents That Remember Across Sessions\" class=\"read-more\" href=\"https:\/\/www.theagenticprotocol.com\/index.php\/ai-agent-memory-python\/\" aria-label=\"Read more about AI Agent Memory: Build Agents That Remember Across Sessions\">Read more<\/a><\/p>\n","protected":false},"author":1,"featured_media":588,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[740,741,744,742,743],"class_list":["post-587","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-work-agentic-ai","tag-agent-memory-python-2026","tag-ai-agent-memory","tag-langchain-memory-2026","tag-langgraph-memory-tutorial","tag-persistent-ai-agent-memory"],"_links":{"self":[{"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/posts\/587","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/comments?post=587"}],"version-history":[{"count":1,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/posts\/587\/revisions"}],"predecessor-version":[{"id":589,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/posts\/587\/revisions\/589"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/media\/588"}],"wp:attachment":[{"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/media?parent=587"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/categories?post=587"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.theagenticprotocol.com\/index.php\/wp-json\/wp\/v2\/tags?post=587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}