Optimización del rendimiento en la Interfaz Generativa
Cómo mantener rápidas las interfaces potenciadas por IA: estrategias de streaming, optimización del bundle y patrones de renderizado.
El reto del rendimiento
La Interfaz Generativa tiene una penalización de latencia inherente: antes de que cualquier interfaz pueda renderizarse, un LLM debe ejecutar la inferencia y decidir qué generar. Ese proceso tarda entre 200 y 800 ms para una respuesta simple y puede alargarse a varios segundos para respuestas complejas con múltiples herramientas.
La optimización tradicional de interfaces — caché, CDN, SSG — no puede eliminar esta latencia. El paso de decisión del LLM está en la ruta crítica de cada petición.
Sin embargo, "más lento que HTML estático" no significa "tiene que sentirse lento". La arquitectura correcta hace que 500 ms se sientan instantáneos. La arquitectura incorrecta hace que 300 ms se sientan una eternidad. Esta guía cubre las técnicas que cierran esa brecha.
Las métricas que importan
Antes de optimizar, define qué estás midiendo:
Tiempo hasta el primer componente (TTFC): Cuánto tarda el usuario en ver cualquier elemento generado por IA, aunque sea un estado de carga. Objetivo: menos de 200 ms. Esto es alcanzable transmitiendo el skeleton de inmediato mientras la inferencia se ejecuta.
Tiempo hasta el componente interactivo (TTIC): Cuánto tarda en aparecer el primer componente real, con datos reales. Objetivo: menos de 800 ms. Este es el final de la inferencia del LLM para la primera llamada a herramienta.
Tiempo de finalización del streaming: Cuánto tarda en cargarse todos los componentes generados. Varía según el número de llamadas a herramientas. Con streaming, esto es menos importante que TTFC y TTIC.
Puntuación de desplazamiento de layout (CLS): Los componentes generados no deben desplazar el layout de la página mientras se cargan. Los skeletons deben coincidir con el tamaño del componente final.
Estrategia 1: Transmitir skeletons de inmediato
La optimización de mayor impacto es transmitir un skeleton de carga antes de que el LLM resuelva el primer parámetro. El patrón generador del Vercel AI SDK lo permite directamente:
tools: {
revenueChart: {
description: 'Display a revenue chart',
parameters: z.object({
period: z.string(),
data: z.array(z.object({ date: z.string(), value: z.number() })),
}),
generate: async function* (params) {
// Esto se ejecuta de forma INMEDIATA — antes de que los params estén resueltos
// El skeleton aparece en tiempo cero
yield <ChartSkeleton />;
// Opcionalmente, obtén datos reales mientras la IA resuelve los params
// El componente aparece cuando ambos están listos
return <RevenueChart {...params} />;
},
},
}
La instrucción yield se ejecuta de forma síncrona. El usuario ve el skeleton en el mismo viaje de ida y vuelta que la petición inicial. La inferencia del LLM ocurre en paralelo. Por eso TTFC puede ser inferior a 200 ms aunque TTIC sea 800 ms.
Detalle crítico: El skeleton debe coincidir con las dimensiones del componente final. Si el skeleton mide 100 px de alto y el componente cargado mide 300 px, tendrás un desplazamiento de layout que perjudica el CLS y resulta desconcertante.
// Mal: skeleton genérico que no coincide con el tamaño del componente
yield <div className="h-8 animate-pulse bg-muted rounded" />;
// Bien: skeleton que coincide con el componente
yield (
<div className="rounded-lg border p-6 h-64">
<div className="h-4 w-32 animate-pulse bg-muted rounded mb-4" />
<div className="h-48 w-full animate-pulse bg-muted rounded" />
</div>
);
Estrategia 2: Llamadas a herramientas en paralelo
Cuando la IA necesita llamar a múltiples herramientas, deben ejecutarse en paralelo. El Vercel AI SDK lo gestiona automáticamente — varias llamadas a herramientas en una única respuesta ejecutan sus funciones generate de forma concurrente.
Pero la obtención de datos de tu componente no debe bloquear:
// Lento: obtención de datos secuencial dentro de generate
generate: async function* ({ userId, period }) {
yield <DashboardSkeleton />;
const revenue = await fetchRevenue(userId, period); // 200 ms
const users = await fetchUsers(userId, period); // 150 ms
const conversions = await fetchConversions(userId); // 100 ms
// Total: ~450 ms
return <Dashboard revenue={revenue} users={users} conversions={conversions} />;
},
// Rápido: obtención de datos en paralelo
generate: async function* ({ userId, period }) {
yield <DashboardSkeleton />;
const [revenue, users, conversions] = await Promise.all([
fetchRevenue(userId, period),
fetchUsers(userId, period),
fetchConversions(userId),
]);
// Total: ~200 ms (gana la obtención más larga)
return <Dashboard revenue={revenue} users={users} conversions={conversions} />;
},
Para fuentes de datos independientes, Promise.all siempre es más rápido que los awaits secuenciales.
Estrategia 3: Caché de respuestas
Muchas consultas de Interfaz Generativa se repiten. "Muéstrame el panel de ingresos de este mes" se ejecuta docenas de veces al día para el mismo usuario con los mismos datos subyacentes.
Guarda en caché a nivel de respuesta del LLM, usando como clave un hash del prompt y el contexto relevante:
import { createHash } from 'crypto';
interface CacheEntry {
value: React.ReactNode;
cachedAt: number;
ttlMs: number;
}
const responseCache = new Map<string, CacheEntry>();
function getCacheKey(prompt: string, context: object): string {
return createHash('md5')
.update(prompt + JSON.stringify(context))
.digest('hex');
}
export async function generateUIWithCache(
prompt: string,
context: object = {},
ttlMs: number = 5 * 60 * 1000 // 5 minutos por defecto
) {
const key = getCacheKey(prompt, context);
const cached = responseCache.get(key);
if (cached && Date.now() - cached.cachedAt < cached.ttlMs) {
return cached.value;
}
const result = await streamUI({ /* ... */ });
responseCache.set(key, { value: result.value, cachedAt: Date.now(), ttlMs });
return result.value;
}
En producción, usa Redis en lugar de un Map en memoria. Considera usar Vercel KV o Upstash Redis para caché compatible con el edge.
Importante: La invalidación de caché debe coincidir con la frecuencia de actualización de tus datos. Un panel de ingresos que se cachea durante 5 minutos está bien. Un ticker de acciones en tiempo real que se cachea durante 5 minutos es incorrecto.
Estrategia 4: Selección de modelo
No todas las consultas necesitan GPT-4o. La selección del modelo es la optimización de coste y latencia con mayor apalancamiento disponible.
| Modelo | Latencia | Coste | Calidad |
|---|---|---|---|
| GPT-4o | 400–800 ms | Alto | Mejor |
| GPT-4o-mini | 200–400 ms | 10x más barato | Bueno |
| Claude Haiku | 150–300 ms | 5x más barato | Bueno |
| Gemini Flash | 100–200 ms | 5x más barato | Bueno |
Para la mayoría de las tareas de selección de herramientas en Interfaz Generativa, GPT-4o-mini o Claude Haiku producen resultados indistinguibles de GPT-4o. Reserva los modelos de frontera para tareas de razonamiento complejo.
// Enruta al modelo adecuado según la complejidad de la consulta
function selectModel(toolCount: number, contextLength: number) {
if (toolCount <= 5 && contextLength < 500) {
return openai('gpt-4o-mini');
}
return openai('gpt-4o');
}
Estrategia 5: Optimización del bundle
Las bibliotecas de componentes de Interfaz Generativa pueden crecer bastante. Cada componente en tu registro de herramientas se envía al navegador. Gestiona esto de forma activa.
Carga diferida de componentes no críticos:
// Importa componentes de gráficos pesados solo cuando se necesiten
const HeavyChartComponent = dynamic(
() => import('@/components/heavy-chart'),
{ loading: () => <ChartSkeleton /> }
);
Separa el bundle de componentes del registro de herramientas:
// Registro de herramientas: ligero, enviado pronto
export const toolDefinitions = {
revenueChart: {
description: '...',
parameters: z.object({ ... }),
},
};
// Implementaciones de componentes: carga diferida cuando se necesitan
export const toolComponents = {
revenueChart: dynamic(() => import('@/components/revenue-chart')),
};
Mide tu bundle. Ejecuta npx @next/bundle-analyzer y busca componentes desproporcionadamente grandes. Una sola biblioteca de gráficos puede añadir más de 50 KB a tu bundle.
Estrategia 6: UI optimista
Para consultas que el sistema puede predecir, muestra una UI optimista antes de que la IA responda:
export function useGenerativeUI() {
const [ui, setUI] = useState<React.ReactNode>(null);
const [optimisticUI, setOptimisticUI] = useState<React.ReactNode>(null);
async function generate(prompt: string) {
// Muestra inmediatamente un skeleton plausible según el tipo de consulta
if (prompt.toLowerCase().includes('weather')) {
setOptimisticUI(<WeatherCardSkeleton />);
} else if (prompt.toLowerCase().includes('stock') || prompt.toLowerCase().includes('price')) {
setOptimisticUI(<StockTickerSkeleton />);
} else {
setOptimisticUI(<GenericSkeleton />);
}
const result = await generateUI(prompt);
setOptimisticUI(null);
setUI(result);
}
return { ui: optimisticUI ?? ui, generate };
}
La coincidencia simple de palabras clave en el cliente no tiene latencia. Mostrar un skeleton meteorológico en el instante en que el usuario envía una consulta sobre el tiempo se percibe significativamente más rápido que esperar el viaje de ida y vuelta al servidor.
Impacto en las Core Web Vitals
La Interfaz Generativa afecta a tus Core Web Vitals. Esto es lo que debes vigilar:
Largest Contentful Paint (LCP): Si tu contenido principal es generado por IA, el LCP reflejará el tiempo total de generación. Mitígalo generando primero el contenido por encima del pliegue y usando streaming para pintar la página progresivamente.
Cumulative Layout Shift (CLS): El mayor riesgo. Si tus skeletons no coinciden con los tamaños de los componentes, cada carga de componente provoca un desplazamiento de layout. Usa min-height en los contenedores de skeleton para reservar espacio.
Interaction to Next Paint (INP): Asegúrate de que la generación de IA se activa mediante acciones del usuario (clics en botones, envíos de formularios), no mediante carga pasiva de página. La generación pasiva puede bloquear el manejo de interacciones.
First Input Delay / INP: No ejecutes streamUI directamente en un manejador de eventos de React. Es una operación asíncrona de larga duración. Mantén el manejador de eventos rápido:
// Potencialmente lento: streamUI bloquea el manejador
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const result = await streamUI({ ... }); // bloquea
setUI(result.value);
}
// Mejor: inicia la operación asíncrona y actualiza el estado cuando esté lista
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setLoading(true);
generateUI(prompt).then(ui => {
setUI(ui);
setLoading(false);
});
}
Medir lo que estás optimizando
Sin medición, la optimización es una conjetura. Añade seguimiento de rendimiento desde el principio:
export async function generateUIWithMetrics(prompt: string) {
const startTime = performance.now();
const result = await streamUI({
/* ... */
onFinish: ({ toolCalls }) => {
const totalTime = performance.now() - startTime;
// Envía a tu plataforma de analítica u observabilidad
track('genui.generation_complete', {
prompt_length: prompt.length,
tool_calls_count: toolCalls.length,
total_ms: Math.round(totalTime),
tools_used: toolCalls.map(c => c.toolName),
});
},
});
return result.value;
}
Registra TTFC y TTIC por separado midiendo el tiempo del yield del skeleton y el retorno del componente final. Tras una semana de datos, tendrás una imagen clara de dónde va realmente el tiempo.
¿Trabajando en retos de rendimiento con GenUI? Hablemos — la optimización a lo largo de todo el stack es una especialidad.
Alex
Ingeniero y Consultor de Generative UI
Ingeniero senior especializado en interfaces con AI y sistemas Generative UI. Ayudando a equipos de producto a lanzar más rápido con el stack GenUI adecuado.
Artículos relacionados
Κατασκευάζοντας το Πρώτο σας Generative UI με το Vercel AI SDK
Βήμα-βήμα οδηγός για τη δημιουργία της πρώτης σας AI-powered διεπαφής με streaming συστατικά.
CopilotKit vs Vercel AI SDK vs Thesys: Σύγκριση Frameworks
Μια ειλικρινής σύγκριση των τριών κύριων frameworks Generative UI, με πλεονεκτήματα, μειονεκτήματα και πότε να χρησιμοποιείτε το καθένα.
Προσβασιμότητα σε Generative UI: Δημιουργία Συμπεριληπτικών AI Διεπαφών
Πρακτικός οδηγός για προσβάσιμα γεννητικά interfaces — screen readers, πλοήγηση με πληκτρολόγιο και συνδυαστικά προβλήματα προσβασιμότητας.
Adelántate en Generative UI
Artículos semanales, actualizaciones de frameworks y guías de implementación prácticas — directamente a tu bandeja de entrada.
¿Necesitas ayuda para implementar lo que acabas de leer?
Reserva una consulta gratuita