R
Interview Deckreact · next.js · senior/staff
0·0·122
IA Básico

Streaming e UX de LLMs

Server-Sent Events, streaming de tokens na UI, skeleton states e perceived performance.

LLMs geram token a token — streaming envia cada token ao cliente imediatamente em vez de esperar a resposta completa. Isso reduz o tempo até o primeiro token visível de segundos para milissegundos. Implementação: Server-Sent Events (SSE) ou ReadableStream. No React: Vercel AI SDK com useChat(), ou manualmente com EventSource. Skeleton states enquanto o primeiro chunk não chega.

Problema
Esperar 5-10 segundos por uma resposta completa parece "travado". Usuário acha que falhou. Sem feedback visual do progresso da geração.
Solução
Streaming: mostrar tokens sendo "digitados" em tempo real. Skeleton loader antes do primeiro token. Indicador de loading (⋯ pulsando) enquanto gera. Botão "Parar geração" para interromper. Abort signal para cancelar o stream quando o usuário navega.
Tip
Perceived performance importa mais que performance real. Uma resposta que aparece sendo "digitada" em 5 segundos parece mais rápida que uma resposta que aparece completa em 3 segundos. Sempre implemente streaming em interfaces de chat — é expectativa do usuário em 2025.
// Vercel AI SDK — streaming no Next.js
// app/api/chat/route.ts
import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'

export async function POST(req: Request) {
  const { messages } = await req.json()
  const result = streamText({
    model: openai('gpt-4o'),
    system: 'Você é um assistente técnico especializado em React.',
    messages,
    onFinish({ text, usage }) {
      console.log('Tokens usados:', usage.totalTokens)
    },
  })
  return result.toDataStreamResponse()
}

// app/page.tsx — client component
'use client'
import { useChat } from 'ai/react'

export default function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading, stop } = useChat()

  return (
    <div>
      {messages.map(m => (
        <div key={m.id}>
          <b>{m.role}:</b> {m.content}
          {/* Cursor piscando durante geração */}
          {isLoading && m.role === 'assistant' && <span className="cursor">▊</span>}
        </div>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={input} onChange={handleInputChange} placeholder="Pergunte algo..." />
        {isLoading
          ? <button onClick={stop} type="button">⏹ Parar</button>
          : <button type="submit">Enviar</button>
        }
      </form>
    </div>
  )
}
Q.Como implementar streaming no Next.js App Router?
A.Route Handler com Vercel AI SDK: `const result = streamText({ model, messages }); return result.toDataStreamResponse()`. No cliente: `useChat({ api: '/api/chat' })` — o hook gerencia o estado de streaming automaticamente. Para streaming custom: `TransformStream` ou `ReadableStream` no servidor, `EventSource` ou fetch com `response.body.getReader()` no cliente.
Q.Como cancelar um stream quando o usuário navega?
A.AbortController: passar `signal` para o fetch/stream. No useEffect de cleanup: `controller.abort()`. Com Vercel AI SDK: `stop()` do useChat() para interromper a geração. Sem cancelamento, o backend continua gerando e consumindo tokens (e dinheiro) mesmo com o usuário já em outra página.
Q.Como lidar com erros durante o streaming?
A.Erros podem acontecer no meio do stream. Estratégias: (1) mostrar o que foi gerado até o ponto do erro + mensagem de erro, (2) retry automático para transient errors, (3) fallback para chamada não-streaming se o streaming falhar. Com Vercel AI SDK: `onError` callback. Sempre mostrar feedback claro ao usuário quando a geração falha.
94/122← →