Tutorial Image: LangGraph Tutorial: Tool Execution with Configuration Management - Unit 2.1 Exercise 3

LangGraph Tutorial: Tool Execution with Configuration Management - Unit 2.1 Exercise 3

Learn to execute tools in LangGraph with secure configuration management, including API key handling, state tracking, and error recovery. This tutorial ensures robust tool execution and consistent state management for reliable operations.

๐ŸŽฏ What You'll Learn Today

LangGraph Tutorial: Tool Execution with Configuration Management - Unit 2.1 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 implement tool execution in LangGraph with proper configuration management and API key handling. We combine robust tool execution with secure configuration management.

Key Concepts Covered

  1. Configuration Management
  2. Tool Execution Flow
  3. Error Handling
  4. State Management
import json
import os
from typing import Annotated, Any, TypedDict
!pip install langchain-core
!pip install langgraph
!pip install pydantic-settings
from langchain_community.tools import TavilySearchResults
from langchain_core.messages import BaseMessage
from langgraph.graph.message import add_messages
from pydantic_settings import BaseSettings

Step 1: Configuration Setup

We establish our configuration management using Pydantic.

Why This Matters

Proper configuration management is crucial because

  1. Ensures secure API key handling
  2. Provides consistent tool setup
  3. Supports different environments
  4. Enables easy testing

Debug Tips

  1. Configuration Issues:

    • Verify environment variables
    • Check settings initialization
    • Monitor API key handling
    • Test fallback behavior
MOCK_TAVILY_KEY = "mock_tavily_api_key_12345"

class Settings(BaseSettings):
    """Configuration management for tool execution.

    This class manages tool configuration with:
    1. API key handling
    2. Environment variable support
    3. Default value management

    Attributes:
        tavily_api_key: API key for Tavily integration
    """

    tavily_api_key: str = MOCK_TAVILY_KEY

Step 2: State Definition

We define our state structure for tool execution.

Why This Matters

Proper state structure is crucial because

  1. Maintains execution context
  2. Tracks tool operations
  3. Supports error recovery
  4. Enables debugging
class State(TypedDict):
    """State container for tool execution.

    This state implementation tracks:
    1. Message history with special handling
    2. Tool call specifications
    3. Tool execution results

    Attributes:
        messages: Conversation history with proper annotation
        tool_calls: Tool call specifications
        tool_outputs: Results from tool executions
    """

    messages: Annotated[list[BaseMessage], add_messages]
    tool_calls: list[dict]
    tool_outputs: list[Any]

Step 3: Tool Executor Implementation

We implement the tool execution logic with proper configuration handling.

Why This Matters

Robust tool execution is crucial because

  1. Ensures reliable operations
  2. Handles errors gracefully
  3. Maintains consistent output
  4. Supports debugging
def setup_tool_environment():
    """Initialize the tool environment with configuration.

    Returns:
        tuple: Settings and tool instance
    """
    settings = Settings()
    os.environ["TAVILY_API_KEY"] = settings.tavily_api_key
    tavily_tool = TavilySearchResults()
    return settings, tavily_tool

settings = Settings()

os.environ["TAVILY_API_KEY"] = settings.tavily_api_key

tavily_tool = TavilySearchResults()

return settings, tavily_tool

def tool_executor(state: State) -> State:
    """Execute tools with proper configuration and error handling.

    Args:
        state: Current system state with tool calls

    Returns:
        Updated state with tool outputs
    """
    if not state.get("tool_calls"):
        return {"tool_outputs": []}

    # Setup tool environment
    _, tavily_tool = setup_tool_environment()
    tool_call = state["tool_calls"][-1]

    try:
        if tool_call["tool_name"] == "TavilySearchResults":
            output = tavily_tool.invoke(tool_call["args"])
            return {"tool_outputs": [json.dumps(output)]}
    except Exception as e:
        return {"tool_outputs": [json.dumps({"error": str(e)})]}

    return {"tool_outputs": []}

Step 4: Usage Demonstration

Example showing the complete tool execution flow.

Debug Tips

  1. Testing Flow:

    • Verify configuration loading
    • Check tool initialization
    • Monitor execution results
    • Validate error handling
def demonstrate_tool_execution():
    """Demonstrates the complete tool execution workflow."""
    # Initialize test state
    initial_state = {
        "tool_calls": [
            {"tool_name": "TavilySearchResults", "args": {"query": "capital of France"}}
        ],
        "messages": [],
        "tool_outputs": [],
    }

    # Execute tool
    result = tool_executor(initial_state) 

Display results


    print("Tool Execution Results:")
    print(f"Tool output received: {bool(result['tool_outputs'])}")
    if result["tool_outputs"]:
        print("Output structure:", json.dumps(result["tool_outputs"][0], indent=2))

initial_state = {
        "tool_calls": [
            {"tool_name": "TavilySearchResults", "args": {"query": "capital of France"}}
        ],
        "messages": [],
        "tool_outputs": [],
    }

Execute tool

result = tool_executor(initial_state)

Display results

    print("Tool Execution Results:")
    print(f"Tool output received: {bool(result['tool_outputs'])}")
    if result["tool_outputs"]:
        print("Output structure:", json.dumps(result["tool_outputs"][0], indent=2))

if __name__ == "__main__":
    demonstrate_tool_execution()

Common Pitfalls

  1. Missing API key configuration
  2. Improper environment setup
  3. Incorrect error handling
  4. Poor state management

Key Takeaways

  1. Configuration: Proper settings management is essential
  2. Tool Setup: Clean initialization process
  3. Execution: Robust error handling
  4. State: Consistent state management

Next Steps

  1. Implement real API key management
  2. Add comprehensive error handling
  3. Enhance configuration options
  4. Add execution logging
  5. Implement security measures

Expected Output

Tool Execution Results

Tool output received: True
Output structure: {
  "query": "capital of France",
  "results": [...]
}

Rod Rivera

๐Ÿ‡ฌ๐Ÿ‡ง Chapter