Miscellaneous

Miscellaneous

How to add memory to LangGraph Agents

LangGraph is built around state. Each node in the graph reads the state, mutates it, and passes it on. Checkpointers persist that state. Threads scope it. The architecture is honest about what it is: a runtime for stateful agent workflows.

The gap is that the state and the threads are bound to the graph, not to the user. When the same user returns three weeks later in a new thread, their preferences and history start over. That is what an external memory layer is for, and Mem0's LangGraph integration is the most direct path through it.


What LangGraph is

LangGraph is a low-level orchestration framework and runtime for building long-running, stateful agents. It is built by the LangChain team and does not require LangChain itself. The two are usually paired (LangGraph for the flow, LangChain for the LLM and tool primitives) but the LangGraph runtime stands on its own.

The core abstractions are small. A StateGraph defines the agent's workflow. Nodes are Python functions that read state and return updates. Edges connect nodes. State is a TypedDict (or MessagesState for chat-shaped flows) that flows through the graph as nodes mutate it. Two special nodes, START and END, define the execution boundaries.

The framework is aimed at complex agent orchestration where the flow itself matters: branching, conditional handoffs, tool loops, human-in-the-loop checkpoints, and multi-agent coordination. For simpler one-shot agents LangChain ships prebuilt architectures. LangGraph is where the work goes when the agent's decision graph has multiple paths.


How LangGraph handles memory natively

LangGraph splits persistence into two layers. The split matters for what each one is and is not built for.

Checkpointers persist graph state. After every super-step, the runtime serializes a snapshot (channel values, next nodes to execute, metadata, timestamps, task info) and writes it through a BaseCheckpointSaver implementation. The shipped backends cover the usual range:

  • InMemorySaver keeps checkpoints in process. Resets on restart.

  • SqliteSaver (and AsyncSqliteSaver) writes to a local SQLite file. Good for development.

  • PostgresSaver (and AsyncPostgresSaver) targets production. Survives restarts and scales horizontally.

  • langchain-azure-cosmosdb adds Cosmos DB as a managed option.

Threads scope checkpoints. A thread is a unique ID that ties a sequence of checkpoints together. Pass thread_id in the configurable portion of the config on every call and the runtime resumes from the latest checkpoint for that thread. New thread, new state.

Store is the second persistence hook, separate from checkpointers. The Store interface persists data across threads as namespaced tuples and supports semantic search via an embedding model. Where checkpointers hold the in-flight state of one workflow, the store holds long-lived information the application wants any future thread to see.

Both abstractions are well-designed primitives. They are also primitives, not products. The application owns what gets put into them.


Where LangGraph's built-in memory stops

The native persistence layer is well-shaped for one specific job: durable graph state for stateful, long-running agents. Outside that shape, the gap shows.

  • Checkpointers persist graph state, not extracted facts. A 50-turn conversation gets stored as 50 messages plus the surrounding channel data. Reconstructing what the user prefers from the raw history is the application's problem.

  • The Store is unopinionated. Semantic search and namespacing are built in, but write logic, fact extraction, deduplication, reconciliation, and consolidation are not. Every team that moves from checkpointer to Store ends up writing the same fact-extraction layer.

  • InMemorySaver loses everything on restart. SqliteSaver pins state to a single machine. PostgresSaver scales horizontally but still leaves the application owning the schema for facts versus raw history.

  • No multi-tenant isolation beyond thread_id. Threads scope a single conversation. There is no built-in user_id or agent_id primitive. Multi-tenant apps stitch user identity into thread names or metadata and then have to enforce isolation themselves.

  • No fact extraction model. What enters the store is what the application puts there. The "what is worth remembering" decision sits in agent code, which means another LLM call the application has to write, prompt, and pay for.

Mem0 is the integration LangGraph users reach for when the gap above is real. The wiring is shorter than the list of limits.


How to add Mem0 to LangGraph

The full setup is documented at docs.mem0.ai/integrations/langgraph. Install pulls LangGraph alongside the Mem0 client:

The integration uses the standard MemoryClient and plugs into a normal StateGraph. The state carries the messages and the user identifier the memory layer scopes on:

from typing import Annotated, TypedDict, List
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from mem0 import MemoryClient

llm = ChatOpenAI(model="gpt-5-mini")
mem0 = MemoryClient()

class State(TypedDict):
    messages: Annotated[List[HumanMessage | AIMessage]

from typing import Annotated, TypedDict, List
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from mem0 import MemoryClient

llm = ChatOpenAI(model="gpt-5-mini")
mem0 = MemoryClient()

class State(TypedDict):
    messages: Annotated[List[HumanMessage | AIMessage]

from typing import Annotated, TypedDict, List
from langgraph.graph import StateGraph, START
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
from mem0 import MemoryClient

llm = ChatOpenAI(model="gpt-5-mini")
mem0 = MemoryClient()

class State(TypedDict):
    messages: Annotated[List[HumanMessage | AIMessage]

The chatbot node does the work. Three steps per turn: retrieve memories, generate, save the exchange.

def chatbot(state: State):
    messages = state["messages"]
    user_id = state["mem0_user_id"]

def chatbot(state: State):
    messages = state["messages"]
    user_id = state["mem0_user_id"]

def chatbot(state: State):
    messages = state["messages"]
    user_id = state["mem0_user_id"]

Graph wiring is the usual LangGraph pattern:




Three things change once the chatbot node owns its own memory:

  • Persistence outlives any thread. A new thread_id next week still has access to every fact stored under mem0_user_id. The agent's continuity is keyed to the user, not to the run.

  • Fact extraction runs server-side. The add call ships the exchange to Mem0 and an extraction model decides what to keep. The next call retrieves a short list of facts, not the raw transcript.

  • Per-user isolation is built in. user_id scopes every search and write. agent_id and run_id provide narrower scopes when the application needs them.

The same pattern composes cleanly with multi-node graphs. A retrieval node can read memories before a planner node decides which tool to call. A reflection node can write a derived fact after an action runs. The state graph stays the place where flow lives. The memory layer stays the place where durable facts live.


Final Note

LangGraph gives every node a place to read and write state. The native persistence layer keeps that state alive across super-steps and threads. What it does not do is decide what is worth keeping past the run or scope facts to the user rather than the workflow. One Mem0 integration, a couple of node-level calls, and the graph picks up persistence keyed to who the user is rather than which run they came in on.

Mem0 is an intelligent, open-source memory layer designed for LLMs and AI agents to provide long-term, personalized, and context-aware interactions across sessions.

Get your free API Key here: app.mem0.ai or self-host mem0 from our open source github repository.

GET TLDR from:

Summarize

Website/Footer

Summarize

Website/Footer

Summarize

Website/Footer

Summarize

Website/Footer