Tutorial Image: LangGraph Tutorial: Conversation Flow Control - Unit 2.2 Exercise 8

LangGraph Tutorial: Conversation Flow Control - Unit 2.2 Exercise 8

Learn to master conversation flow control in LangGraph! This tutorial covers state-based routing, end condition detection, and step transitions to build seamless, context-aware conversational agents.

๐ŸŽฏ What You'll Learn Today

LangGraph Tutorial: Conversation Flow Control - Unit 2.2 Exercise 8

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 implement effective flow control in LangGraph conversations, managing transitions between states and end conditions.

Key Concepts Covered

  1. Flow State Management
  2. End Condition Detection
  3. Step Transition Logic
  4. State-based Routing
from typing import Annotated, Literal, TypedDict
!pip install langchain-core
!pip install langgraph
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
from langgraph.graph.message import add_messages

Step 1: State Definition for Flow Control

Define state structure for managing conversation flow.

Why This Matters

Flow control state is crucial because

  1. Tracks conversation progress
  2. Maintains execution context
  3. Enables decision making

Debug Tips

  1. State Structure Issues:
  • Verify message initialization
  • Check tool_outputs structure
  • Monitor state consistency
class State(TypedDict):
    """State container for flow control.

    Notes:
        - messages tracks conversation history
        - tool_outputs needed for completion check
        - Both fields must be properly initialized

    Attributes:
        messages: Conversation history with proper typing
        tool_outputs: List of tool execution results
    """

    messages: Annotated[list[BaseMessage], add_messages]
    tool_outputs: list[str]

Step 2: Flow Control Implementation

Implement logic to determine next conversation steps.

Why This Matters

Flow control is critical because

  1. Manages conversation progression
  2. Handles termination conditions
  3. Ensures proper tool execution

Debug Tips

  1. Flow Control Issues:
  • Check message type handling
  • Verify end conditions
  • Monitor state transitions
def get_next_step(state: State) -> Literal["tool_selector", "end"]:
    """Determine next conversation step based on state.

    Notes:
        - Checks conversation state
        - Detects termination conditions
        - Routes to appropriate next step

    Args:
        state: Current conversation state

    Returns:
        Literal["tool_selector", "end"]: Next step identifier
    """
    # Safety check for empty state
    if not state.get("messages"):
        return "end"

    # Get latest message for analysis
    last_message = state["messages"][-1]

    # Check termination conditions
    # 1. User indicates completion
    if isinstance(last_message, HumanMessage):
        if any(word in last_message.content.lower() for word in ["thanks", "bye"]):
            return "end"

    # 2. Tool execution completed
    if isinstance(last_message, AIMessage) and state.get("tool_outputs"):
        return "end"

    # Continue with tool selection if no end conditions met
    return "tool_selector"

Step 3: System Demonstration

Test the flow control system with various scenarios.

Why This Matters

Testing verifies

  1. Proper state transitions
  2. End condition detection
  3. Flow control logic
def demonstrate_flow_control():
    """Demonstrate flow control with various test cases."""
    # Test cases
    test_cases = [
        {
            "name": "Empty State",
            "state": {"messages": [], "tool_outputs": []},
            "description": "Testing empty state handling",
        },
        {
            "name": "User Goodbye",
            "state": {
                "messages": [HumanMessage(content="thanks for your help!")],
                "tool_outputs": [],
            },
            "description": "Testing user termination",
        },
        {
            "name": "Tool Completion",
            "state": {
                "messages": [AIMessage(content="Here's the result")],
                "tool_outputs": ["calculation complete"],
            },
            "description": "Testing tool completion",
        },
        {
            "name": "Continue Conversation",
            "state": {
                "messages": [HumanMessage(content="what's the weather?")],
                "tool_outputs": [],
            },
            "description": "Testing continuation",
        },
    ]

    print("Flow Control Demonstration")
    print("=========================")

    for case in test_cases:
        print(f"\nTest: {case['name']}")
        print(f"Description: {case['description']}")

        # Process flow
        next_step = get_next_step(case["state"])

        # Print results
        print("State:")
        print(f"- Messages: {len(case['state']['messages'])} messages")
        print(f"- Tool Outputs: {len(case['state']['tool_outputs'])} outputs")
        print(f"Next Step: {next_step}")

        print("-" * 50)

Common Pitfalls

  1. Not handling empty states
  2. Missing message type checks
  3. Incomplete end conditions
  4. Poor state validation

Key Takeaways

  1. Always verify state integrity
  2. Check message types explicitly
  3. Define clear end conditions
  4. Maintain consistent routing

Expected Output

Flow Control Demonstration
Test: Empty State
Description: Testing empty state handling

## State

- Messages: 0 messages
- Tool Outputs: 0 outputs

## Next Step: end

Test: User Goodbye
Description: Testing user termination

## State

- Messages: 1 messages
- Tool Outputs: 0 outputs

## Next Step: end

Test: Tool Completion
Description: Testing tool completion

## State

- Messages: 1 messages
- Tool Outputs: 1 outputs

## Next Step: end

Test: Continue Conversation
Description: Testing continuation

## State

- Messages: 1 messages
- Tool Outputs: 0 outputs

## Next Step: tool_selector
if __name__ == "__main__":
    demonstrate_flow_control()

Rod Rivera

๐Ÿ‡ฌ๐Ÿ‡ง Chapter