Tutorial Image: LangGraph Tutorial: Intelligent Tool Selection System - Unit 2.2 Exercise 4

LangGraph Tutorial: Intelligent Tool Selection System - Unit 2.2 Exercise 4

Learn how to build an intelligent tool selection system in LangGraph that uses message type management and state tracking to decide which tools to invoke based on user input. This tutorial explores message hierarchy, conversation flow control, and robust logic for selecting and reasoning about tools for various tasks.

๐ŸŽฏ What You'll Learn Today

LangGraph Tutorial: Intelligent Tool Selection System - Unit 2.2 Exercise 4

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 build a robust tool selection system using LangGraph, implementing proper message typing and state management.

Key Concepts Covered

  1. Message Type Hierarchy
  2. State Management with TypedDict
  3. Tool Selection Logic
  4. Conversation Flow Control
import uuid
from typing import Annotated, TypedDict
!pip install langchain-core
!pip install langgraph
from langchain_core.messages import (
    AIMessage,
    BaseMessage,
    HumanMessage,
    SystemMessage,
    ToolMessage,
)
from langgraph.graph.message import add_messages

Step 1: State Definition

We define our state structure to manage the conversation and tool selection.

Why This Matters

Proper state structure is crucial because

  1. Enables type-safe message handling
  2. Maintains tool availability tracking
  3. Provides clear tool selection state

Debug Tips

  1. State Structure Issues:

    • Verify TypedDict fields are properly annotated
    • Check message list initialization
    • Ensure tool_name can be None
class State(TypedDict):
    """State container for tool selection system.

    Attributes:
        messages: Conversation history with proper message typing
        available_tools: List of tools the agent can access
        tool_name: Currently selected tool (or None if no selection)
    """

    messages: Annotated[list[BaseMessage], add_messages]
    available_tools: list[str]
    tool_name: str | None

Step 2: System Initialization

Set up the initial system state with proper context and instructions.

Why This Matters

System initialization is essential because

  1. Sets up consistent starting state
  2. Provides clear tool instructions
  3. Establishes conversation context

Debug Tips

  1. Initialization Issues:

    • Verify SystemMessage content
    • Check AIMessage formatting
    • Ensure message list structure
def initialize_system() -> list[BaseMessage]:
    """Initialize the system with instructions and context.

    Returns:
        list[BaseMessage]: Initial system messages
    """
    return [
        SystemMessage(
            content="""
I am an AI assistant that can help you with various tasks.
Available tools:
- Calculator: For mathematical calculations
- Weather: For checking weather information
- Search: For finding general information
            """
        ),
        AIMessage(content="Hello! I'm ready to help. What would you like to know?"),
    ]

Step 3: Message Type Management

Implement message type handling and retrieval.

Why This Matters

Message type management is crucial because

  1. Ensures proper conversation flow
  2. Maintains message type safety
  3. Enables targeted message processing

Debug Tips

  1. Message Retrieval Issues:

    • Add logging for message type checks
    • Verify message order handling
    • Check type comparison logic
def get_last_message_by_type(state: State, message_type) -> BaseMessage | None:
    """Retrieve the most recent message of a specific type.

    Args:
        state: Current conversation state
        message_type: Type of message to find

    Returns:
        BaseMessage | None: Latest matching message or None
    """
    for message in reversed(state["messages"]):
        if isinstance(message, message_type):
            return message
    return None

Step 4: Tool Selection Logic

Implement intelligent tool selection based on message content.

Why This Matters

Tool selection logic is important because

  1. Determines appropriate tool for user needs
  2. Maintains conversation coherence
  3. Provides clear selection reasoning

Debug Tips

  1. Selection Logic Issues:

    • Monitor keyword matching
    • Verify tool availability
    • Check message generation
def tool_selector(state: State) -> State:
    """Select appropriate tool based on message content.

    Args:
        state: Current conversation state

    Returns:
        State: Updated state with tool selection
    """
    if not state.get("messages"):
        return {**state, "messages": initialize_system()}

    last_human = get_last_message_by_type(state, HumanMessage)
    if not last_human:
        return state

    message = last_human.content.lower()
    tool_name = None
    reasoning = ""

    if "weather" in message:
        tool_name = "check_weather"
        reasoning = "I'll use the weather tool to check weather conditions."
    elif any(word in message for word in ["calculate", "compute", "+", "-", "*", "/"]):
        tool_name = "calculator"
        reasoning = "I'll use the calculator tool to help with this calculation."
    else:
        tool_name = "search"
        reasoning = "I'll use the search tool to find this information."

    tool_message = ToolMessage(
        content=f"Selected tool: {tool_name}",
        tool_call_id=str(uuid.uuid4()),
        name="tool_selector",
    )
    ai_message = AIMessage(content=reasoning)

    return {
        **state,
        "tool_name": tool_name,
        "messages": state["messages"] + [tool_message, ai_message],
    }

Step 5: System Demonstration

Demonstrate the tool selection system in action.

Why This Matters

System demonstration is valuable because

  1. Verifies system functionality
  2. Shows expected behavior
  3. Provides usage patterns

Debug Tips

  1. Demonstration Issues:

    • Monitor state transitions
    • Verify message sequence
    • Check output formatting
def demonstrate_tool_selection():
    """Demonstrate tool selection with various queries."""
    state = {
        "messages": initialize_system(),
        "available_tools": ["calculator", "check_weather", "search"],
        "tool_name": None,
    }

    queries = [
        "What's the weather in Paris?",
        "Calculate 2 + 2",
        "Who won the World Cup?",
    ]

    print("Tool Selection Demonstration")
    print("==========================")

    for query in queries:
        state["messages"].append(HumanMessage(content=query))
        result = tool_selector(state)

        print(f"\nUser Query: {query}")
        tool_msg = get_last_message_by_type(result, ToolMessage)
        ai_msg = get_last_message_by_type(result, AIMessage)

        if tool_msg:
            print(f"Tool Selection: {tool_msg.content}")
        if ai_msg:
            print(f"Assistant: {ai_msg.content}")

        print(f"Selected Tool: {result['tool_name']}")
        print("-" * 40)

Common Pitfalls

  1. Not handling missing messages properly
  2. Incorrect message type comparison
  3. Mutable state modifications
  4. Incomplete tool selection logic

Key Takeaways

  1. Message type safety is crucial
  2. Immutable state updates prevent bugs
  3. Clear tool selection logic improves reliability
  4. Proper demonstration validates functionality

Next Steps

  1. Add tool selection confidence scoring
  2. Implement multi-tool selection
  3. Add conversation context tracking
  4. Implement tool usage analytics

Expected Output

Tool Selection Demonstration
User Query: What's the weather in Paris?
Tool Selection: Selected tool: check_weather
Assistant: I'll use the weather tool to check weather conditions.

## Selected Tool: check_weather

User Query: Calculate 2 + 2
Tool Selection: Selected tool: calculator
Assistant: I'll use the calculator tool to help with this calculation.

## Selected Tool: calculator

User Query: Who won the World Cup?
Tool Selection: Selected tool: search
Assistant: I'll use the search tool to find this information.

## Selected Tool: search
if __name__ == "__main__":
    demonstrate_tool_selection()

Rod Rivera

๐Ÿ‡ฌ๐Ÿ‡ง Chapter