๐ฏ What You'll Learn Today
This tutorial is also available in Google Colab here or for download here
Joint Initiative: This tutorial is part of a collaboration between AI Product Engineer and the Nebius Academy.
This tutorial explores message handling in LangGraph using LangChain's message types. We'll learn how to properly type and structure messages in our conversation state, which is essential for building robust conversational agents.
Key Concepts Covered
- Message Types and Hierarchy
- Type-Safe Message Storage
- Message Content Access
- State Integration with LangChain
Let's break it down step by step:
from typing import TypedDict
!pip install langchain-core
from langchain_core.messages import BaseMessage, HumanMessage
Step 1: Understanding LangChain Message Types
LangChain provides a robust message hierarchy starting with BaseMessage
BaseMessage
โโโ HumanMessage (User inputs)
โโโ AIMessage (Model responses)
โโโ SystemMessage (System instructions)
โโโ FunctionMessage (Function calls/returns)
Using these typed messages ensures proper handling of different message sources and enables advanced features like role-based processing.
class State(TypedDict):
"""Enhanced state container using LangChain's message types.
This version of State enforces that all messages must be instances
of BaseMessage, providing several advantages:
1. Role-based message handling (human vs AI vs system)
2. Structured message content
3. Metadata support
4. Chain/agent compatibility
Attributes:
messages: A strongly-typed list that only accepts BaseMessage objects
or its subclasses (HumanMessage, AIMessage, etc.)
Note:
BaseMessage is abstract - you'll always use one of its concrete
subclasses like HumanMessage or AIMessage in practice.
"""
messages: list[BaseMessage]
Step 2: Working with Typed Messages
Let's explore how to create, store, and access properly typed messages in our LangGraph state.
Initialize state with typed message list
state: State = {"messages": []}
Create a new message using HumanMessage Note: HumanMessage is a concrete implementation of BaseMessage
hello_message = HumanMessage(content="Hello!")
Add message to state
state["messages"].append(hello_message)
Access message content Messages provide structured access to their content
print(state["messages"][0].content) # Output: "Hello!"
Message Type Benefits
Using LangChain's message types provides several advantages:
- Type Safety:
- Can't accidentally add non-message objects to state
- IDE autocompletion for message properties
- Runtime type checking
- Role-Based Processing:
- Easy to filter messages by type
- Clear distinction between message sources
- Proper handling of different message roles
- Structured Data:
- Consistent message format
- Built-in metadata support
- Serialization capabilities
Key Takeaways
- Message Typing: Using BaseMessage ensures type safety and proper message handling
- Message Hierarchy: Different message types for different roles (Human, AI, System)
- Content Access: Structured access to message content and metadata
- State Integration: Properly typed messages integrate seamlessly with LangGraph
Common Pitfalls to Avoid
- Using raw strings instead of message objects
- Mixing different message types inappropriately
- Forgetting to specify message type in state definition
- Not handling message metadata when needed
Next Steps
- Implement AIMessage handling for responses
- Add SystemMessage for conversation configuration
- Explore message additional_kwargs for metadata
- Implement message filtering by type
Example of filtering messages by type
Advanced usage example
human_messages = [
msg for msg in state["messages"]
if isinstance(msg, HumanMessage)
]
๐ฌ๐ง Chapter