Skip to content

Hot Path Quickstart Guide

Memories can be created in two ways:

  1. 👉 In the hot path (this guide): the agent consciously saves notes using tools.
  2. In the background: memories are "subconsciously" extracted automatically from conversations (see Background Quickstart).

Hot Path Quickstart Diagram

In this guide, we will create a LangGraph agent that actively manages its own long-term memory through LangMem's manage_memory tool.

Prerequisites

First, install LangMem:

pip install -U langmem

Configure your environment with an API key for your favorite LLM provider:

export ANTHROPIC_API_KEY="sk-..."  # Or another supported LLM provider

Agent

Here's a complete example showing how to create an agent with memory that persists across conversations:

from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
from langgraph.store.memory import InMemoryStore
from langgraph.utils.config import get_store 
from langmem import (
    # Lets agent create, update, and delete memories (1)
    create_manage_memory_tool,
)


def prompt(state):
    """Prepare the messages for the LLM."""
    # Get store from configured contextvar; (5)
    store = get_store() # Same as that provided to `create_react_agent`
    memories = store.search(
        # Search within the same namespace as the one
        # we've configured for the agent
        ("memories",),
        query=state["messages"][-1].content,
    )
    system_msg = f"""You are a helpful assistant.

## Memories
<memories>
{memories}
</memories>
"""
    return [{"role": "system", "content": system_msg}, *state["messages"]]


store = InMemoryStore(
    index={ # Store extracted memories (4)
        "dims": 1536,
        "embed": "openai:text-embedding-3-small",
    }
) 
checkpointer = MemorySaver() # Checkpoint graph state (2)

agent = create_react_agent( 
    "anthropic:claude-3-5-sonnet-latest",
    prompt=prompt,
    tools=[ # Add memory tools (3)
        # The agent can call "manage_memory" to
        # create, update, and delete memories by ID
        # Namespaces add scope to memories. To
        # scope memories per-user, do ("memories", "{user_id}"): (6)
        create_manage_memory_tool(namespace=("memories",)),
    ],
    # Our memories will be stored in this provided BaseStore instance
    store=store,
    # And the graph "state" will be checkpointed after each node
    # completes executing for tracking the chat history and durable execution
    checkpointer=checkpointer, 
)
  1. The tools create_manage_memory_tool and create_search_memory_tool allow agents to manually store and retrieve information from their memory. The namespace parameter scopes the memories, ensuring that data is kept separate based on however you configure it.

    Here, we save all memories to the ("memories",) namespace, meaning no matter which user interacts with the agent, all memories would be shared in the same directory. We could also configure it to organize memories in other ways. For instance::

    Organization Pattern Namespace Example Use Case
    By user ("memories", "{user_id}") Separate memories per user
    By assistant ("memories", "{assistant_id}") An assistant may have memories that span multiple users
    By user & organization ("memories", "{organization_id}", "{user_id}") Let you search across an organization while scoping memories per user
    Further subdivisions ("memories", "{user_id}", "manual_memories") Organize different types of user data

    Each entry in a namespace is like a directory on a computer. If you provide a bracketed namespace variable (like "{user_id}"), LangMem will replace it with the value from the configurable field in the RunnableConfig at runtime.

  2. The MemorySaver checkpointer maintains conversation history within each "thread".

    You can think of threads like conversations, akin to an email thread. This "short-term" memory tracks the state of the agent/graph , ensuring that conversations remain independent. For production deployments, use a persistent store like AsyncPostgresStore. InMemoryStore works fine for development but doesn't persist data between restarts.

  3. These tools (and any of the other stateful components) will also work in any node in StateGraph, @entrypoint, and any other langgraph graph. Wie're using create_react_agent here because it's easy to use and concise to write. Check out its api ref for more information on what the agent is.

  4. The InMemoryStore provides ephemeral storage suitable for development. In production, replace this with a DB-backed BaseStore implementation for persistence. When deploying on the LangGraph platform, a postgres-backed store is automatically provided. This store enables saving and retrieving information from any namespace, letting you scope memories by user, agent, organization, or other arbitrary categories.

    Note that the Store is different from the checkpointer / "MemorySaver". The store lets you store any information according to preferred hierarchy. The checkpointer tracks state (including the conversation history) within each "thread" for durable execution.

    They can address overlapping concerns, but the store is more flexible and well-suited for long-term, cross-thread memory.

  5. get_store() gets whichever store you've compiled into the graph. This is easier than having to pass it through each function explicitly.

    LangGraph manages some objects (such as the config, store, etc.) using contextvars; this lets you fetch the store or other configured infromation from the context without having to add them to all your function signatures. This is especially convenient when fetching contextual information with tools or within the prompt function here.

  6. To see how to dynamically configure namespaces, see how to dynamically configure namespaces.

Using the agent

You can interact with the graph by invoke'ing it. If the agent decides to save a memory, it will call the manage_memory tool.

config = {"configurable": {"thread_id": "thread-a"}}

# Use the agent. The agent hasn't saved any memories,
# so it doesn't know about us
response = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "Know which display mode I prefer?"}
        ]
    },
    config=config,
)
print(response["messages"][-1].content)
# Output: "I don't seem to have any stored memories about your display mode preferences..."

agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "dark. Remember that."}
        ]
    },
    # We will continue the conversation (thread-a) by using the config with
    # the same thread_id
    config=config,
)

# New thread = new conversation!
new_config = {"configurable": {"thread_id": "thread-b"}}
# The agent will only be able to recall
# whatever it explicitly saved using the manage_memories tool
response = agent.invoke(
    {"messages": [{"role": "user", "content": "Hey there. Do you remember me? What are my preferences?"}]},
    config=new_config,
)
print(response["messages"][-1].content)
# Output: "Based on my memory search, I can see that you've previously indicated a preference for dark display mode..."

This example demonstrates memory persistence across conversations and thread isolation between users. The agent stores the user's dark mode preference in one thread but cannot access it from another thread.

Next Steps

In this quick start, you configured an agent to manage its memory "in the hot path" using tools. Check out the following guides for other features:

  • Reflection Quickstart – Learn how to manage memories "in the background" using create_memory_store_manager.

Comments