search

Respostas em Tempo Real: Server-Sent Events e Streaming para LLMs

Respostas em Tempo Real: Server-Sent Events e Streaming para LLMs

Respostas em Tempo Real: Server-Sent Events e Streaming para LLMs

A adoção de modelos de linguagem (LLMs) em produtos digitais elevou a expectativa do usuário: ninguém quer esperar vários segundos para ver uma resposta “pronta” quando poderia acompanhar a geração em tempo real. Esse padrão — respostas que chegam em fluxo contínuo, token a token ou trecho a trecho — é parte central do que o mercado chama de Real-Time Inference: inferência com percepção de imediatismo, baixa latência e feedback contínuo.

Para viabilizar essa experiência na web, uma das abordagens mais comuns e simples é o Server-Sent Events (SSE). Neste artigo, você verá como SSE funciona, quando usar, como estruturar um streaming para LLMs, e quais são os cuidados mais importantes de cyber segurança, confiabilidade e observabilidade ao colocar isso em produção.


O que é Real-Time Inference (e por que streaming importa)

Real-Time Inference não significa “latência zero”. Na prática, significa que o sistema:

  1. Começa a responder rápido (baixa time to first byte, ou TTFB).
  2. Mantém um fluxo consistente de dados enquanto processa.
  3. Entrega feedback incremental que reduz a ansiedade do usuário e melhora a percepção de desempenho.

Para LLMs, o streaming é particularmente valioso porque a geração é incremental por natureza. Mesmo que a resposta final leve 8–15 segundos para completar, o usuário pode ver conteúdo surgindo em 300–800 ms — o que muda completamente a experiência.


SSE: o básico em termos claros

Server-Sent Events é uma tecnologia baseada em HTTP na qual:

  • o cliente abre uma conexão HTTP;
  • o servidor mantém a conexão aberta;
  • o servidor envia eventos em texto, em um formato simples;
  • o navegador/cliente recebe esses eventos conforme chegam.

Características importantes:

  • Unidirecional (servidor → cliente): diferente do WebSocket, o SSE não é “full-duplex”.
  • Funciona muito bem para streaming de texto: como tokens e parágrafos de LLM.
  • Mais simples de operar do que WebSockets em muitos ambientes corporativos (proxies e balanceadores tendem a lidar melhor com HTTP).
  • Requer cuidado com buffering: alguns proxies podem “segurar” a resposta e quebrar o streaming se não configurados corretamente.

SSE x WebSockets x HTTP chunked: quando escolher o quê

SSE (Server-Sent Events)

Use SSE quando:

  • o fluxo é principalmente do servidor para o cliente;
  • você quer simplicidade e compatibilidade com infraestrutura HTTP;
  • o cliente é browser ou apps que suportam eventos contínuos via HTTP.

Limitações:

  • não é ideal para comunicação bidirecional intensa;
  • reconexão e retomada exigem design (embora o protocolo ajude com Last-Event-ID).

WebSockets

Use WebSockets quando:

  • há necessidade de bidirecionalidade real (ex.: colaboração em tempo real, jogos, controle granular de fluxo do cliente);
  • você precisa enviar muitos comandos do cliente durante a sessão (pausar, retomar, alterar parâmetros, etc.).

Custo:

  • operação mais complexa (state, escalabilidade, timeouts, proxies);
  • exige mais atenção a segurança de sessão e limites.

HTTP chunked (resposta em chunks)

É uma alternativa quando:

  • você quer streaming sem o formato de eventos do SSE;
  • está controlando cliente e servidor e prefere um protocolo próprio simples (por exemplo, JSON por linha).

Na prática, SSE vira uma escolha comum para streaming de LLMs por combinar simplicidade, compatibilidade e boa experiência no front-end.


Como o streaming de LLMs costuma funcionar na prática

Um fluxo típico de Real-Time Inference com SSE tem estas etapas:

  1. Cliente envia uma requisição com o prompt e metadados (conversa, parâmetros, contexto).
  2. Servidor valida e autentica a requisição.
  3. Servidor inicia a chamada ao provedor do LLM (local ou API externa) com streaming habilitado.
  4. Para cada token/trecho recebido do LLM, o servidor envia um evento SSE ao cliente.
  5. No final, o servidor envia um evento de término e fecha a conexão (ou mantém para próximos eventos, dependendo do desenho).

Formato de evento SSE (visão geral)

SSE é texto com linhas no padrão:

  • event: nome_do_evento
  • id: identificador
  • data: payload

Linhas em branco separam eventos.

Para LLMs, um padrão comum é:

  • evento delta com trechos incrementais;
  • evento done ao concluir;
  • evento error em falhas controladas.

Implementação passo a passo (arquitetura e lógica)

Abaixo está um roteiro técnico, independente de linguagem/framework.

1) Defina o contrato de eventos

Antes do código, defina o contrato. Exemplo de eventos:

  • event: delta
    data: {"text":"...","index":12}

  • event: metadata
    data: {"model":"...","requestId":"...","startedAt":"..."}

  • event: done
    data: {"finishReason":"stop","tokens":1234}

  • event: error
    data: {"code":"UPSTREAM_TIMEOUT","message":"..."}

Decisão importante: envie texto puro ou JSON. JSON facilita evolução, telemetria e compatibilidade.

2) Prepare o servidor para streaming

Requisitos típicos:

  • desabilitar buffers no caminho (quando possível);
  • enviar headers adequados:
    • Content-Type: text/event-stream
    • Cache-Control: no-cache
    • Connection: keep-alive

Em infra com reverse proxy, verifique:

  • timeout de conexão (idle e total);
  • buffering de resposta;
  • limites de tamanho.

3) Faça backpressure e controle de fluxo

Mesmo em SSE, você precisa evitar:

  • enfileirar dados indefinidamente se o cliente estiver lento;
  • uso excessivo de memória por conexões longas.

Estratégias:

  • limite de taxa de envio (ex.: agrupar tokens a cada 50–100 ms);
  • limite de tamanho por evento e por sessão;
  • cancelamento: se o cliente desconectar, interrompa a geração no provedor do LLM (quando suportado).

4) Trate reconexão e idempotência

SSE tem suporte a reconexão automática em muitos clientes. Para isso funcionar de forma robusta:

  • emita id incremental por evento;
  • armazene (por curto tempo) o que foi enviado, para que o cliente possa retomar via Last-Event-ID (opcional, mas útil em redes instáveis);
  • ou seja explícito: “sem retomada”, e reinicie a resposta do zero.

Para LLMs, retomada perfeita é difícil (porque geração é estocástica), então a maioria das implementações aceita reiniciar ou apenas reexibir o que já foi recebido.

5) Finalização previsível

Sempre emita um done e feche a conexão. Isso ajuda:

  • UI a encerrar estado de “digitando”;
  • métricas a calcular duração;
  • prevenção de conexões zumbis.

Segurança em streaming de LLMs: os principais riscos e controles

Streaming melhora UX, mas amplia superfície de ataque e riscos operacionais. Os pontos abaixo são os mais relevantes em produção.

1) Autenticação, autorização e isolamento de sessão

Risco: vazamento de dados entre usuários, especialmente se houver cache intermediário ou roteamento incorreto.

Boas práticas:

  • autenticação forte (tokens curtos, rotação, validação no gateway);
  • autorização por escopo (quem pode chamar quais modelos, quais ferramentas, quais bases RAG);
  • vincule requestId a userId/tenantId e registre em logs com cuidado.

2) Vazamento de dados sensíveis em tempo real

Risco: o modelo pode gerar PII/segredos e você transmite imediatamente ao cliente, antes de qualquer filtro posterior.

Mitigações:

  • política de redaction em streaming (filtro incremental):
    • detecte padrões sensíveis (e-mail, CPF, chaves) e masque antes de enviar;
  • instruções e guardrails no prompt (não garantem, mas ajudam);
  • para cenários críticos, avalie “streaming atrasado” por pequenos lotes para permitir checagem (ex.: 200–400 ms de buffer).

3) Prompt injection e ferramentas (tool calling)

Se o LLM tem acesso a ferramentas (busca interna, banco, ações), o streaming pode expor:

  • logs de ferramentas;
  • dados intermediários;
  • raciocínio ou conteúdo que não deveria aparecer.

Boas práticas:

  • separe canais: não envie saídas internas ao usuário;
  • sanitize de respostas de ferramentas;
  • lista de permissões por ferramenta e por usuário;
  • trate o modelo como componente não confiável para autorizar ações.

4) DoS e exaustão de recursos

Risco: conexões SSE são longas. Um atacante pode abrir muitas conexões e manter ocupadas (file descriptors, threads, memória), ou forçar respostas longas.

Mitigações:

  • rate limiting por IP/usuário/token;
  • limite de conexões concorrentes por tenant;
  • timeout total por inferência;
  • limite de tokens gerados e tamanho do output;
  • proteção no edge (WAF/CDN quando apropriado);
  • cancelamento imediato quando o cliente desconectar.

5) Logging e privacidade

Risco: logs podem capturar o conteúdo em streaming (prompts e respostas), o que vira um problema de privacidade e compliance.

Boas práticas:

  • logue metadados (latência, contagem de tokens, status), não o texto completo por padrão;
  • amostragem e mascaramento quando precisar de payload;
  • segregação de logs por ambiente e controles de acesso;
  • políticas de retenção curtas.

Observabilidade: métricas que mostram se o streaming está saudável

Para Real-Time Inference, métricas comuns de API não bastam. Recomenda-se medir:

  • TTFB (tempo até o primeiro evento delta chegar)
  • Tempo total de geração
  • Taxa de eventos por segundo (regularidade do fluxo)
  • Tokens por segundo (quando disponível)
  • Erros do upstream (timeout, rate limit, falha de rede)
  • Abortos por desconexão do cliente
  • Distribuição de tamanhos dos eventos
  • Percentil P95/P99 de duração por modelo/rota/tenant

Além disso, trace de ponta a ponta ajuda a separar:

  • latência do gateway/proxy
  • latência do servidor de aplicação
  • latência do provedor LLM
  • gargalos de rede

Armadilhas comuns em produção (e como evitar)

  1. Proxy fazendo buffering: o usuário não vê streaming, só a resposta final.
    Solução: revisar configurações de buffering, headers e timeouts no caminho.

  2. Eventos muito pequenos (token a token): overhead alto e custo de rede desnecessário.
    Solução: agrupar tokens em janelas curtas (ex.: 50–100 ms) ou por tamanho.

  3. Sem cancelamento: usuário fecha a aba, mas o servidor continua pagando a inferência.
    Solução: detectar desconexão e cancelar a geração no upstream.

  4. Mistura de conteúdos de sessões: bug de concorrência ou reuso incorreto de streams.
    Solução: isolar estritamente por request, validar invariantes, testes de carga com concorrência.

  5. Falhas silenciosas: o cliente “fica esperando” porque não chega done nem error.
    Solução: timeouts claros e evento error estruturado.


Conclusão

SSE se consolidou como um caminho pragmático para streaming de respostas de LLMs na web, entregando a sensação de Real-Time Inference com menor complexidade operacional do que alternativas bidirecionais. O ganho de experiência do usuário é real, mas vem acompanhado de exigências: controle de fluxo, cancelamento, boas métricas, e principalmente uma postura de segurança que trate o streaming como um canal sensível — porque ele é.

Ao desenhar o contrato de eventos, ajustar a infraestrutura para evitar buffering, e aplicar controles de autenticação, limites e sanitização, é possível oferecer respostas em tempo real com confiabilidade e segurança, mesmo em cenários de alto tráfego e dados críticos.

Compartilhar este artigo: