Blog
Arquitetura

Streaming UI pra LLM: o que a tela do agente faz quando a conexão cai no oitavo token

Montei uma demo em que a resposta do modelo cai no meio, de propósito. Cancelar, retomar e mostrar o erro são decisões de produto, e quando a tela aprova uma ação do agente elas viram contrato da plataforma de IA, com idempotência no backend.

Toda demo de chat com LLM funciona no Wi-Fi do escritório. A interface de um agente de IA mostra quem pensou nela no dia em que a conexão cai no meio da resposta, e esse dia chega rápido em produção.

Por isso eu montei uma demo que derruba a conexão de propósito. Ela é o primeiro item da série Frontend com IA: 01-streaming-ui, em TypeScript estrito, sem framework de UI. Abra este link e aperte Enviar: a resposta cai no oitavo token. A tela mostra “tentando de novo”, espera e continua do ponto onde parou.

Três decisões que eu tomaria em qualquer time

Cancelar precisa cancelar. Esconder o texto e deixar o pedido rodando ainda gasta token e ainda pode disparar efeito colateral. Na demo, o botão chama abort() num AbortController ligado ao fetch, e a sessão vai para o estado cancelled.

Retry tem limite e aparece na tela. Se a conexão cai sozinha, a sessão tenta de novo com espera crescente e desiste na quarta tentativa, com uma mensagem que diz em qual token caiu:

export function backoffMs(attempt: number, base = 250, cap = 2_000): number {
  return Math.min(cap, base * 2 ** Math.max(0, attempt - 1));
}

Com quatro tentativas no total, as esperas ficam em 250 ms, 500 ms e 1 s, e o teto de 2 s fica de reserva caso alguém aumente o limite. O estado de erro existe no tipo (idle, connecting, streaming, stalled, retrying, done, cancelled, error), então a tela precisa ter o que mostrar em cada um.

O DOM não acompanha o ritmo dos tokens. Os pedaços entram num buffer e a escrita acontece uma vez por requestAnimationFrame. A própria demo compara “a cada token” com “uma vez por frame”, e o contador de tokens recebidos sobe mais rápido que o de escritas.

Onde a demo é honesta comigo

Retomar do último token funciona porque o servidor de mentira aceita um fromToken. Quando você liga um provedor real pela API de chat completions, o código faz outra coisa:

if (mode === 'provider') {
  fromToken = 0;
  hooks.onReset();
  batcher.reset();
}

Começa do zero, porque esse endpoint não sabe continuar uma geração pela metade. A tela só retoma o que o backend sabe retomar. Esse é o ponto em que a conversa sai do frontend e vai para a arquitetura.

Quando a tela aprova uma ação

Em agentes de IA, o stream carrega texto e também pedidos de ação: “posso mandar essa mensagem?”, “posso abrir esse PR?“. Imagina a pessoa clicando em Aprovar e a conexão caindo logo depois. O retry com backoff, que é ótimo para texto, manda a aprovação de novo. Se o backend executa duas vezes, a mensagem sai duas vezes.

Como Tech Lead, eu trataria a UI do agente como parte do contrato da plataforma, com três exigências:

  1. Cada pedido de aprovação tem um id estável, e a aprovação viaja com uma chave de idempotência. Receber a mesma chave duas vezes devolve o resultado da primeira execução.
  2. O estado da ação mora no servidor (pendente, aprovado, executando, concluído, falhou). Depois de reconectar, a tela pergunta esse estado em vez de supor.
  3. A geração tem um id e um offset, para o cliente pedir “continua do token N” ou, no mínimo, saber que precisa recomeçar.

Em TypeScript, o pedaço que o frontend manda fica pequeno:

type Approval = {
  actionId: string;        // vem do stream, igual em todo retry
  idempotencyKey: string;  // gerada uma vez no clique
  decision: "approve" | "reject";
};

O resto é backend: guardar a chave, devolver a resposta já registrada e não executar de novo. Aprovação humana (HITL) que não sobrevive a uma queda de rede vira um botão que às vezes funciona.

O que eu levaria para a mesa

Antes de discutir qual modelo usar, eu perguntaria ao time: se o stream cair agora, o que a pessoa vê, o que o servidor sabe e o que roda duas vezes? A resposta mostra se a plataforma de IA foi desenhada para rede de verdade.

A demo está aberta. Dá para mexer em latência, rajada, chance de pausa e no token do corte pela URL. As outras três demos da série estão em eliseu.dev/projects.

Fontes

esc
  • Home Início
  • Sobre Trajetória e como trabalho
  • Blog Artigos sobre IA, plataforma e liderança
  • Projetos Demos públicos de frontend com IA
  • Currículo Web, PDF, Markdown e texto
  • Setup Hardware e ferramentas
  • Streaming UI pra LLM: o que a tela do agente faz quando a conexão cai no oitavo token Arquitetura
  • MCP Tasks no Claude Code: a aprovação precisa saber qual commit a pessoa viu Arquitetura
  • Sandbox de agente virou infraestrutura: 4 anúncios da semana Arquitetura
  • LiteLLM comprometido: o log do gateway de LLM também guarda segredo Arquitetura
↑↓ navegar ↵ abrir esc fechar