English
Week 1: Fundamentals
02. Tool Calling

02. Tool Calling & Structured Output

Overview

In this session, we evolve from regex-based parsing to structured tool calling using Pydantic and OpenAI's Function Calling API. This gives us type safety, validation, and reliability.

Why Function Calling?

Regex ParsingFunction Calling
❌ Fragile✅ Robust
❌ No type checking✅ Full type safety
❌ Inconsistent✅ Guaranteed format
❌ Hard to maintain✅ Easy to extend

Key Concepts

Pydantic for Tool Schemas

Define your tool parameters with full type safety:

from pydantic import BaseModel, Field
 
class CalculatorInput(BaseModel):
    """Input schema for calculator tool"""
    expression: str = Field(
        description="Mathematical expression to evaluate"
    )

Converting to OpenAI Format

OpenAI expects a specific JSON schema format:

def pydantic_to_openai_function(model, name, description):
    return {
        "type": "function",
        "function": {
            "name": name,
            "description": description,
            "parameters": model.model_json_schema()
        }
    }

The Function Calling Flow

Hands-on Practice

In the notebook, you will:

  1. Define Pydantic Schemas - Calculator, Search, Weather tools
  2. Convert to OpenAI Format - Build the tools array
  3. Implement Tool Handlers - Actual execution logic
  4. Build FunctionCallingAgent - Complete agent class
  5. Integrate Tavily - Real web search capability

Tavily Integration

If you have a Tavily API key, you get real web search:

from tavily import TavilyClient
client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
results = client.search(query, max_results=5)

Comparison: Before vs After

Before (Regex Parsing)

# Fragile - breaks easily
pattern = r"Action:\s*(\w+)\[(.+?)\]"
match = re.search(pattern, response)

After (Function Calling)

# Robust - guaranteed structure
response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
    tool_choice="auto"
)
 
for tool_call in response.message.tool_calls:
    # Type-safe, validated arguments
    args = json.loads(tool_call.function.arguments)

Best Practices

⚠️

Tip: Always validate tool arguments even with Function Calling. LLMs can still make mistakes.

  1. Descriptive Schemas - Better descriptions = better tool selection
  2. Error Handling - Wrap tool execution in try/except
  3. Logging - Log all tool calls for debugging
  4. Timeouts - External APIs can hang

Next Steps

Ready to build something real? Head to the Weekend Project to create a Personal Research Assistant!