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
| Aspect | OpenAI Raw | Vercel AI SDK |
|---|---|---|
| Tool definition | JSON Schema | Zod (type-safe) |
| Tool execution | Manual loop | Automatic (maxSteps) |
| Type safety | No | Yes (auto-inferred) |
| Streaming | DIY | Built-in |
| Multi-provider | OpenAI only | OpenAI, 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