All posts
tutoriallanggraphintegration

Building a LangGraph Agent with Persistent Memory

Isaac Gutiérrez Brugada··7 min read

What Is LangGraph?

LangGraph is a framework for building AI agents as graphs. Instead of a linear chain of LLM calls, you define nodes (functions) and edges (transitions) that form a state machine. This makes it straightforward to build agents with loops, branching logic, tool use, and human-in-the-loop steps.

At the core of LangGraph is state — a typed dictionary that flows through the graph. Each node reads from and writes to this shared state. When the graph finishes, the final state contains the result.

But here's the problem: by default, that state lives only in memory. When the process ends, it's gone.

The Checkpointer Pattern

LangGraph solves persistence through checkpointers. A checkpointer is an object that saves and restores graph state at each step. Every time a node runs, the checkpointer serializes the current state to storage. If the agent crashes, restarts, or needs to resume later, the checkpointer loads the last saved state and continues from where it left off.

LangGraph ships with a built-in checkpointer called MemorySaver:

from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import StateGraph, MessagesState

# Build a simple chatbot graph
graph = StateGraph(MessagesState)
graph.add_node("chatbot", chatbot_node)
graph.set_entry_point("chatbot")

# Compile with in-memory checkpointer
app = graph.compile(checkpointer=MemorySaver())

This works for development. The problem is obvious: MemorySaver stores everything in a Python dictionary. Restart the process and every conversation, every thread, every checkpoint is lost.

For production agents, you need a checkpointer backed by a real database.

MnemoraCheckpointSaver: Drop-In Replacement

Mnemora provides a LangGraph-compatible checkpointer that stores state in DynamoDB and Aurora. It implements the same BaseCheckpointSaver interface, so switching from MemorySaver is a single line change.

Install the SDK with the LangGraph extra:

pip install "mnemora[langgraph]"

Then swap your checkpointer:

from mnemora import MnemoraClient
from mnemora.integrations.langgraph import MnemoraCheckpointSaver

# Create the Mnemora checkpointer
client = MnemoraClient(api_key="mnm_your_key_here")
checkpointer = MnemoraCheckpointSaver(client=client)

That's it. Your graph state now persists across process restarts.

Full Working Example

Here's a complete tool-calling agent with persistent memory. The agent can search the web and remember conversations across sessions.

import asyncio
from typing import Annotated

from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph, MessagesState
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition

from mnemora import MnemoraClient
from mnemora.integrations.langgraph import MnemoraCheckpointSaver


# Define a simple tool
def search_web(query: str) -> str:
    """Search the web for information."""
    return f"Search results for: {query}"


# Set up the LLM with tools
llm = ChatOpenAI(model="gpt-4o-mini")
tools = [search_web]
llm_with_tools = llm.bind_tools(tools)


# Define the chatbot node
def chatbot(state: MessagesState) -> dict:
    response = llm_with_tools.invoke(state["messages"])
    return {"messages": [response]}


# Build the graph
graph = StateGraph(MessagesState)
graph.add_node("chatbot", chatbot)
graph.add_node("tools", ToolNode(tools))
graph.set_entry_point("chatbot")
graph.add_conditional_edges("chatbot", tools_condition)
graph.add_edge("tools", "chatbot")


async def main():
    # Compile with Mnemora checkpointer
    client = MnemoraClient(api_key="mnm_your_key_here")
    checkpointer = MnemoraCheckpointSaver(client=client)
    app = graph.compile(checkpointer=checkpointer)

    # First conversation — thread "user-123"
    config = {"configurable": {"thread_id": "user-123"}}

    result = await app.ainvoke(
        {"messages": [HumanMessage(content="What is LangGraph?")]},
        config=config,
    )
    print(result["messages"][-1].content)

    # Second invocation on the SAME thread — the agent remembers the first message
    result = await app.ainvoke(
        {"messages": [HumanMessage(content="Can you elaborate on the graph part?")]},
        config=config,
    )
    print(result["messages"][-1].content)

    await client.close()


asyncio.run(main())

When you run this, the first invocation creates a checkpoint in Mnemora. The second invocation loads the checkpoint, so the agent sees the full conversation history and can respond in context. Even if you stop the process and restart it, the agent picks up where it left off.

Multi-Session Support with thread_id

LangGraph uses thread_id to isolate conversations. Each thread is an independent state machine with its own checkpoint history. Mnemora maps thread_id to an agent ID and stores checkpoints per-thread.

# Thread for user Alice
alice_config = {"configurable": {"thread_id": "alice-session-1"}}
await app.ainvoke({"messages": [HumanMessage(content="Hello")]}, config=alice_config)

# Thread for user Bob — completely isolated state
bob_config = {"configurable": {"thread_id": "bob-session-1"}}
await app.ainvoke({"messages": [HumanMessage(content="Hi there")]}, config=bob_config)

Alice's conversation history never leaks into Bob's thread. Each thread has its own checkpoint stream stored in Mnemora.

Querying Saved State

You can inspect checkpoints directly through the Mnemora SDK, outside of LangGraph:

from mnemora import MnemoraSync

with MnemoraSync(api_key="mnm_your_key_here") as client:
    # Get the latest state for a thread
    state = client.get_state("user-123")
    print(state.data)

    # List all sessions (threads) for an agent
    sessions = client.list_sessions("user-123")
    print(sessions)

This is useful for building admin dashboards, debugging agent behavior, or auditing conversation history.

Benefits Over MemorySaver

Switching to MnemoraCheckpointSaver gives you:

  • Persistence across restarts. State survives process crashes, deployments, and server reboots.
  • TTL cleanup. Set time-to-live on checkpoints so old threads are automatically cleaned up. No manual garbage collection.
  • Multi-tenant isolation. Each API key scopes to a tenant. Different customers' agent threads are isolated at the database level.
  • Serverless scaling. Mnemora's DynamoDB backend scales automatically. No capacity planning for checkpoint storage.
  • Cross-service access. Multiple services can read and write to the same checkpoint store. A web server can inspect agent state that a background worker created.

Getting Started

  1. Install the SDK: pip install "mnemora[langgraph]"
  2. Get an API key at mnemora.dev/dashboard
  3. Replace MemorySaver() with MnemoraCheckpointSaver(client=client)
  4. Your LangGraph agent now has persistent memory

The full SDK reference is available in the documentation. For questions, open an issue on GitHub.