LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
43 small wins to finish your pathNext lesson

Remembering the conversation

Every invoke so far started from nothing. A checkpointer saves the conversation after each step, and a thread_id says which conversation a call belongs to.

Ravi asks about one order and then another. With lesson 8's agent, the second call has no idea the first happened.

Exampleagent.py
from langchain.agents import create_agent
from shop_model import ShopModel
from tools import lookup_order

agent = create_agent(ShopModel(), tools=[lookup_order])
Example
first = agent.invoke({"messages": [{"role": "user", "content": "Where is A17?"}]})
second = agent.invoke({"messages": [{"role": "user", "content": "And C40?"}]})

print(len(first["messages"]), len(second["messages"]))
print([m.text for m in second["messages"] if m.type == "human"])

Each call returned its own four messages. The conversation you pass in is all the agent has, so the second call never saw A17.

A checkpointer and a thread

Exampleagent.py
from langchain.agents import create_agent
from shop_model import ShopModel
from tools import lookup_order
from langgraph.checkpoint.memory import InMemorySaver


agent = create_agent(ShopModel(), tools=[lookup_order], checkpointer=InMemorySaver())

A checkpointer saves the agent's state, the conversation included, after every step, and reads it back at the start of the next. InMemorySaver keeps it in a Python dictionary.

Example
ravi = {"configurable": {"thread_id": "ravi-1"}}

agent.invoke({"messages": [{"role": "user", "content": "Where is A17?"}]}, ravi)
result = agent.invoke({"messages": [{"role": "user", "content": "And C40?"}]}, ravi)

print(len(result["messages"]))
print([m.text for m in result["messages"] if m.type == "human"])

The second call passed one new message and got back eight. A thread is one conversation, named by thread_id in the config; everything saved under that id came back and the new message was added to the end.

Example
state = agent.get_state(ravi)
print(len(state.values["messages"]))
print(state.values["messages"][-1].text)

get_state reads what the checkpointer saved for a thread without running the agent. Its values are the state: here the eight messages, ending with the answer about C40.

Another thread starts empty

Example
mei = {"configurable": {"thread_id": "mei-1"}}
result = agent.invoke({"messages": [{"role": "user", "content": "Hello"}]}, mei)

print(len(result["messages"]))

Mei's thread holds only her two messages, even though Ravi's is in the same checkpointer. Each thread's history is kept apart.

A dictionary forgets on exit
InMemorySaver is gone when the program ends. A checkpointer backed by a database is what you want in production, such as Postgres or SQLite, each a separate package with the same interface.
Try it yourself
  • Invoke the checkpointed agent without the config and read the error.
  • Print agent.get_state(mei).values["messages"] after Mei's call.
  • Ask a third question in Ravi's thread and predict the message count before you run it.

Slow is fine. Stopping is the only problem.