Building AI Agents with LangChain and LangGraph: Part 2 › Memory and Conversation Threads
Building AI Agents with LangChain and LangGraph: Part 2 › Memory and Conversation ThreadsOne section · Sections in this article6 / 9 ↓
Reading tools Definitions & inline links
Toggle tooltip visibility. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
One of the most powerful features of agentic workflows is the ability to maintain context across multiple interactions. This is where memory and conversation threads become essential.
Real applications need agents that can:
- Remember previous interactions
- Build context over time
- Handle interruptions and human feedback loops
- Maintain separate conversation contexts for different users
Understanding Memory and Threads
Memory (Checkpointer): Stores the complete state history of your workflow, allowing agents to "remember" previous interactions and build context over time.
Thread: A unique identifier that groups related conversations together. Different threads maintain separate conversation histories, enabling multi-user applications.
The combination enables sophisticated behaviors:
- Contextual responses based on conversation history
- Resuming interrupted conversations
- Personalization across multiple interactions
Using Pre-Built Agent Patterns
The create_react_agent function is a utility provided by LangGraph for building conversational agents that can reason and act using tools. It implements the ReAct (Reasoning + Acting) pattern, allowing your agent to alternate between thinking and taking actions (like calling tools) until the task is complete.
With create_react_agent, you can:
- Define which tools your agent can use
- Provide a language model for reasoning
- Supply a prompt to guide the agent's behavior
- Optionally add memory (via a checkpointer) to enable multi-turn conversations
This function abstracts away much of the boilerplate, letting you focus on your agent's logic and capabilities.
The ReAct (Reasoning + Acting) pattern alternates between reasoning about what to do and taking actions until the task is complete.
I intentionally sent a nonsensical request to showcase how the agent handles unexpected or unclear inputs.
agent = create_react_agent(
model=llm,
tools=[draft_customer_reply],
prompt="Respond to the user's request using the tools provided.",
checkpointer=InMemorySaver(),
)
config = {"configurable": {"thread_id": "1"}}
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Why are lemons yellow?",
}
]
},
config,
)config = {"configurable": {"thread_id": "1"}}
state = agent.get_state(config)
for message in state.values["messages"]:
message.pretty_print()================================[1m Human Message [0m================================= Why are lemons yellow? ==================================[1m Ai Message [0m================================== I am sorry, I cannot fulfill that request. I can only draft a reply to a customer.
Accessing Conversation History
The get_state() method allows you to inspect the complete conversation history for any thread. This is invaluable for debugging, analytics, and understanding how your agent behaves over time.
# Continue the conversation
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Inform Jon about his delivery status being in-progres.",
}
]
},
config,
)
for m in result["messages"]:
m.pretty_print()================================[1m Human Message [0m================================= Why are lemons yellow? ==================================[1m Ai Message [0m================================== I am sorry, I cannot fulfill that request. I can only draft a reply to a customer. ================================[1m Human Message [0m================================= Inform Jon about his delivery status being in-progres. ==================================[1m Ai Message [0m================================== Could you please provide me with the category of the request so I can draft the reply?
Continuing Conversations
By using the same thread ID, our agent maintains context from previous interactions. Notice how it remembers the earlier question and can reference it in subsequent responses.
Watch how the conversation builds naturally. Each exchange adds to the shared context, enabling more sophisticated interactions that feel human-like in their continuity and understanding.
# Continue the conversation
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Let him know that it's specifically 15 min out.",
}
]
},
config,
)
for m in result["messages"]:
m.pretty_print()================================[1m Human Message [0m================================= Why are lemons yellow? ==================================[1m Ai Message [0m================================== I am sorry, I cannot fulfill that request. I can only draft a reply to a customer. ================================[1m Human Message [0m================================= Inform Jon about his delivery status being in-progres. ==================================[1m Ai Message [0m================================== Could you please provide me with the category of the request so I can draft the reply? ================================[1m Human Message [0m================================= Let him know that it's specifically 15 min out. ==================================[1m Ai Message [0m================================== Could you please provide me with the category of the request so I can draft the reply? I also need the exact message you want to send to Jon.
The impressive part is that the LLM understands "him" refers to Jon, thanks to the maintained conversational context.