LangChain Memory Agent

repository·main·Indexed 19 days ago

https://github.com/langchain-ai/memory-agent

A LangGraph-based ReAct agent implementation designed to manage long-term user memories across different conversation threads using a persistent Store. The agent utilizes a similarity-search based retrieval process and an `upsert_memory` tool to save and update user preferences scoped by `user_id`. It supports integration with Anthropic and OpenAI models and can be deployed and tested via LangGraph Studio.

Tokens
2.3K
Snippets
7
Records
11
Agent score
66%

What's inside memory-agent

  1. How the ReAct Memory Agent works

    main

    The agent follows a ReAct (Reasoning and Acting) pattern to manage long-term memory:

    1. Memory Retrieval: The chatbot reads from the graph's Store to list and access previously extracted memories.
    2. Memory Storage: When the agent decides to call a tool to save information, LangGraph routes the request to the store_memory node, which persists the data to the Store.
    3. Scoping: Memories are scoped to a configurable user_id, enabling the agent to learn user preferences across different conversational threads.
  2. Quickstart: Deploy the Memory Agent with LangGraph Studio

    main

    To deploy and interact with the ReAct Memory Agent, use LangGraph Studio. This allows you to test the agent's ability to persist information across different conversational threads using a user_id scope.

    1. Install LangGraph Studio if you haven't already.
    2. Initialize environment variables:
      cp .env.example .env
    3. Configure API keys in your .env file (see 'Setup Model' for details).
    4. Open in LangGraph Studio and navigate to the memory_agent graph.
    5. Test persistence:
      • Send messages to the bot (e.g., "My name is Alice").
      • Create a new thread using the + icon.
      • Chat again; the bot should recall the information from the previous thread.
      • Use the "memory" button in the UI to review saved memories.
    cp .env.example .env
  3. Customize the Memory Agent

    main

    You can extend or modify the agent in several ways:

    • Memory Structure: The default structure is content: str, context: str. You can redefine this to suit your needs.
    • Additional Tools: Connect the bot to other functions to increase its utility.
    • Model Selection: Change the default model by providing a provider/model-name string (e.g., openai/gpt-4) via configuration.
    • Prompts: The default system prompts are located in src/memory_agent/prompts.py and can be updated via configuration.
  4. Evaluate Memory Agent performance

    main

    To ensure the agent saves high-quality memories and chooses tools correctly, use an evaluation-driven approach:

    1. Evaluation Sets: Start with an evaluation set and add cases as you encounter errors in production.
    2. Integration Tests: Reference tests/integration_tests/test_graph.py for existing example evaluation cases.
    3. LangSmith Integration: The project uses LangSmith's @unit decorator to sync evaluations to the LangSmith platform, allowing for easier optimization and root-cause analysis.
  5. Configure the LLM Model and API Keys

    main

    The agent requires a chat model and corresponding API keys defined in a .env file. The default model is anthropic/claude-3-5-sonnet-20240620.

    Anthropic Configuration

    To use Anthropic models:

    1. Obtain an API key from the Anthropic Console.
    2. Add to .env:
      ANTHROPIC_API_KEY=your-api-key

    OpenAI Configuration

    To use OpenAI models:

    1. Obtain an API key from the OpenAI Platform.
    2. Add to .env:
      OPENAI_API_KEY=your-api-key
    3. Note: If using OpenAI, you must update the model configuration to a compatible string like openai/gpt-4.
    model: anthropic/claude-3-5-sonnet-20240620
  6. Configure the MemoryAgent runtime context

    main

    The MemoryAgent graph requires a Runtime[Context] to function. The Context object must provide the following fields which are accessed during the call_model node execution:

    • user_id: Used as a key to partition memories in the BaseStore (e.g., ("memories", user_id)).
    • model: The identifier for the chat model to be loaded via utils.load_chat_model.
    • system_prompt: A template string used to construct the system message. It must support the following placeholders:
      • {user_info}: Injected with the formatted <memories> XML block.
      • {time}: Injected with the current ISO timestamp.

    Additionally, the runtime.store must implement the BaseStore interface to support asearch for similarity-based memory retrieval.

  7. Use upsert_memory to store or update agent memories

    main

    The upsert_memory tool allows the agent to persist information about a user in a long-term database. It supports both creating new memories and updating existing ones to prevent duplicates or correct errors.

    Arguments

    • content: The primary information to remember (e.g., "User likes coffee").
    • context: Background information related to the memory (e.g., "Mentioned during breakfast").
    • memory_id: (Optional) Provide this UUID if you are updating an existing memory instead of creating a new one.

    Implementation Note

    This tool uses InjectedToolArg for user_id and store. These arguments are handled by the LangGraph runtime and are not visible to or provided by the LLM model itself.

    await upsert_memory(
        content="User expressed interest in learning about French.",
        context="This was mentioned while discussing career options in Europe.",
        memory_id=existing_uuid  # Only if updating
    )
  8. Configure the agent using the Context class

    main

    The Context class is the main configuration object for the memory graph system. It defines the runtime environment for the agent, including user identification, the language model to be used, and the system prompt.

    Attributes:

    • user_id: A string identifying the user for conversation memory. Defaults to "default".
    • model: The name of the language model in provider/model-name format (e.g., "anthropic/claude-sonnet-4-5-20250929").
    • system_prompt: The system prompt used to guide the agent. Defaults to the value in prompts.SYSTEM_PROMPT.

    Environment Variable Overrides If you do not provide these values during initialization, the class will attempt to fetch them from environment variables using the uppercase version of the attribute name:

    • USER_ID
    • MODEL
    • SYSTEM_PROMPT
    from memory_agent.context import Context
    
    # Explicit configuration
    ctx = Context(
        user_id="user_123",
        model="openai/gpt-4o",
        system_prompt="You are a helpful assistant."
    )
    
    # Configuration via defaults (will use environment variables if present)
    ctx = Context()
  9. Use the MemoryAgent graph to extract and store memories

    main

    The graph object is the primary entrypoint for the memory extraction process. It is a compiled LangGraph StateGraph that uses a ReAct pattern to retrieve relevant past memories, include them in a system prompt, and invoke a language model to decide if new memories should be stored using the upsert_memory tool.

    Workflow

    1. call_model: Retrieves recent memories from the BaseStore using a similarity search based on the last few messages. It formats these memories into a <memories> block and injects them into the system prompt along with the current timestamp.
    2. route_message: A conditional router that checks if the model's last message contains tool_calls. If it does, it routes to store_memory; otherwise, it ends the graph.
    3. store_memory: Executes all upsert_memory tool calls concurrently and returns the results as tool messages to the conversation history.
    4. Loop: After storing memories, the graph routes back to call_model to allow the model to respond to the successful storage operation.
    from memory_agent.graph import graph
    
    # The graph is a compiled LangGraph object
    # You can invoke it with a State object and a Runtime containing Context
    # result = await graph.ainvoke(initial_state, config=...) 
  10. Define the State schema for the memory agent graph

    main

    The State class defines the shared data structure used within the LangGraph agent graph. It uses a dataclass with kw_only=True to ensure all fields are passed as keyword arguments.

    Currently, the state tracks the conversation history via the messages field. This field is annotated with add_messages, which is a LangGraph reducer that allows new messages to be appended to the existing list rather than overwriting it.

    from memory_agent.state import State
    from langchain_core.messages import HumanMessage
    
    # Example of how the state structure looks conceptually
    state = State(messages=[HumanMessage(content="Hello")])