Oito segundos encarando um spinner matam a conversão; os mesmos oito segundos chegando em chunks dão sensação de resposta imediata. Streaming de respostas de IA derruba a latência percebida de 8s para menos de 1s porque o primeiro token chega rápido e o resto flui, e a taxa de abandono cai junto. Na maioria dos casos, Server-Sent Events entrega isso com bem menos complexidade que WebSockets.

Como funciona o SSE

SSE é HTTP puro: resposta com content type text/event-stream, eventos separados por linha em branco e reconexão automática embutida no EventSource do navegador. Cada evento cabe em poucos bytes, então proxies e balanceadores lidam bem com o fluxo. Conexão ociosa morre em proxy intermediário; um comentário de keepalive (: ping a cada 15s) mantém o caminho aberto. Para LLM, em que o fluxo vai do servidor ao cliente, SSE entrega tudo que WebSockets oferece com um request HTTP comum.

Quando WebSockets compensam?

  • Fluxo bidirecional real: agentes de voz, sessões colaborativas, interrupção do usuário no meio da fala
  • Latência de mensagem abaixo de 100ms muda a experiência percebida
  • Multiplexação de várias streams lógicas numa conexão única

Streaming de tokens no backend

O servidor encaminha os deltas do provedor conforme chegam e descarrega buffers sem esperar acumular. O nginx segura chunks por padrão e queima a experiência: proxy_buffering off; na location ou o header X-Accel-Buffering: no na resposta resolvem. Compressão gzip em respostas parciais também retém bytes; desative-a nesse endpoint.

Cancelamento: pare de pagar tokens invisíveis

Usuário clicou em stop? O cliente aborta o fetch, o servidor detecta a conexão fechada (request.on('close') no Express, cancelamento de generator no FastAPI) e encerra a chamada upstream no provedor. Provedores cobram pelos tokens gerados até o corte, então o corte precisa acontecer dos dois lados. Registre o cancelamento nos logs com o ID da sessão; o suporte agradece quando o usuário relata resposta cortada. Sem essa cadeia, toda sessão abandonada paga tokens que ninguém leu.

Erros no meio do stream

O provedor derruba a conexão após metade da resposta. Envie o que chegou mais um evento final de erro estruturado; o cliente renderiza o parcial com marcador claro de falha e botão de regenerar. Quando o provedor suporta continuação, regenere a partir do último chunk recebido. Silêncio até o timeout é a alternativa ruim.

Notas por framework

  • Next.js: route handlers streamam ReadableStream sem biblioteca extra
  • Express: headers Content-Type: text/event-stream, Cache-Control: no-cache e Connection: keep-alive, com res.flush() após cada chunk
  • FastAPI: StreamingResponse de fábrica consumindo generators async