TechLead
Lección 6 de 8
6 min de lectura
AI SDK

Tool calling y function calling

Permite que modelos de IA llamen funciones y utilicen herramientas para ampliar capacidades

Tool calling en AI SDK

El tool calling (también conocido como function calling) permite que los modelos de IA soliciten la ejecución de funciones específicas. Esto permite a los asistentes interactuar con sistemas externos, obtener datos, realizar cálculos y ejecutar acciones en el mundo real.

Lo que permiten las herramientas

  • Obtención de datos: Clima, precios de acciones, datos de usuarios en tiempo real
  • Cálculos: Matemática compleja o procesamiento de datos
  • APIs externas: Interacción con servicios de terceros
  • Operaciones en base de datos: Consultar y actualizar datos
  • Acciones: Enviar emails, crear eventos de calendario, etc.

Definir herramientas

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

// Define a weather tool
const weatherTool = tool({
  description: 'Get the current weather for a location',
  parameters: z.object({
    location: z.string().describe('The city and state, e.g., San Francisco, CA'),
    unit: z.enum(['celsius', 'fahrenheit']).optional().default('fahrenheit'),
  }),
  execute: async ({ location, unit }) => {
    // Call your weather API here
    const weather = await fetchWeather(location, unit);
    return weather;
  },
});

// Define a calculator tool
const calculatorTool = tool({
  description: 'Perform mathematical calculations',
  parameters: z.object({
    expression: z.string().describe('The math expression to evaluate'),
  }),
  execute: async ({ expression }) => {
    // Safely evaluate the expression
    const result = evaluateMathExpression(expression);
    return { result };
  },
});

Usar herramientas con streamText

// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';

export const maxDuration = 30;

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

  const result = streamText({
    model: openai('gpt-4-turbo'),
    messages,
    tools: {
      weather: tool({
        description: 'Get the current weather for a location',
        parameters: z.object({
          location: z.string(),
        }),
        execute: async ({ location }) => {
          // Simulate weather API call
          return {
            location,
            temperature: 72,
            condition: 'sunny',
          };
        },
      }),
      calculator: tool({
        description: 'Calculate a math expression',
        parameters: z.object({
          expression: z.string(),
        }),
        execute: async ({ expression }) => {
          return { result: eval(expression) }; // Use a safe evaluator in production
        },
      }),
    },
  });

  return result.toDataStreamResponse();
}

Resultados de herramientas en el chat

'use client';

import { useChat } from 'ai/react';

export default function ChatWithTools() {
  const { messages, input, handleInputChange, handleSubmit } = useChat();

  return (
    <div className="max-w-2xl mx-auto p-4">
      {messages.map((message) => (
        <div key={message.id} className="mb-4">
          <strong>{message.role}:</strong>

          {/* Display text content */}
          {message.content && <p>{message.content}</p>}

          {/* Display tool invocations */}
          {message.toolInvocations?.map((tool, index) => (
            <div key={index} className="bg-gray-100 p-2 rounded mt-2">
              <p className="text-sm text-gray-600">
                Called: {tool.toolName}
              </p>
              {tool.state === 'result' && (
                <pre className="text-sm">
                  {JSON.stringify(tool.result, null, 2)}
                </pre>
              )}
            </div>
          ))}
        </div>
      ))}

      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Ask about weather or calculate..."
          className="w-full p-2 border rounded"
        />
      </form>
    </div>
  );
}

Llamadas de herramientas en varios pasos

Los modelos de IA pueden llamar múltiples herramientas en secuencia para completar tareas complejas:

const result = streamText({
  model: openai('gpt-4-turbo'),
  messages,
  tools: {
    searchProducts: tool({
      description: 'Search for products in the catalog',
      parameters: z.object({
        query: z.string(),
        maxResults: z.number().optional().default(5),
      }),
      execute: async ({ query, maxResults }) => {
        const products = await searchCatalog(query, maxResults);
        return products;
      },
    }),
    getProductDetails: tool({
      description: 'Get detailed information about a product',
      parameters: z.object({
        productId: z.string(),
      }),
      execute: async ({ productId }) => {
        const details = await getProduct(productId);
        return details;
      },
    }),
    addToCart: tool({
      description: 'Add a product to the shopping cart',
      parameters: z.object({
        productId: z.string(),
        quantity: z.number().default(1),
      }),
      execute: async ({ productId, quantity }) => {
        await addToCart(productId, quantity);
        return { success: true, message: 'Added to cart' };
      },
    }),
  },
  maxSteps: 5, // Allow up to 5 tool calls
});

Opciones de selección de herramienta

const result = streamText({
  model: openai('gpt-4-turbo'),
  messages,
  tools: { weather, calculator },

  // Let the model decide which tools to use (default)
  toolChoice: 'auto',

  // Force the model to use a specific tool
  // toolChoice: { type: 'tool', toolName: 'weather' },

  // Require the model to use at least one tool
  // toolChoice: 'required',

  // Prevent tool usage
  // toolChoice: 'none',
});

Manejo de errores de herramientas

const weatherTool = tool({
  description: 'Get current weather',
  parameters: z.object({
    location: z.string(),
  }),
  execute: async ({ location }) => {
    try {
      const response = await fetch(
        `https://api.weather.com/${location}`
      );

      if (!response.ok) {
        // Return error message for the AI to handle
        return {
          error: true,
          message: `Could not get weather for ${location}`,
        };
      }

      return await response.json();
    } catch (error) {
      return {
        error: true,
        message: 'Weather service is unavailable',
      };
    }
  },
});

Server Action con herramientas

// app/actions.ts
'use server';

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

export async function askWithTools(prompt: string) {
  const { text, toolResults } = await generateText({
    model: openai('gpt-4-turbo'),
    prompt,
    tools: {
      getCurrentTime: tool({
        description: 'Get the current time',
        parameters: z.object({
          timezone: z.string().optional().default('UTC'),
        }),
        execute: async ({ timezone }) => {
          return new Date().toLocaleString('en-US', { timeZone: timezone });
        },
      }),
    },
  });

  return { text, toolResults };
}

Ejemplo real: asistente de IA

// app/api/assistant/route.ts
import { openai } from '@ai-sdk/openai';
import { streamText, tool } from 'ai';
import { z } from 'zod';

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

  const result = streamText({
    model: openai('gpt-4-turbo'),
    system: `You are a helpful assistant with access to tools.
      Use tools when they would help answer the user's question.
      Always explain what you're doing when using tools.`,
    messages,
    tools: {
      searchWeb: tool({
        description: 'Search the web for current information',
        parameters: z.object({
          query: z.string(),
        }),
        execute: async ({ query }) => {
          // Integrate with search API
          return await searchWeb(query);
        },
      }),
      createReminder: tool({
        description: 'Create a reminder for the user',
        parameters: z.object({
          title: z.string(),
          datetime: z.string(),
          priority: z.enum(['low', 'medium', 'high']).optional(),
        }),
        execute: async ({ title, datetime, priority }) => {
          await createReminder({ title, datetime, priority });
          return { success: true, message: `Reminder "${title}" created` };
        },
      }),
      sendEmail: tool({
        description: 'Send an email on behalf of the user',
        parameters: z.object({
          to: z.string().email(),
          subject: z.string(),
          body: z.string(),
        }),
        execute: async ({ to, subject, body }) => {
          await sendEmail({ to, subject, body });
          return { success: true, message: `Email sent to ${to}` };
        },
      }),
    },
    maxSteps: 3,
  });

  return result.toDataStreamResponse();
}

Consideraciones de seguridad

  • • Valida cuidadosamente todas las entradas con esquemas Zod
  • • Nunca confíes en código o expresiones generadas por IA (evita eval)
  • • Implementa autorización adecuada para herramientas sensibles
  • • Aplica rate limiting para evitar abuso
  • • Registra el uso de herramientas para auditoría y depuración

💡 Puntos clave

  • • Las herramientas extienden capacidades con acciones reales
  • • Define herramientas con descripciones claras y esquemas Zod
  • • Usa maxSteps para interacciones de varios pasos
  • • Maneja errores de herramientas con gracia para mejor UX
  • • Accede a toolInvocations en mensajes para mostrar resultados

📚 Más recursos

Continuar aprendiendo