< BACK Notas de código escritas a mano en un cuaderno sobre un escritorio de madera con luz suave y nublada y una taza de espresso

Vercel AI SDK en la Práctica: Envía Más Inteligente, No Más Duro

Un cliente de fintech me llamó a finales de 2023, con pánico genuino en la voz, porque su equipo de desarrollo había pasado seis semanas construyendo una interfaz de chat en streaming sobre la API de OpenAI y seguía roto en Safari móvil. Race conditions. Problemas de chunking de tokens. La UI se congelaba a mitad de la respuesta. Seis semanas. Miré su base de código y era exactamente lo que esperarías: una implementación ReadableStream hecha a mano, una capa de gestión de estado personalizada para los tokens en streaming, lógica de reintentos copiada de una respuesta de Stack Overflow de hace tres años. Todo estaba sostenido con cinta.ReadableStream implementation, a bespoke state management layer for the streaming tokens, retry logic copied from a three-year-old Stack Overflow answer. The thing was held together with tape.

Reescribí el núcleo de ello usando Vercel AI SDK en aproximadamente dos días. El streaming funcionó. Safari móvil funcionó. El cliente dejó de llamar.Vercel AI SDK in about two days. Streaming worked. Mobile Safari worked. The client stopped ringing.

Eso no es un pitch. Es solo lo que pasó. Y es por eso que quiero recorrer qué hace realmente este SDK, dónde genuinamente te ahorra tiempo, y dónde todavía te golpearás con paredes.

Qué es Realmente Vercel AI SDK

La gente escucha "Vercel AI SDK" y asume que está bloqueado en la infraestructura de Vercel. No lo está. Puedes ejecutarlo en cualquier servidor Node.js, incluyendo tu propio VPS, Railway, Render, donde sea. Lo que realmente es: una librería TypeScript que abstrae las partes más desordenadas de construir características impulsadas por LLM en una aplicación web.

Hay dos paquetes principales que usarás. ai es el runtime central. @ai-sdk/openai (o @ai-sdk/anthropic, @ai-sdk/google, etc.) son los adaptadores de proveedores. El SDK utiliza una interfaz unificada, así que cambiar de GPT-4o a Claude 3.5 Sonnet es genuinamente un cambio de una línea en la mayoría de los casos. Lo he probado. Funciona.ai is the core runtime. @ai-sdk/openai (or @ai-sdk/anthropic , @ai-sdk/google, etc.) are the provider adapters. The SDK uses a unified interface so switching from GPT-4o to Claude 3.5 Sonnet is genuinely a one-line change in most cases. I've tested this. It holds up.

Los Primitivos Centrales

Tres cosas están en el corazón del SDK:

  • streamText, transmite una respuesta de texto token por token, ideal para interfaces de chat, streams a text response token by token, ideal for chat interfaces
  • generateText, espera la respuesta completa, bueno para trabajos en segundo plano o cuando no necesitas UX de streaming, waits for the full response, good for background jobs or when you don't need streaming UX
  • generateObject, devuelve JSON estructurado validado contra un esquema de Zod, que es donde las cosas se ponen realmente interesantes, returns structured JSON validated against a Zod schema, which is where things get properly interesting

streamText es lo que la mayoría de las personas quiere primero. Maneja las tuberías de ReadableStream, la codificación y el chunking. ¿Ese pesadilla de fintech de seis semanas que mencioné? Eso es exactamente lo que streamText habría resuelto el primer día. is what most people want first. It handles the ReadableStream plumbing, the encoding, and the chunking. That six-week fintech nightmare I mentioned? That's exactly what streamText would have solved on day one.

Configurándolo Sin Perder la Razón

Asumiendo que estás en Next.js 14 o 15 con el App Router (que probablemente estés si estás leyendo esto en 2025), la configuración es genuinamente rápida.

  1. Instala los paquetes: npm install ai @ai-sdk/openainpm install ai @ai-sdk/openai
  2. Crea un manejador de ruta en app/api/chat/route.tsapp/api/chat/route.ts
  3. Importa streamText y tu proveedor, pasa tu modelo y mensajes, devuelve el resultado como StreamingTextResponsestreamText and your provider, pass in your model and messages, return the result as a StreamingTextResponse
  4. En el cliente, usa el hook useChat del paquete ai/reactuseChat hook from the ai/react package

Eso es todo. Tendrás un chat con streaming funcional en menos de una hora si conoces Next.js razonablemente bien. El hook useChat gestiona el array de mensajes, el estado de carga, el binding del campo de entrada, y las actualizaciones de streaming. No escribes nada de eso tú mismo.useChat hook manages the message array, the loading state, the input field binding, and the streaming updates. You don't write any of that yourself.

Lo que agregaría inmediatamente a cualquier configuración de producción: un prompt del sistema adecuado, limitación de velocidad (uso Upstash para esto, su limitador de velocidad respaldado por Redis funciona bien con Edge Functions), y algún tipo de registro de uso de tokens. El SDK expone datos de uso en el objeto de respuesta, así que puedes registrar tokens de prompt y tokens de finalización en tu base de datos sin ninguna llamada API adicional.Upstash for this, their Redis-backed rate limiter plays nicely with Edge Functions), and some kind of token usage logging. The SDK exposes usage data on the response object, so you can log prompt tokens and completion tokens to your database without any extra API calls.

generateObject Es la Característica en la que Estás Durmiendo

Opinión honesta: streamText obtiene toda la atención pero generateObject es la que ha cambiado cómo arquitecto características de IA.streamText gets all the press but generateObject is the one that's changed how I architect AI features.

La idea es simple. Defines un esquema Zod, lo pasas a generateObject, y el SDK instruye al modelo para devolver JSON que se conforme a él. Obtienes un objeto tipado, no una cadena que tengas que analizar y rezar.generateObject, and the SDK instructs the model to return JSON that conforms to it. You get back a typed object, not a string you have to parse and pray over.

Seahawk tuvo un proyecto el año pasado para una empresa de administración de propiedades. Querían extraer datos estructurados de documentos de arrendamiento cargados, nombres de inquilinos, montos de alquiler, cláusulas de ruptura, fechas de renovación. El enfoque antiguo habría sido: solicitar al modelo, obtener texto, escribir un parser de regex, llorar. Con generateObject, definimos un LeaseSchema con Zod, y el modelo devolvió datos limpios tipados en cada ejecución. Estábamos colocando arrendamientos extraídos en una tabla Postgres en una semana.generateObject , we defined a LeaseSchema with Zod, and the model returned clean typed data on every run. We were pushing extracted leases into a Postgres table within a week.

Lo que confunde a la gente: no todos los modelos soportan salida estructurada igualmente bien. GPT-4o y Claude 3.5 Sonnet la manejan de forma confiable. Algunos modelos más pequeños o antiguos alucinarán campos o ignorarán el esquema completamente. Cíñete a los modelos estrella para este, al menos hasta que hayas validado tu caso de uso.

Llamadas a herramientas: dónde reside el verdadero poder

Si generateObject está subutilizado, las llamadas a herramientas se entienden activamente mal. La mayoría de los desarrolladores piensan que "llamadas a herramientas" significa que la IA puede navegar por internet o ejecutar código. A veces lo hace. Pero en la práctica, las herramientas son solo funciones que defines y que el modelo puede elegir invocar durante una respuesta.generateObject is underused, tool calling is actively misunderstood. Most developers think "tool calling" means the AI can browse the internet or run code. Sometimes it does. But in practice, tools are just functions you define that the model can choose to invoke during a response.

Cómo pensarlo

Digamos que estás construyendo un bot de soporte para un producto SaaS. El usuario pregunta "¿cuál es mi plan de suscripción actual?". El modelo no puede saberlo. Pero puedes definir una herramienta getUserSubscription que toma un ID de usuario y devuelve los datos del plan desde tu base de datos. El modelo reconoce la intención, llama a la herramienta, obtiene los datos de vuelta, e los incorpora en la respuesta. El usuario solo ve una respuesta coherente.getUserSubscription tool that takes a user ID and returns the plan data from your database. The model recognises the intent, calls the tool, gets the data back, and incorporates it into the response. The user just sees a coherent answer.

El parámetro tools del SDK en streamText y generateText toma un objeto donde cada clave es un nombre de herramienta, y cada valor tiene una description (que el modelo usa para decidir cuándo llamarla), un schema parameters (Zod de nuevo), y una función execute. La función execute se ejecuta del lado del servidor, de forma segura, lejos del cliente.tools parameter on streamText and generateText takes an object where each key is a tool name, and each value has a description (which the model uses to decide when to call it), a parameters schema (Zod again), and an execute function. The execute function runs server-side, safely, away from the client.

He construido agentes de múltiples pasos de esta manera. El SDK soporta maxSteps para que el modelo pueda encadenar llamadas a herramientas, llamar una herramienta, usar el resultado, llamar otra herramienta, sintetizar todo, responder. No es magia. Aún necesitas pensar cuidadosamente sobre tus descripciones de herramientas y tu system prompt. Pero el cableado se maneja todo por ti.maxSteps so the model can chain tool calls, call one tool, use the result, call another tool, synthesise everything, respond. It's not magic. You still need to think carefully about your tool descriptions and your system prompt. But the wiring is all handled for you.

Middleware, envolvimiento y el modelo de canalización

Una cosa que no aprecié hasta que leí más a fondo la documentación: el SDK tiene un concepto de middleware que te permite envolver llamadas de modelo para agregar logging, caching, o comportamiento personalizado sin tocar tu código de funciones real.

wrapLanguageModel toma un modelo y un objeto middleware. Puedes interceptar solicitudes antes de que lleguen a la API, modificar parámetros, cachear respuestas. Usé esto en una herramienta de generación de contenido que construimos para una editorial; su caso de uso tenía muchos prompts repetidos (el mismo resumen de artículo siendo solicitado varias veces al día), y cacheamos respuestas en Redis usando la capa de middleware. El costo bajó aproximadamente 35% en el primer mes. takes a model and a middleware object. You can intercept requests before they hit the API, modify parameters, cache responses. I used this on a content generation tool we built for a publisher, their use case had a lot of repeated prompts (same article summary being requested multiple times a day), and we cached responses in Redis using the middleware layer. Cost dropped by about 35% in the first month.

Este es el tipo de cosa que normalmente acoplarías de forma incómoda alrededor del exterior de tus llamadas a API. Tenerlo como un concepto de primera clase en el SDK significa que es composable y testeable.

Qué No Hace (Sé Honesto Contigo Mismo)

Bien. Déjame ahorrarte algo de dolor.

El SDK no maneja la memoria ni el historial de conversaciones a largo plazo por ti. useChat mantiene el array de mensajes en el estado del cliente, pero en el momento en que el usuario recarga, desaparece. Si necesitas conversaciones persistentes, tienes que construirlo tú mismo. Postgres o MongoDB para almacenar mensajes, fetch en mount para rehidratar el chat, nada de eso se proporciona. En realidad, este es el comportamiento correcto. El SDK no debería ser dueño de tu capa de datos. Pero los principiantes a menudo lo esperan.useChat maintains the message array in client state, but the moment the user refreshes, that's gone. If you need persistent conversations, you're building that yourself. Postgres or MongoDB for message storage, fetch on mount to rehydrate the chat, none of that is provided. This is correct behaviour, actually. The SDK shouldn't own your data layer. But newcomers often expect it to.

Tampoco maneja inputs multimodales de forma nativa de una manera que abstraiga toda la complejidad. Puedes pasar URLs de imágenes en el array de mensajes y modelos como GPT-4o los manejarán bien, pero construir un flujo de carga de imágenes adecuado con vista previa, compresión y almacenamiento sigue siendo tu trabajo. Uso Uploadthing cuando estoy en Next.js, toma aproximadamente 30 minutos conectarlo.Uploadthing for this when I'm on Next.js, takes about 30 minutes to wire up.

RAG (generación aumentada con recuperación) tampoco está incluido. No hay búsqueda vectorial incorporada, no hay pipeline de embeddings, no hay lógica de chunking. Para eso recurres a algo como pgvector en Postgres o un servicio dedicado como Pinecone. El SDK maneja la llamada al LLM. El resto de la arquitectura de recuperación es tuya para construir.pgvector on Postgres or a dedicated service like Pinecone. The SDK handles the LLM call. The rest of the retrieval architecture is yours to build.

Desplegándolo: Vercel vs Cualquier Otro Lugar

Sí, el SDK funciona mejor en Vercel. StreamingTextResponse funciona con Vercel Edge Runtime de forma inmediata, y los cold starts en Edge Functions son mucho más bajos que en funciones serverless de Node.js. Si tus respuestas de chat se sienten lentas, la latencia hasta el primer token importa mucho, y Edge la reduce.StreamingTextResponse works with Vercel's Edge Runtime out of the box, and cold starts on Edge Functions are much lower than on serverless Node.js functions. If your chat responses feel sluggish, the latency until the first token matters a lot, and Edge cuts that down.

Pero lo he desplegado en Railway (Node.js, no Edge) y funciona bien. Solo usas Response con headers de streaming apropiados en lugar de los helpers específicos de Vercel, y los documentos del SDK lo cubren. No dejes que "Vercel AI SDK" te haga pensar que estás atrapado.Response with proper streaming headers instead of the Vercel-specific helpers, and the SDK docs cover this. Don't let "Vercel AI SDK" make you think you're locked in.

Una cosa que genuinamente recomendaría: mantén tus manejadores de rutas de IA delgados. No hagas consultas a bases de datos, comprobaciones de autenticación y llamadas a LLM todo en una función. El middleware (middleware de Next.js, no el middleware del SDK) debe manejar la autenticación antes de que la solicitud llegue a tu ruta de IA. Mantiene las cosas rápidas, mantiene las cosas depurables.

FAQ

¿El SDK de Vercel AI es gratuito?

El SDK en sí es de código abierto y gratuito. Lo que pagas son las APIs de los modelos subyacentes: OpenAI, Anthropic, Google, el que sea que estés llamando. Esos costos corren por tu cuenta. El SDK no añade ningún margen.

¿Puedo usarlo con modelos que no sean OpenAI?

Sí, y esta es una de sus fortalezas genuinas. Hay adaptadores oficiales para Anthropic, Google (Gemini), Mistral, Groq, Cohere y más. La comunidad también ha creado adaptadores para Ollama si quieres ejecutar modelos locales. La interfaz unificada significa que tu código de funcionalidades no cambia cuando cambias de proveedor.

¿Funciona con React Server Components?

Parcialmente. generateText y generateObject pueden ejecutarse en Server Components sin problemas, son simplemente funciones asincrónicas. streamText con el hook useChat requiere estado del lado del cliente, así que esa parte vive en un Client Component. Esta es la arquitectura estándar de Next.js y no debería causarte problemas si entiendes el límite.generateText and generateObject can run in Server Components without issues, they're just async functions. streamText with the useChat hook requires client-side state, so that part lives in a Client Component. This is standard Next.js architecture and shouldn't cause you any grief if you understand the boundary.

¿Cómo manejo los errores cuando la API se cae?

El SDK lanza errores tipados que puedes capturar y manejar. En la práctica, envuelvo mis llamadas a LLM en un try/catch y devuelvo un mensaje de fallback elegante en lugar de dejar que el error del stream burbujee hacia la UI como una respuesta parcial rota. La documentación del SDK sobre manejo de errores cubre los tipos de error en detalle y vale la pena leer antes de ir a producción.SDK docs on error handling cover the error types in detail and are worth reading before you go to production.

¿Cuál es la situación con el límite de tokens?

El SDK no gestiona los límites de la ventana de contexto por ti. Si estás construyendo una conversación de larga duración y no recortas el historial de mensajes, alcanzarás la ventana de contexto del modelo y obtendrás un error. Una solución simple: mantén los últimos N mensajes (normalmente hago 20) más un prompt del sistema fijado. Lo suficientemente bueno para la mayoría de aplicaciones de chat.

---

Mira, el Vercel AI SDK no es un milagro. Es una abstracción bien diseñada sobre algunos trabajos de fontanería genuinamente tediosos. Maneja el streaming correctamente, te da salidas estructuradas tipadas, hace que las llamadas de herramientas sean accesibles, y funciona entre proveedores. Para el 80% del trabajo de características de IA que asumo, es el punto de partida correcto.

El cliente de fintech, por cierto, se puso en vivo con el chat reconstruido dos semanas después de que lo reescribiera. Sin problemas en Safari móvil. Sin condiciones de carrera. Seis semanas se convirtieron en dos días. A veces la abstracción correcta vale mucho.

< BACK