๐ฏ What You'll Learn Today
LangGraph Tutorial: Implementing Multi-Response Agent Architecture - Unit 1.3 Exercise 3
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 demonstrates how to create a sophisticated multi-response system in LangGraph using specialized response nodes. We'll build an agent that can generate contextually appropriate responses based on message classification.
Key Concepts Covered
- Specialized Response Nodes
- Message Type Management
- Classification-Based Routing
- State Context Preservation
from typing import Annotated, TypedDict
#!pip install langchain-core
#!pip install langgraph
from langchain_core.messages import AIMessage, BaseMessage
from langgraph.graph import StateGraph
from langgraph.graph.message import add_messages
Step 1: State Definition for Response Management
We define a state structure that supports classification-based response generation.
class State(TypedDict):
"""Advanced state container for response management.
This implementation coordinates three key aspects:
1. Message history with type awareness
2. Classification tracking
3. Confidence management
Attributes:
messages: Conversation messages with proper annotation
classification: Current message category
confidence: Classification confidence score
Note:
The state maintains context across different response
nodes for coherent conversation flow.
"""
messages: Annotated[list[BaseMessage], add_messages]
classification: str
confidence: float
Why This Matters
Specialized response nodes are crucial because they
- Enable context-appropriate responses
- Support different conversation patterns
- Maintain conversation coherence
- Enable sophisticated dialog management
Step 2: Specialized Response Node Implementation
We implement distinct nodes for different response types.
def greeting_node(state: State) -> State:
"""Generate appropriate greeting responses.
This node demonstrates:
1. Type-specific message generation
2. Context preservation
3. State consistency maintenance
The response generation follows this flow:
1. Create appropriate AIMessage
2. Preserve classification context
3. Maintain confidence scores
Args:
state: Current conversation state
Returns:
State: Updated state with greeting response
Example:
>>> state = {"messages": [], "classification": "greeting", "confidence": 0.9}
>>> result = greeting_node(state)
>>> print(result["messages"][0].content)
"Hello there!"
"""
return {
"messages": [AIMessage(content="Hello there!")],
"classification": state["classification"],
"confidence": state["confidence"],
}
def help_node(state: State) -> State:
"""Generate helpful responses to assistance requests.
This node implements:
1. Help-specific message creation
2. Context maintenance
3. State field preservation
Args:
state: Current conversation state
Returns:
State: Updated state with help response
"""
return {
"messages": [AIMessage(content="How can I help you?")],
"classification": state["classification"],
"confidence": state["confidence"],
}
Debug Tips
- Response Generation:
- Verify message type consistency
- Check context preservation
- Monitor state field updates
- Node Integration:
- Validate node connections
- Check classification routing
- Test confidence handling
- Common Errors:
- Type mismatches in messages
- Lost classification context
- Incorrect confidence preservation
Step 3: Graph Construction with Response Nodes
We create a LangGraph structure that incorporates our specialized response nodes.
Initialize response graph
graph = StateGraph(State)
Add specialized response nodes
graph.add_node("greeting", greeting_node)
graph.add_node("help", help_node)
Why This Matters
Proper graph construction ensures
- Correct response routing
- Maintained conversation context
- Appropriate message generation
Key Takeaways
- Response Design:
- Use specialized nodes for different responses
- Maintain message type consistency
- Preserve state context
- Node Management:
- Implement clear node responsibilities
- Handle state transitions properly
- Maintain classification context
Common Pitfalls
- Incorrect message types
- Lost classification context
- Inconsistent state updates
- Poor response routing
Next Steps
- Add more response types
- Implement fallback responses
- Add response validation
- Enable response chaining
Example Usage
initial_state = {"messages": [], "classification": "greeting", "confidence": 0.9}
result = greeting_node(initial_state)
print(f"Response: {result['messages'][0].content}")
print(f"Classification: {result['classification']}")
print(f"Confidence: {result['confidence']}")
Variations and Extensions
- Enhanced Response Patterns:
- Context-aware responses
- Multi-turn dialog handling Example use case: Complex conversation flows
- Advanced Response Selection:
- Confidence-based response variation
- Context-sensitive message generation Scenario: Sophisticated dialog management
Expected Output
Response: Hello there!
Classification: greeting
Confidence: 0.9
๐ฌ๐ง Chapter