LangGraph Part-2: Complete Message Management System With Reducers, Annotated Framework & Dynamic Memory
In Part-1 of this LangGraph Blog Series, we understood the foundation of LangGraph — Graph structure, Nodes, Edges, Conditional Routing, State system, and Graph Execution.
Now in Part-2, we upgrade our knowledge and turn LangGraph into a real conversation system that:
Stores message history
Appends interactions
Repairs memory structure
Removes old messages
Trims state to prevent token explosion
Summarises past conversations
Automatically manages conversation loops
This is the stage where LangGraph starts resembling real-world conversational intelligence:
Chatbots, customer support workflows, AI interview engines, tutoring systems, and research assistance.
🔷 Why Message Management Matters in LangGraph?
Large Language Models don’t just need input → response.
They need evolving memory.
add_messages instructs StateGraph to append new messages
Without this, chatbot memory breaks.
Reducer Overview
Reducer is a rule applied automatically every time a node returns messages.
add_messages reducer does:
1️⃣ Take old messages
2️⃣ Take returned messages
3️⃣ Merge both
4️⃣ Store result into next State
Example Test:
my_list = add_messages(
[HumanMessage("Hi! I'm Oscar."),
AIMessage("Hey, Oscar. How can I assist you?")],
[HumanMessage("Could you summarize today's news?")]
)
print(my_list)
This ensures all LangGraph and model dependencies are installed.
Step-2: Import All Required Modules
from langgraph.graph import MessagesState, StateGraph, START, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
from langgraph.graph.message import RemoveMessage
import random
Step-8: Node-4 → Trim Old Messages for Token Safety
def trim_messages(state: State) -> State:
# keep last 6 messages
if len(state["messages"]) > 6:
remove = [RemoveMessage(id=m.id) for m in state["messages"][:-6]]
return {"messages": remove}
return state
Why keep 6 messages?
2 cycles of conversation
enough context
no memory overload
Step-9: Node-5 → Summarize When Loop Too Big
def summarize(state: State) -> State:
message_texts = "\n".join([m.content for m in state["messages"]])
summary = llm.invoke(
[HumanMessage(f"Summarize the following conversation:\n{message_texts}")]
)
remove_all = [
RemoveMessage(id=m.id) for m in state["messages"]
]
return State(
summary=summary.content,
messages=remove_all
)
Flow:
1️⃣ all messages compressed
2️⃣ summary saved
3️⃣ old messages deleted
Step-10: Conditional Routing Logic
def router(state: State):
last = state["messages"][-1].content.lower()
if "no" in last:
return "end"
if len(state["messages"]) > 12:
return "summarize"
return "ask_question"
In the previous two parts, we built a strong foundation of LangGraph fundamentals—nodes, edges, message states, conditional routing, reducers, summarization loops, and graph orchestration.
In Part-1 of this LangGraph Blog Series, we understood the foundation of LangGraph — Graph structure, Nodes, Edges, Conditional Routing, State system, and Graph Execution.
Now in Part-2, we upgrade our knowledge and turn LangGraph into a real conversation system.
Modern AI workflows need more than just a prompt and a model call. Real applications require memory, state transitions, branching logic, routing decisions, and orchestration of multiple AI models. This is where LangGraph enters the scene.