Tutorial Image: LangGraph Tutorial: Creating Custom LangChain Tools - Unit 2.2 Exercise 2

LangGraph Tutorial: Creating Custom LangChain Tools - Unit 2.2 Exercise 2

Discover how to create powerful custom tools in LangChain with this hands-on tutorial. Learn to define and implement tools for mathematical calculations and weather simulations, following modern best practices.

๐ŸŽฏ What You'll Learn Today

LangGraph Tutorial: Creating Custom LangChain Tools - Unit 2.2 Exercise 2

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 and implement custom tools in LangChain, focusing on proper tool definition, validation, and error handling using modern practices.

Key Concepts Covered

  1. Tool Definition and Decoration
  2. Modern Tool Invocation
  3. Error Handling
  4. Mathematical Operations
  5. Current LangChain Best Practices
import math
from datetime import datetime
#!pip install langchain-core
import numexpr
from langchain_core.tools import tool

Step 1: Calculator Tool Implementation

We implement a mathematical expression calculator tool.

Why This Matters

Proper tool implementation is crucial because

  1. Ensures safe expression evaluation
  2. Provides input validation
  3. Handles errors gracefully
  4. Maintains type safety

Debug Tips

  1. Tool Invocation:

    • Use .invoke() instead of direct calling
    • Monitor input validation
    • Track error cases
    • Verify output types
@tool
def calculator(expression: str) -> str:
    """Evaluate mathematical expressions safely.

    Args:
        expression: Mathematical expression (e.g., "2 * pi + 5")

    Returns:
        String result or error message
    """
    local_dict = {
        "pi": math.pi,
        "e": math.e,
        "sin": math.sin,
        "cos": math.cos,
        "tan": math.tan,
        "sqrt": math.sqrt,
        "abs": abs,
    }

    try:
        cleaned_expression = expression.strip()
        if not cleaned_expression:
            return "Error: Empty expression"

        result = numexpr.evaluate(
            cleaned_expression,
            global_dict={},
            local_dict=local_dict,
        )

        if isinstance(result, (int, float)):
            return f"{float(result):.6f}".rstrip("0").rstrip(".")
        return str(result)

    except Exception as e:
        return f"Error evaluating expression: {e!s}"

Step 2: Weather Tool Implementation

We implement a simulated weather information tool.

Why This Matters

Modern tool implementation is crucial because

  1. Follows current best practices
  2. Ensures forward compatibility
  3. Provides consistent behavior
  4. Enables proper testing

Debug Tips

  1. Modern Usage:

    • Use .invoke() method
    • Check return types
    • Monitor error handling
    • Validate output format
@tool
def check_weather(location: str) -> str:
    """Simulate weather information retrieval.

    Args:
        location: Location to check weather for

    Returns:
        Simulated weather information
    """
    try:
        cleaned_location = location.strip()
        if not cleaned_location:
            return "Error: Empty location"

        current_time = datetime.now().strftime("%H:%M")

        return (
            f"Weather for {cleaned_location} at {current_time} (Mock Data):\n"
            f"Temperature: 22ยฐC\n"
            f"Condition: Sunny\n"
            f"Humidity: 60%"
        )

    except Exception as e:
        return f"Error checking weather: {e!s}"

Step 3: Tool Demonstration

We implement demonstration functions using modern tool invocation.

Why This Matters

Proper demonstration is crucial because

  1. Shows correct usage patterns
  2. Avoids deprecation warnings
  3. Follows best practices
  4. Ensures future compatibility

Debug Tips

  1. Invocation:

    • Use .invoke() not direct calls
    • Check return values
    • Monitor error cases
    • Verify output formats
def demonstrate_tools():
    """Demonstrate tool usage with current practices."""
    print("\nCalculator Examples:")
    expressions = ["2 + 2", "pi * 2", "sin(pi/2)", "sqrt(16)", "invalid expression", ""]

    calc_tool = calculator
    weather_tool = check_weather

    for expr in expressions:
        # Use invoke() instead of direct calling
        result = calc_tool.invoke(expr)
        print(f"Expression: {expr!r}")
        print(f"Result: {result}\n")

    print("\nWeather Examples:")
    locations = ["Paris", "New York", "", "Tokyo"]

    for loc in locations:
        # Use invoke() instead of direct calling
        result = weather_tool.invoke(loc)
        print(f"Location: {loc!r}")
        print(f"Result: {result}\n")
def main():
    """Run the tool demonstration."""
    print("LangChain Tools Demonstration")
    print("============================")
    demonstrate_tools()

if __name__ == "__main__":
    main()

Common Pitfalls

  1. Using direct tool calls instead of .invoke()
  2. Missing input validation
  3. Poor error messages
  4. Insufficient documentation
  5. Unsafe expression evaluation

Key Takeaways

  1. Modern Invocation: Use .invoke() method
  2. Tool Definition: Proper decoration
  3. Error Handling: Clear messages
  4. Documentation: Complete docstrings

Next Steps

  1. Add async tool support
  2. Implement tool chaining
  3. Add input validation
  4. Enhance error handling
  5. Add tool metadata

Expected Output

Calculator Examples

Expression: '2 + 2'
Result: 4
Expression: 'pi * 2'
Result: 6.283185

Weather Examples

Location: 'Paris'
Result: Weather for Paris at 14:30 (Mock Data):
Temperature: 22ยฐC
Condition: Sunny
Humidity: 60%

Rod Rivera

๐Ÿ‡ฌ๐Ÿ‡ง Chapter