Tool Use & Function Calling

How agents call tools, function calling patterns, and integration with OpenAI API

Tool Use Design Pattern

Tool Use is the most important pattern in AI Agents — it allows LLMs to go beyond generating text to actually interact with the outside world.

When Do You Need Tool Use?

  • Data Retrieval: Query databases, search APIs, read files
  • Code Execution: Run code, process data
  • Workflow Automation: Send emails, create tickets, deploy
  • Customer Support: Query CRM, knowledge base

Function Calling: How It Works

Function calling has 3 steps:

1. Define functions (schema) → Send to LLM
2. LLM decides which function to call + arguments
3. Code executes function → Return result to LLM

Step 1: Define Tool Schema

import OpenAI from "openai";

const client = new OpenAI();

const tools: OpenAI.ChatCompletionTool[] = [
  {
    type: "function",
    function: {
      name: "get_current_time",
      description: "Get current time in a city",
      parameters: {
        type: "object",
        properties: {
          location: {
            type: "string",
            description: "City name, e.g., 'Tokyo'"
          }
        },
        required: ["location"]
      }
    }
  }
];

Step 2: LLM Decides to Call Tool

const messages: OpenAI.ChatCompletionMessageParam[] = [
  { role: "user", content: "What time is it in Tokyo?" }
];

const response = await client.chat.completions.create({
  model: "gpt-4o-mini",
  messages,
  tools,
  tool_choice: "auto" // LLM decides
});

// LLM returns tool_calls instead of text
const toolCalls = response.choices[0].message.tool_calls;
// → [{ function: { name: "get_current_time", arguments: '{"location":"Tokyo"}' } }]

Step 3: Execute and Return Results

function getCurrentTime(location: string): string {
  const timezones: Record<string, string> = {
    "tokyo": "Asia/Tokyo",
    "new york": "America/New_York",
    "london": "Europe/London"
  };

  const tz = timezones[location.toLowerCase()];
  if (!tz) return JSON.stringify({ error: "Unknown location" });

  const time = new Date().toLocaleString("en-US", { timeZone: tz });
  return JSON.stringify({ location, current_time: time });
}

if (toolCalls) {
  messages.push(response.choices[0].message);

  for (const call of toolCalls) {
    const args = JSON.parse(call.function.arguments);
    const result = getCurrentTime(args.location);

    messages.push({
      role: "tool",
      tool_call_id: call.id,
      content: result
    });
  }

  const finalResponse = await client.chat.completions.create({
    model: "gpt-4o-mini",
    messages
  });

  console.log(finalResponse.choices[0].message.content);
  // → "The current time in Tokyo is 3:30 PM."
}

Tool Use With Vercel AI SDK

Vercel AI SDK simplifies this flow significantly:

import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";

const result = await generateText({
  model: openai("gpt-4o-mini"),
  tools: {
    getCurrentTime: tool({
      description: "Get current time in a city",
      parameters: z.object({
        location: z.string().describe("City name")
      }),
      execute: async ({ location }) => {
        const time = new Date().toLocaleString("en-US", {
          timeZone: "Asia/Tokyo"
        });
        return { location, current_time: time };
      }
    }),
    searchWeb: tool({
      description: "Search for information on the web",
      parameters: z.object({
        query: z.string().describe("Search query")
      }),
      execute: async ({ query }) => {
        return { results: [`Results for: ${query}`] };
      }
    })
  },
  maxSteps: 5, // Automatic agent loop
  prompt: "What time is it in Tokyo?"
});

console.log(result.text);
// Vercel AI SDK handles the tool calling loop automatically!

Comparison: Raw OpenAI vs Vercel AI SDK

AspectOpenAI RawVercel AI SDK
Tool definitionJSON SchemaZod (type-safe)
Tool executionManual loopAutomatic (maxSteps)
Type safetyNoYes (auto-inferred)
StreamingDIYBuilt-in
Multi-providerOpenAI onlyOpenAI, Anthropic, Google…

Multiple Tools

A smart agent with many tools automatically selects the right one:

const result = await generateText({
  model: openai("gpt-4o-mini"),
  tools: {
    getWeather: tool({ /* ... */ }),
    searchFlights: tool({ /* ... */ }),
    bookHotel: tool({ /* ... */ }),
    translateText: tool({ /* ... */ })
  },
  maxSteps: 10,
  prompt: "Find me flights to Tokyo next week — what's the weather like?"
  // Agent will call: searchFlights + getWeather
});

Best Practices

1. Clear Tool Descriptions

// ❌ Bad
tool({ description: "Search" })

// ✅ Good
tool({
  description: "Search products in the database. Returns a list of products with price, description, and rating."
})

2. Validate Input

parameters: z.object({
  email: z.string().email("Invalid email"),
  age: z.number().min(0).max(150),
  date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/)
})

3. Handle Errors Gracefully

execute: async ({ query }) => {
  try {
    const data = await fetchData(query);
    return { success: true, data };
  } catch (error) {
    return {
      success: false,
      error: "Unable to query data. Please try again."
    };
  }
}

Summary

  • Function Calling is the foundational pattern: Schema → LLM picks tool → Execute → Return
  • Vercel AI SDK simplifies tool use with Zod schemas and auto tool loop
  • Write clear, detailed tool descriptions for the LLM to pick the right tool
  • Always validate input and handle errors gracefully