Vercel AI SDK — Módulo de Tool Calling
Un módulo autocontenido para tool calling usando el Vercel AI SDK con Google (Gemini) como proveedor. Cubre definición de tools, schemas de tools, ejecución de tools, múltiples tools, resultados de tools, validación de tools y errores de tools.
Es independiente: no depende de los módulos vercel-ai-sdk-text-generation ni vercel-ai-sdk-chat. No usa streaming, por lo que no hay client.ts: actions.ts se llama directamente desde Server Components o Client Components.
Flujo conceptual
AI
↓
Tool
↓
Database/API
↓
Result
↓
AI
El modelo decide cuándo llamar a una tool y con qué argumentos. Tu función execute es donde ocurre la llamada real a la Database/API. El resultado vuelve al modelo, que puede llamar a otra tool o producir su respuesta final, hasta TOOL_LOOP_CONFIG.maxSteps rondas.
Dependencias
ai— el core del Vercel AI SDK (generateText,tool,isStepCount, y las clases de error relacionadas con tools)@ai-sdk/google— el adaptador del proveedor Google (Gemini)zod— se usa para definir el schema de parámetros de cada tool
Nota de versión: el AI SDK renombró más de una vez su helper para limitar pasos (
maxSteps→stepCountIs→isStepCount). Este módulo usaisStepCount. Si tu versión instalada deaino lo exporta, cambiá el import enservice.tspor el que esté disponible en tu versión; el uso (isStepCount(n)pasado astopWhen) es idéntico en lo demás.
Estructura de archivos
types.ts ToolDefinition contract, run params/result, tool loop config, errors
tools.ts Tool registry — register your own tools here
service.ts Internal logic: builds AI SDK tools, runs the loop, normalizes errors — do not modify
actions.ts Server Action — import into Server or Client Components
.env.local Environment variable for the Google API key
Configuración
-
Definí y registrá tus tools. Cada tool necesita un nombre, una descripción (el modelo la lee para decidir cuándo usarla), un schema de Zod para sus parámetros y una función
execute, que es donde llamás a tu Database/API real.import { z } from "zod"; import { registerTool } from "@/lib/ai-tools/tools"; registerTool({ name: "getOrderStatus", description: "Look up the current status of a customer order by its ID.", parameters: z.object({ orderId: z.string().describe("The order ID, e.g. 'ORD-1234'"), }), execute: async ({ orderId }) => { // Replace with your real Database/API call. const order = await db.orders.findUnique({ where: { id: orderId } }); if (!order) throw new Error(`Order ${orderId} not found`); return { status: order.status, updatedAt: order.updatedAt }; }, });Registralo durante el inicio de la app, de la misma forma en que registrarías un
ConversationStoreen el módulo de chat. -
Agregá tu API key de Google AI en
.env.local. Podés conseguir una en https://aistudio.google.com/apikey
Uso
import { runWithTools } from "@/lib/ai-tools/actions";
const result = await runWithTools({
prompt: "What's the status of order ORD-1234?",
system: "You are a helpful customer support assistant.",
});
if (result.success) {
console.log(result.data.text); // the model's final answer
for (const call of result.data.toolCalls) {
console.log(call.toolName, call.args, call.result ?? call.error);
}
} else {
console.error(result.error.message);
}
result.data.toolCalls es la traza completa de cada llamada a tools hecha durante la ejecución, en orden: cada entrada tiene un result o un error, nunca ambos. A esto se reducen los "resultados de tools" y los "errores de tools": podés ver exactamente qué pasó en cada paso de la Database/API, no solo el texto final del modelo.
Limitar qué tools están disponibles
Por defecto, todas las tools registradas están disponibles para el modelo. Para restringir una ejecución específica a un subconjunto:
const result = await runWithTools({
prompt: "What's the status of order ORD-1234?",
toolNames: ["getOrderStatus"],
});
Si pasás un nombre que no está registrado, la llamada falla con INVALID_REQUEST antes de hacer cualquier llamada al modelo.
Manejo de errores
{
code:
| "RATE_LIMITED"
| "INVALID_REQUEST"
| "PROVIDER_ERROR"
| "TOOL_VALIDATION_ERROR"
| "TOOL_EXECUTION_ERROR"
| "UNKNOWN",
message: string, // safe to show in the UI
retryAfter?: number, // seconds, only present for RATE_LIMITED
}
TOOL_VALIDATION_ERROR— el modelo llamó a una tool con argumentos que no coinciden con su schema de Zod, o intentó llamar a una tool que no se le habilitó. Esto detiene la ejecución.- Un
executefallido (tu llamada a la Database/API lanzando un error) no detiene la ejecución: el AI SDK le devuelve el fallo al modelo para que pueda reintentar, usar otra tool o explicarle el fallo al usuario. Lo vas a ver como una entradaerroren elToolCallTracede esa tool, no como un fallo de nivel superior deAIResult.
Rate limiting
El mismo limitador de ventana deslizante en memoria que en los otros módulos (RATE_LIMIT_CONFIG en types.ts, por defecto 20 requests/minuto por instancia del servidor), más el manejo normalizado de las respuestas 429 del propio proveedor.
Loop de tools
TOOL_LOOP_CONFIG.maxSteps en types.ts (por defecto 5) limita cuántos viajes de ida y vuelta AI → Tool → AI puede hacer una sola llamada a runWithTools. Subilo si tu caso de uso necesita encadenar más llamadas a tools antes de responder; bajalo para fallar rápido ante loops descontrolados.