0Pricing
AI Engineering Academy · Aula

Entendendo o streaming de tokens

Entenda como a API de streaming envia conclusões parciais à medida que são geradas, como funciona o parâmetro stream=True da OpenAI e quando o streaming melhora a experiência do usuário.

Entendendo o streaming de tokens é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.

Por que a transmissão é importante para a experiência do usuário

Sem transmissão, sua aplicação precisa esperar que o LLM gere a resposta completa antes de exibir qualquer coisa — geralmente de 5 a 30 segundos para respostas longas. Com a transmissão, o primeiro token aparece entre 200 e 500 ms após o envio da solicitação, e os tokens seguintes são transmitidos à medida que são gerados. Isso transforma a experiência percebida pelo usuário: em vez de esperar, ele acompanha uma geração ao vivo envolvente, melhorando significativamente a sensação de responsividade, embora o tempo total de geração seja idêntico.

Como os LLMs geram tokens

Os LLMs são autoregressivos: geram o texto um token por vez, e cada novo token é condicionado por todos os tokens anteriores. Quando a API recebe uma solicitação, a GPU começa a amostrar o primeiro token assim que o processamento do prompt termina. Cada token seguinte leva aproximadamente o mesmo tempo. A transmissão envia cada token ao cliente assim que ele é amostrado, em vez de armazenar todos os tokens e enviar a sequência completa ao final.

# Conceptual model of autoregressive generation
prompt = 'The capital of France is'

# Step 1: process full prompt, predict next token
# token_1 = sample(logits) → ' Paris'

# Step 2: append token_1 to context, predict next
# token_2 = sample(logits) → '.'

# Step 3: append token_2 to context, predict next
# token_3 = sample(logits) → '<|end|>'

# Total time: time_to_process_prompt + n_tokens * time_per_token
# With streaming: first token arrives after time_to_process_prompt (TTFT)
# Without streaming: everything arrives after TTFT + n_tokens * time_per_token

TTFT e TPOT: duas métricas de latência

A transmissão introduz dois conceitos distintos de latência. TTFT (tempo até o primeiro token) é o intervalo entre o envio da solicitação e o recebimento do primeiro token — dominado pelo tempo de processamento do prompt. TPOT (tempo por token de saída) é o intervalo entre tokens consecutivos — determinado pelo tamanho do modelo e pelo hardware. O TTFT afeta a rapidez com que a interface responde; o TPOT afeta a fluidez da transmissão do texto. Ambos devem ser acompanhados separadamente na sua camada de observabilidade.

import time
from openai import OpenAI

client = OpenAI()

def measure_streaming_latency(prompt: str):
    t_start = time.perf_counter()
    t_first_token = None
    token_times = []

    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}],
        stream=True,
    )
    for chunk in stream:
        if chunk.choices[0].delta.content:
            t_now = time.perf_counter()
            if t_first_token is None:
                t_first_token = t_now
                print(f'TTFT: {(t_first_token - t_start) * 1000:.0f}ms')
            else:
                token_times.append(t_now - token_times[-1] if token_times else t_now - t_first_token)
            token_times.append(t_now)
    print(f'TPOT avg: {1000 * (token_times[-1] - t_first_token) / max(len(token_times)-1, 1):.1f}ms')

O parâmetro stream=True

Ativar a transmissão no SDK da OpenAI exige definir stream=True na chamada chat.completions.create. O tipo da resposta muda de um objeto ChatCompletion para um iterador Stream[ChatCompletionChunk]. Cada fragmento contém um delta com um fragmento de texto content ou None quando o token é uma chamada de ferramenta ou quando a transmissão está terminando.

from openai import OpenAI

client = OpenAI()

# Non-streaming: wait for complete response
response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'Explain RAG in one paragraph.'}],
)
full_text = response.choices[0].message.content

# Streaming: receive tokens incrementally
stream = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'Explain RAG in one paragraph.'}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:  # delta can be None for non-content chunks
        print(delta, end='', flush=True)
print()  # newline at end

Acumulando a resposta completa

Em muitos fluxos de aplicação, você precisa tanto transmitir os tokens para a interface para obter responsividade quanto acumular o texto completo da resposta para processamento posterior, como logging, armazenamento em cache ou outras etapas do fluxo de processamento. O padrão é simples: percorra a transmissão, imprima ou forneça cada fragmento ao cliente e, simultaneamente, concatene o conteúdo em uma string completa.

def stream_and_accumulate(prompt: str) -> str:
    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=[{'role': 'user', 'content': prompt}],
        stream=True,
    )

    full_text = ''
    finish_reason = None

    for chunk in stream:
        choice = chunk.choices[0]
        delta = choice.delta.content
        if delta:
            print(delta, end='', flush=True)  # real-time display
            full_text += delta               # accumulate
        if choice.finish_reason:
            finish_reason = choice.finish_reason

    print()  # newline
    print(f'Finished: {finish_reason}, total chars: {len(full_text)}')
    return full_text

Transmissão com estatísticas de uso

Por padrão, a resposta transmitida não inclui estatísticas de uso de tokens (tokens do prompt e da conclusão). Para incluí-las, passe stream_options={'include_usage': True}. Os dados de uso chegam em um fragmento final depois que a transmissão do conteúdo termina. Isso é importante para acompanhar custos e monitorar limites de requisições em aplicações de produção.

stream = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[{'role': 'user', 'content': 'What is a vector database?'}],
    stream=True,
    stream_options={'include_usage': True},  # include token counts
)

full_text = ''
usage = None

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        full_text += chunk.choices[0].delta.content
    if chunk.usage:  # arrives in the final chunk
        usage = chunk.usage

if usage:
    print(f'Prompt tokens: {usage.prompt_tokens}')
    print(f'Completion tokens: {usage.completion_tokens}')
    print(f'Total tokens: {usage.total_tokens}')

Quando não usar transmissão

A transmissão nem sempre é a escolha certa. Evite usá-la quando: (1) você precisa da resposta completa antes de fazer qualquer coisa com ela, como analisar JSON ou detectar chamadas de ferramentas; (2) a resposta é muito curta (menos de 30 tokens), de modo que a sobrecarga da transmissão adiciona mais atraso do que economiza; ou (3) você está processando muitas solicitações em lote, situação em que a vazão é mais importante do que a latência de cada resposta. Nesses casos, as chamadas padrão sem transmissão são mais simples e igualmente rápidas.

Transmissão com as APIs da Anthropic e do Gemini

A transmissão está disponível nas APIs de todos os principais provedores de LLM, não apenas na OpenAI. O padrão é semelhante, mas as interfaces dos SDKs diferem um pouco. O SDK da Anthropic para Python usa client.messages.stream() como um gerenciador de contexto, enquanto o Gemini usa generate_content(stream=True). Ao criar aplicações independentes de provedor, abstraia a interface de transmissão por trás de uma função geradora comum.

import anthropic

ant_client = anthropic.Anthropic(api_key='YOUR_KEY')

# Anthropic streaming
with ant_client.messages.stream(
    model='claude-sonnet-4-5',
    max_tokens=1024,
    messages=[{'role': 'user', 'content': 'Explain hybrid search briefly.'}],
) as stream:
    for text in stream.text_stream:
        print(text, end='', flush=True)

# Final message with usage stats
final_msg = stream.get_final_message()
print(f'\nInput tokens: {final_msg.usage.input_tokens}')
print(f'Output tokens: {final_msg.usage.output_tokens}')

Interface de transmissão baseada em gerador

Um padrão de arquitetura limpo encapsula a transmissão em uma função geradora do Python que fornece strings de tokens. Isso desacopla a lógica de transmissão da lógica de consumo — os chamadores podem percorrer o gerador, gravar em um arquivo, encaminhar para um WebSocket ou acumular em uma string sem que o código de transmissão saiba como sua saída será usada. Essa é a base da maioria das APIs de transmissão de produção.

from typing import Generator

def stream_completion(
    messages: list[dict],
    model: str = 'gpt-4o-mini',
    **kwargs,
) -> Generator[str, None, None]:
    stream = client.chat.completions.create(
        model=model,
        messages=messages,
        stream=True,
        **kwargs,
    )
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            yield delta

# Usage: pipe to stdout
for token in stream_completion([{'role': 'user', 'content': 'Hello!'}]):
    print(token, end='', flush=True)

# Usage: accumulate
full = ''.join(stream_completion([{'role': 'user', 'content': 'Hello!'}]))

Transmissão em aplicações de terminal e CLI

Em aplicações de terminal, a saída transmitida se parece exatamente com uma digitação — cada caractere aparece imediatamente assim que é gerado. O requisito principal é usar flush=True em cada chamada de print. Sem liberar o conteúdo, o Python armazena a saída em um buffer até encontrar uma nova linha, o que elimina o propósito da transmissão. Você também pode usar sys.stdout.write(token) seguido de sys.stdout.flush() para ter mais controle sobre a formatação da saída.

import sys

def stream_to_terminal(messages: list[dict]):
    stream = client.chat.completions.create(
        model='gpt-4o-mini',
        messages=messages,
        stream=True,
    )
    token_count = 0
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            sys.stdout.write(delta)  # no newline added
            sys.stdout.flush()       # MUST flush or output buffers
            token_count += 1
    print()  # final newline
    print(f'({token_count} tokens generated)')

Transmissão e recuperação de erros

A transmissão complica o tratamento de erros porque uma falha pode ocorrer no meio da transmissão, depois que você já enviou alguns tokens ao cliente. O padrão recomendado é envolver a iteração da transmissão em um bloco try/except e, em caso de erro, enviar um marcador de erro ao cliente ou fechar a transmissão corretamente. Sempre implemente um tempo limite para a transmissão inteira, a fim de lidar com situações em que o servidor começa a transmitir, mas para no meio da geração.

import signal

def stream_with_timeout(messages, timeout_seconds=30):
    def timeout_handler(signum, frame):
        raise TimeoutError('LLM stream timed out')

    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout_seconds)

    try:
        stream = client.chat.completions.create(
            model='gpt-4o-mini',
            messages=messages,
            stream=True,
        )
        for chunk in stream:
            delta = chunk.choices[0].delta.content
            if delta:
                yield delta
    except TimeoutError:
        yield '\n[Response timed out]'
    except Exception as e:
        yield f'\n[Error: {str(e)}]'
    finally:
        signal.alarm(0)  # cancel timeout

Verificação rápida

Teste sua compreensão sobre a transmissão de tokens de LLM nesta lição.

Recapitulação da lição

Nesta lição, você aprendeu: o streaming envia cada token gerado ao cliente assim que ele é amostrado, melhorando significativamente a responsividade percebida; TTFT e TPOT são as duas principais métricas de latência e devem ser acompanhadas separadamente; e stream=True transforma a resposta do SDK da OpenAI em um iterador de partes que você consome com um laço for. Envolva os fluxos em funções geradoras para obter uma interface limpa e reutilizável. A seguir, implementaremos o streaming assíncrono com o SDK do Python.

Perguntas Frequentes

A aula “Entendendo o streaming de tokens” é grátis?

Sim — o texto completo de “Entendendo o streaming de tokens” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.

O que vou aprender em “Entendendo o streaming de tokens”?

Entenda como a API de streaming envia conclusões parciais à medida que são geradas, como funciona o parâmetro stream=True da OpenAI e quando o streaming melhora a experiência do usuário. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar AI Engineering Academy?

Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Entendendo o streaming de tokens”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de AI Engineering Academy?

Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Entendendo o streaming de tokens
  2. Consumindo streams com o SDK para Python
  3. Streaming no FastAPI com eventos enviados pelo servidor
  4. Lidando com chamadas de ferramentas em respostas em streaming
← Voltar para AI Engineering Academy