Lesson 3 of 6·10 min read

Tool Calling & Structured Output

LLMs can do more than generate text — with Tool Calling they execute actions, and with Structured Output they deliver type-safe, machine-readable data. Both together turn a chatbot into a full-fledged application.

Tool Definitions with Zod

What Is Tool Calling?

The LLM decides based on the user request which tool (function) should be called and generates the matching parameters. The actual execution happens in your code.

Tool Definition

import { tool } from 'ai'
import { z } from 'zod'

const weatherTool = tool({
  description: 'Fetches the current weather for a city',
  parameters: z.object({
    city: z.string().describe('The name of the city'),
    unit: z.enum(['celsius', 'fahrenheit']).default('celsius'),
  }),
  execute: async ({ city, unit }) => {
    const data = await fetchWeatherAPI(city, unit)
    return { temperature: data.temp, condition: data.condition }
  },
})

Tools in Route Handlers

import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'

export async function POST(req: Request) {
  const { messages } = await req.json()

  const result = streamText({
    model: openai('gpt-4.1'),
    messages,
    tools: {
      getWeather: weatherTool,
      searchProducts: productSearchTool,
      createOrder: orderTool,
    },
  })

  return result.toDataStreamResponse()
}

The LLM decides itself which tool is relevant for the request:

  • "What's the weather?" → getWeather
  • "Show me laptops under $1000" → searchProducts
  • "Order the Dell XPS" → createOrder

Multi-Step Tool Calls

Agentic Tool Calling

For complex tasks, the LLM calls multiple tools sequentially:

const result = streamText({
  model: openai('gpt-4.1'),
  messages,
  tools: { getWeather, searchHotels, bookHotel },
  maxSteps: 5, // Allow up to 5 tool calls
  onStepFinish: ({ stepType, toolResults }) => {
    console.log(`Step: ${stepType}`, toolResults)
  },
})

Example flow:

  1. User: "Book a hotel in Munich for next week with good weather"
  2. LLM → getWeather({ city: 'Munich' }) → "15°C, sunny"
  3. LLM → searchHotels({ city: 'Munich', dates: '...' }) → 5 hotels
  4. LLM → "Here are 5 hotels. Hotel Bayerischer Hof has the best reviews. Shall I book?"
  5. User: "Yes, book the Bayerischer Hof"
  6. LLM → bookHotel({ hotelId: '...', dates: '...' }) → Booking confirmation

Structured Output

generateObject — Type-Safe Outputs

Instead of generating unstructured text, generateObject delivers Zod-validated objects:

import { generateObject } from 'ai'
import { z } from 'zod'

const { object } = await generateObject({
  model: openai('gpt-4.1'),
  schema: z.object({
    title: z.string(),
    summary: z.string().max(200),
    tags: z.array(z.string()).max(5),
    sentiment: z.enum(['positive', 'neutral', 'negative']),
    confidence: z.number().min(0).max(1),
  }),
  prompt: 'Analyze this customer comment: "The product is fantastic!"',
})

// object is type-safe: { title: string, summary: string, ... }

streamObject — Streaming Structured Output

For large objects or progressive UIs:

import { streamObject } from 'ai'

const result = streamObject({
  model: openai('gpt-4.1'),
  schema: productAnalysisSchema,
  prompt: 'Analyze the top 5 products...',
})

for await (const partialObject of result.partialObjectStream) {
  // Progressively update UI
  updateUI(partialObject)
}

Type-Safe Responses

End-to-End Type Safety

The AI SDK + Zod enables complete type safety from schema definition to UI:

  1. Schema definition (Zod): Defines the structure
  2. AI SDK (generateObject): Generates and validates
  3. TypeScript inference: typeof schema._type → TypeScript type
  4. React component: Type-safe props from the generated object

Enum Mode vs. JSON Mode

ModeDescriptionIdeal For
jsonComplete JSON objectComplex analyses, data extraction
toolAs tool call parametersIntegration into tool workflows

Best practice: Use generateObject instead of generateText + manual JSON parsing. The AI SDK guarantees schema-conformant outputs and throws type-safe errors on validation failures.