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:
- Começa a responder rápido (baixa time to first byte, ou TTFB).
- Mantém um fluxo consistente de dados enquanto processa.
- 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:
- Cliente envia uma requisição com o prompt e metadados (conversa, parâmetros, contexto).
- Servidor valida e autentica a requisição.
- Servidor inicia a chamada ao provedor do LLM (local ou API externa) com streaming habilitado.
- Para cada token/trecho recebido do LLM, o servidor envia um evento SSE ao cliente.
- 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_eventoid: identificadordata: payload
Linhas em branco separam eventos.
Para LLMs, um padrão comum é:
- evento
deltacom trechos incrementais; - evento
doneao concluir; - evento
errorem 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-streamCache-Control: no-cacheConnection: 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
idincremental 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
requestIdauserId/tenantIde 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
deltachegar) - 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)
-
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. -
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. -
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. -
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. -
Falhas silenciosas: o cliente “fica esperando” porque não chega
donenemerror.
Solução: timeouts claros e eventoerrorestruturado.
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.