IA Generativa

Streaming de respostas do Azure OpenAI: implementando UX responsiva

Sem streaming, o usuário vê uma tela em branco por vários segundos e então recebe toda a resposta de uma vez. Com streaming, o texto aparece progressivamente, exatamente como no ChatGPT. A diferença na percepção de velocidade é enorme, mesmo que o tempo total de geração seja idêntico.

Streaming básico com o SDK

from openai import AzureOpenAI
from azure.identity import DefaultAzureCredential, get_bearer_token_provider

credential = DefaultAzureCredential()
token_provider = get_bearer_token_provider(
    credential, "https://cognitiveservices.azure.com/.default"
)

client = AzureOpenAI(
    azure_ad_token_provider=token_provider,
    api_version="2024-02-01",
    azure_endpoint="https://oai-producao.openai.azure.com"
)

# Streaming com context manager
with client.chat.completions.stream(
    model="gpt4o-prod",
    messages=[
        {"role": "system", "content": "Você é um assistente técnico."},
        {"role": "user", "content": "Explique Private Endpoints no Azure"}
    ]
) as stream:
    for chunk in stream:
        delta = chunk.choices[0].delta.content
        if delta:
            print(delta, end="", flush=True)
    print()

# Tokens disponíveis após o stream
final = stream.get_final_completion()
print(f"Tokens: {final.usage.total_tokens}")

Server-Sent Events com FastAPI

from fastapi import FastAPI
from fastapi.responses import StreamingResponse

app = FastAPI()

async def stream_resposta(pergunta: str):
    async with client.chat.completions.stream(
        model="gpt4o-prod",
        messages=[
            {"role": "system", "content": "Você é um assistente técnico."},
            {"role": "user", "content": pergunta}
        ]
    ) as stream:
        async for chunk in stream:
            delta = chunk.choices[0].delta.content
            if delta:
                yield f"data: {delta}\n\n"
        yield "data: [DONE]\n\n"

@app.get("/chat/stream")
async def chat_stream(q: str):
    return StreamingResponse(
        stream_resposta(q),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no"
        }
    )

Consumindo no frontend

const output = document.getElementById('output');
output.textContent = '';

const response = await fetch(`/chat/stream?q=${encodeURIComponent(pergunta)}`);
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    const lines = decoder.decode(value).split('\n');
    for (const line of lines) {
        if (line.startsWith('data: ')) {
            const data = line.slice(6);
            if (data === '[DONE]') return;
            output.textContent += data;
            output.scrollTop = output.scrollHeight;
        }
    }
}

Streaming com LangChain

from langchain_openai import AzureChatOpenAI
from langchain_core.callbacks import StreamingStdOutCallbackHandler

llm = AzureChatOpenAI(
    azure_deployment="gpt4o-prod",
    azure_endpoint="https://oai-producao.openai.azure.com",
    api_version="2024-02-01",
    azure_ad_token_provider=token_provider,
    streaming=True,
    callbacks=[StreamingStdOutCallbackHandler()]
)

response = llm.invoke("Explique hub-and-spoke no Azure")

Considerações de produção

Timeout: configure adequadamente: respostas longas podem levar 30-60 segundos.

Buffering no nginx: adicione proxy_buffering off na configuração do servidor.

Reconexão automática:

function conectarStream(pergunta, onChunk, onDone) {
    let tentativas = 0;
    function tentar() {
        const source = new EventSource(`/chat/stream?q=${encodeURIComponent(pergunta)}`);
        source.onmessage = (e) => {
            if (e.data === '[DONE]') { source.close(); onDone(); return; }
            onChunk(e.data);
        };
        source.onerror = () => {
            source.close();
            if (++tentativas < 3) setTimeout(tentar, 1000 * tentativas);
        };
    }
    tentar();
}
Streaming e RAG: Em pipelines RAG, faça a busca de documentos antes de iniciar o stream. Mostre "Buscando na base de conhecimento..." enquanto faz a query no AI Search, e inicie o streaming quando o modelo começa a gerar. Isso melhora a percepção de velocidade sem comprometer o grounding.

Conclusão

Streaming é uma das melhorias de UX de maior impacto com o menor esforço de implementação. Para usuários acostumados com ChatGPT e Copilot, ver a resposta aparecer progressivamente é o comportamento esperado, um requisito básico de usabilidade em aplicações de IA em 2026.