El Vercel AI SDK simplifica construir interfaces de chat con LLMs. En este tutorial construimos un chatbot completo con streaming, historial de conversación, y soporte para múltiples modelos.

Setup del proyecto

npx create-next-app@latest mi-chatbot --typescript --tailwind --app
cd mi-chatbot
npm install ai @ai-sdk/openai @ai-sdk/anthropic

La API Route: backend del chat

// app/api/chat/route.ts
import { streamText } from 'ai';
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';

export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages, model = 'gpt-4o' } = await req.json();

  // Seleccionar el modelo basado en el parámetro
  const selectedModel = model.startsWith('claude') 
    ? anthropic(model)
    : openai(model);

  const result = streamText({
    model: selectedModel,
    system: 'Eres un asistente técnico para desarrolladores. Responde siempre con ejemplos de código cuando sea relevante.',
    messages,
    maxTokens: 2048,
  });

  return result.toDataStreamResponse();
}

El componente de chat

// app/page.tsx
'use client';

import { useChat } from 'ai/react';
import { useState } from 'react';

const MODELOS = [
  { id: 'gpt-4o', label: 'GPT-4o' },
  { id: 'gpt-4o-mini', label: 'GPT-4o Mini' },
  { id: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' },
];

export default function Chat() {
  const [modelo, setModelo] = useState('gpt-4o');
  
  const { messages, input, handleInputChange, handleSubmit, isLoading, error } = useChat({
    api: '/api/chat',
    body: { model: modelo },
  });

  return (
    <div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
      {/* Selector de modelo */}
      <div className="mb-4 flex gap-2">
        {MODELOS.map(m => (
          <button
            key={m.id}
            onClick={() => setModelo(m.id)}
            className={`px-3 py-1 rounded text-sm ${
              modelo === m.id 
                ? 'bg-blue-600 text-white' 
                : 'bg-gray-100 text-gray-700 hover:bg-gray-200'
            }`}
          >
            {m.label}
          </button>
        ))}
      </div>

      {/* Historial de mensajes */}
      <div className="flex-1 overflow-y-auto space-y-4 mb-4">
        {messages.length === 0 && (
          <p className="text-gray-400 text-center mt-8">
            Escribe algo para empezar la conversación
          </p>
        )}
        
        {messages.map(m => (
          <div
            key={m.id}
            className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
          >
            <div
              className={`max-w-[80%] rounded-lg p-3 text-sm ${
                m.role === 'user'
                  ? 'bg-blue-600 text-white'
                  : 'bg-gray-100 text-gray-800'
              }`}
            >
              <pre className="whitespace-pre-wrap font-sans">{m.content}</pre>
            </div>
          </div>
        ))}
        
        {isLoading && (
          <div className="flex justify-start">
            <div className="bg-gray-100 rounded-lg p-3 text-sm text-gray-500">
              Generando respuesta...
            </div>
          </div>
        )}
        
        {error && (
          <div className="text-red-500 text-sm">
            Error: {error.message}
          </div>
        )}
      </div>

      {/* Input */}
      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          value={input}
          onChange={handleInputChange}
          placeholder="Escribe tu mensaje..."
          disabled={isLoading}
          className="flex-1 border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
        />
        <button
          type="submit"
          disabled={isLoading || !input.trim()}
          className="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm disabled:opacity-50 hover:bg-blue-700"
        >
          Enviar
        </button>
      </form>
    </div>
  );
}

Variables de entorno

# .env.local
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

Despliegue en Vercel

# Instalar CLI de Vercel
npm i -g vercel

# Desplegar
vercel

# Configurar variables de entorno en el dashboard de Vercel
# o via CLI:
vercel env add OPENAI_API_KEY
vercel env add ANTHROPIC_API_KEY

Con esta estructura tienes un chatbot funcional en producción en menos de 30 minutos, con streaming real, soporte multi-modelo y manejo básico de errores.


Fuentes: Documentación del Vercel AI SDK, Next.js App Router docs, guías oficiales de integración.