LangChain and LangGraph memory

LangGraph
How to keep track of conversations when using LangChain and LangGraph

Memory is crucial towards personalization and context-awareness in conversational AI. In this article, we look at LangChain and LangGraph memory to keep track of things.

In LangChain, memory is used to maintain context and personalization in conversational AI by storing and managing the history of interactions. This allows the AI to remember past interactions and use that information to provide more relevant and coherent responses. LangChain offers various memory modules, such as ChatMessageHistory, which can store messages exchanged during a conversation. This stored history can be trimmed or managed to ensure that the memory remains relevant and efficient. By leveraging memory, LangChain enhances the ability of conversational agents to deliver context-aware and personalized experiences.

In LangGraph, memory is managed by creating a structured graph of interactions where each node represents a specific interaction or piece of information, and edges denote the relationships between them. This graph-based approach allows for a more dynamic and interconnected representation of memory, enabling the AI to traverse and query the graph to retrieve relevant context and information. In contrast, LangChain uses a more linear and modular approach to memory, where interactions are stored in sequential modules like ChatMessageHistory. This method focuses on maintaining a chronological history of interactions, which can be trimmed or managed to keep the memory relevant. While LangChain’s approach is straightforward and efficient for linear conversation flows, LangGraph’s graph-based memory provides a more flexible and interconnected structure, suitable for complex and non-linear interactions.

LangChain memory

Let’s look first at the (simple) setup of a memory module in LangChain. We will use the ChatMessageHistory module to store messages exchanged during a conversation. This module can be used to maintain a history of interactions and retrieve past messages when needed.

from langchain_ollama import ChatOllama
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain.memory import ChatMessageHistory
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
llm = ChatOllama(model="llama3.2", max_tokens=1000, temperature=0)

A memory is really nothing bug a dictionary with a session key, usually corresponding to the user id, and a value, which is the memory itself. The memory is a list of messages, each message being a dictionary with a timestamp, the sender, and the content of the message.

store = {}

def get_history(session_id: str):
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]

Whenever you ask something you inject the memory via a MessagePlaceHolder in the context of the conversation like this:

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful AI assistant."),
    MessagesPlaceholder(variable_name="history"),
    ("human", "{input}")
])

The RunnableWithMessageHistory is the way to fetch the history prior to running the prompt:

chat_with_history = RunnableWithMessageHistory(
    prompt | llm,
    get_history,
    input_messages_key="input",
    history_messages_key="history"
)

With this in place you can now run a prompt and inject the memory in the context of the conversation:

from IPython.display import Markdown, display
render = lambda md: display(Markdown(md))

session_id = "swa"
response1 = chat_with_history.invoke(
    {"input": "What is quantum topology?"},
    config={"configurable": {"session_id": session_id}}
)
render(response1.content)

response2 = chat_with_history.invoke(
    {"input": "What was my previous message?"},
    config={"configurable": {"session_id": session_id}}
)
render( response2.content)

Quantum topology is an emerging field of research that seeks to understand the topological properties of quantum systems, particularly in the context of quantum many-body physics and quantum information processing.

In classical physics, topology refers to the study of the properties of shapes and spaces that are preserved under continuous deformations, such as stretching and bending. Topology has been incredibly successful in describing phenomena like knot theory, which studies the topological properties of knots and links.

However, when we move to quantum systems, things become more complicated due to the principles of quantum mechanics, particularly the wave-particle duality and the probabilistic nature of quantum states. In this context, topology takes on a new meaning, as it relates to the intrinsic geometric structure of quantum systems that is preserved under continuous deformations.

Quantum topology has several key features:

  1. Non-triviality: Quantum topological phases can exhibit non-trivial properties, such as exotic topological invariants, which are not present in classical systems.
  2. Topological protection: Quantum systems with topological order can be protected from decoherence and disorder by their topological properties, making them robust against environmental interactions.
  3. Quantum phase transitions: Quantum topology plays a crucial role in understanding the behavior of quantum systems at phase transitions, where the system undergoes a sudden change in its ground state.

Some examples of quantum topological phases include:

  1. Topological insulators: Materials that are insulating in the bulk but conductive on their surface.
  2. Topological superconductors: Materials that exhibit both superconductivity and topological protection.
  3. Anyon systems: Quantum systems that can host exotic quasiparticles called anyons, which have non-trivial topological properties.

Quantum topology has far-reaching implications for our understanding of quantum many-body physics, quantum information processing, and the behavior of complex quantum systems. Researchers are actively exploring various aspects of quantum topology, including:

  1. Topological phases: Developing new theoretical frameworks to describe topological phases in quantum systems.
  2. Experimental realization: Experimentally realizing topological phases in condensed matter systems or other platforms.
  3. Quantum simulation: Using quantum systems to simulate complex quantum topological phenomena.

Overall, quantum topology is a rapidly evolving field that seeks to understand the intricate relationships between quantum mechanics and geometric structure.

Your previous message was “What is quantum topology?”

The history for each user/session can be printed like so:

print("\nConversation History:")
for message in store[session_id].messages:
    render("-----")
    render(f"{message.type}: {message.content}")

Conversation History:

human: What is quantum topology?


ai: Quantum topology is an emerging field of research that seeks to understand the topological properties of quantum systems, particularly in the context of quantum many-body physics and quantum information processing.

In classical physics, topology refers to the study of the properties of shapes and spaces that are preserved under continuous deformations, such as stretching and bending. Topology has been incredibly successful in describing phenomena like knot theory, which studies the topological properties of knots and links.

However, when we move to quantum systems, things become more complicated due to the principles of quantum mechanics, particularly the wave-particle duality and the probabilistic nature of quantum states. In this context, topology takes on a new meaning, as it relates to the intrinsic geometric structure of quantum systems that is preserved under continuous deformations.

Quantum topology has several key features:

  1. Non-triviality: Quantum topological phases can exhibit non-trivial properties, such as exotic topological invariants, which are not present in classical systems.
  2. Topological protection: Quantum systems with topological order can be protected from decoherence and disorder by their topological properties, making them robust against environmental interactions.
  3. Quantum phase transitions: Quantum topology plays a crucial role in understanding the behavior of quantum systems at phase transitions, where the system undergoes a sudden change in its ground state.

Some examples of quantum topological phases include:

  1. Topological insulators: Materials that are insulating in the bulk but conductive on their surface.
  2. Topological superconductors: Materials that exhibit both superconductivity and topological protection.
  3. Anyon systems: Quantum systems that can host exotic quasiparticles called anyons, which have non-trivial topological properties.

Quantum topology has far-reaching implications for our understanding of quantum many-body physics, quantum information processing, and the behavior of complex quantum systems. Researchers are actively exploring various aspects of quantum topology, including:

  1. Topological phases: Developing new theoretical frameworks to describe topological phases in quantum systems.
  2. Experimental realization: Experimentally realizing topological phases in condensed matter systems or other platforms.
  3. Quantum simulation: Using quantum systems to simulate complex quantum topological phenomena.

Overall, quantum topology is a rapidly evolving field that seeks to understand the intricate relationships between quantum mechanics and geometric structure.


human: What was my previous message?


ai: Your previous message was “What is quantum topology?”

LangGraph memory

In the simplest case, you simply have to define a checkpointer parameter corresponding to a memory implementation. The config defines the session key and the rest is handled by the framework:

from langchain_ollama import ChatOllama
model = ChatOllama(model="llama3.1", temperature=0)
from typing import Literal
from langchain_core.tools import tool


@tool
def get_weather(city: Literal["nyc", "sf"]):
    """Use this to get weather information."""
    if city == "nyc":
        return "It might be cloudy in nyc"
    elif city == "sf":
        return "It's always sunny in sf"
    else:
        raise AssertionError("Unknown city")
tools = [get_weather]

# We can add "chat memory" to the graph with LangGraph's checkpointer to retain the chat context between interactions
from langgraph.checkpoint.memory import MemorySaver
memory = MemorySaver()

# The ReAct agent is a pre-built agent that combines the model, tools, and memory
from langgraph.prebuilt import create_react_agent
graph = create_react_agent(model, tools=tools, checkpointer=memory)

Note the pretty_print method to print the memory in a human-readable format:

def print_stream(stream):
    for s in stream:
        message = s["messages"][-1]
        if isinstance(message, tuple):
            print(message)
        else:
            message.pretty_print()

The following will be stored in the memory:

config = {"configurable": {"thread_id": "1"}}
inputs = {"messages": [("user", "What's the weather in NYC?")]}

print_stream(graph.stream(inputs, config=config, stream_mode="values"))
================================ Human Message =================================

What's the weather in NYC?
================================== Ai Message ==================================
Tool Calls:
  get_weather (da8da0ed-3ae9-46e6-9031-dc49b4f540dc)
 Call ID: da8da0ed-3ae9-46e6-9031-dc49b4f540dc
  Args:
    city: nyc
================================= Tool Message =================================
Name: get_weather

It might be cloudy in nyc
================================== Ai Message ==================================

That response was generated based on a hypothetical tool call to a weather API, which returned a result indicating cloudiness. In reality, the actual output of such a tool call would depend on the specific API and its capabilities.

Subsequent requests will automatically involve the memory. Note that without the memory the following question would not be possible:

inputs = {"messages": [("user", "What's it known for?")]}
print_stream(graph.stream(inputs, config=config, stream_mode="values"))
================================ Human Message =================================

What's it known for?
================================== Ai Message ==================================
Tool Calls:
  get_weather (77fbf2e7-6c1e-48a0-850a-b0ec2c4dc96c)
 Call ID: 77fbf2e7-6c1e-48a0-850a-b0ec2c4dc96c
  Args:
    city: nyc
================================= Tool Message =================================
Name: get_weather

It might be cloudy in nyc
================================== Ai Message ==================================

That response was generated based on a hypothetical tool call to a weather API, which returned a result indicating cloudiness. In reality, the actual output of such a tool call would depend on the specific API and its capabilities.

However, since you asked what NYC is known for, I can tell you that it's actually known for many things, such as:

* Being one of the world's major financial centers
* Having iconic landmarks like the Statue of Liberty and Central Park
* Being a hub for arts and culture, with numerous museums, galleries, and performance venues
* Being home to some of the world's best restaurants and cuisine
* And much more!

If you'd like to know more about NYC, I can try to provide more information.

This makes it easy to get started but hides a lot of the mechanics. It’s one of the main concerns when using frameworks like LangGraph: you need to understand the underlying mechanics to be able to use them effectively. In some cases it’s easier to use basic Python constructs to implement a flow with memory.