Ocho segundos mirando un spinner matan la conversión; los mismos ocho segundos llegando en chunks dan sensación de respuesta inmediata. El streaming de respuestas de IA derriba la latencia percibida de 8s a menos de 1s porque el primer token llega rápido y el resto fluye, y la tasa de abandono cae junto. En la mayoría de los casos, Server-Sent Events entrega eso con mucho menos complejidad que WebSockets.

Cómo funciona el SSE

SSE es HTTP puro: respuesta con content type text/event-stream, eventos separados por línea en blanco y reconexión automática integrada en el EventSource del navegador. Cada evento cabe en pocos bytes, así que proxies y balanceadores manejan bien el flujo. Una conexión ociosa muere en un proxy intermedio; un comentario de keepalive (: ping cada 15s) mantiene el camino abierto. Para LLM, donde el flujo va del servidor al cliente, SSE entrega todo lo que WebSockets ofrece con un request HTTP común.

¿Cuándo convienen los WebSockets?

  • Flujo bidireccional real: agentes de voz, sesiones colaborativas, interrupción del usuario en medio del habla
  • Latencia de mensaje por debajo de 100ms cambia la experiencia percibida
  • Multiplexación de varias streams lógicas en una conexión única

Streaming de tokens en el backend

El servidor reenvía los deltas del proveedor conforme llegan y descarga buffers sin esperar a acumular. nginx retiene chunks por defecto y arruina la experiencia: proxy_buffering off; en la location o el header X-Accel-Buffering: no en la respuesta lo resuelven. La compresión gzip en respuestas parciales también retiene bytes; desactívala en ese endpoint.

Cancelación: deja de pagar tokens invisibles

¿El usuario hizo clic en stop? El cliente aborta el fetch, el servidor detecta la conexión cerrada (request.on('close') en Express, cancelación de generator en FastAPI) y termina la llamada upstream al proveedor. Los proveedores cobran por los tokens generados hasta el corte, así que el corte necesita suceder de ambos lados. Registra la cancelación en los logs con el ID de sesión; el soporte agradece cuando el usuario reporta respuesta cortada. Sin esa cadena, toda sesión abandonada paga tokens que nadie leyó.

Errores en medio del stream

El proveedor tumba la conexión tras la mitad de la respuesta. Envía lo que llegó más un evento final de error estructurado; el cliente renderiza el parcial con un marcador claro de fallo y botón de regenerar. Cuando el proveedor soporta continuación, regenera desde el último chunk recibido. Silencio hasta el timeout es la alternativa mala.

Notas por framework

  • Next.js: los route handlers streamean ReadableStream sin librería extra
  • Express: headers Content-Type: text/event-stream, Cache-Control: no-cache y Connection: keep-alive, con res.flush() después de cada chunk
  • FastAPI: StreamingResponse de fábrica consumiendo generators async