Conteúdo

Esperar cinco, dez ou quinze segundos por uma resposta completa é inaceitável para qualquer interface conversacional moderna. O Microsoft Agent Framework streaming resolve esse problema ao entregar fragmentos de texto à medida que o modelo de linguagem os gera, transformando uma espera silenciosa em uma experiência fluida onde o usuário acompanha o raciocínio em tempo real. Essa capacidade é implementada através do método RunStreamingAsync, que retorna um IAsyncEnumerable processável token a token, reduzindo drasticamente a latência percebida. Este artigo apresenta código completo e funcional em .NET e Python para implementar streaming em agentes de IA com o Microsoft Agent Framework. Começamos pelo contraste entre resposta completa e streaming, demonstramos a implementação básica com Azure OpenAI, avançamos para streaming com manutenção de contexto via AgentThread, e finalizamos com padrões de implementação para aplicações web com ASP.NET Core e Server-Sent Events. Cada exemplo pode ser copiado, colado e executado. Ao final, você terá domínio completo sobre streaming no framework.

Insights

  • Streaming reduz a percepção de latência de segundos para milissegundos, o usuário vê a primeira palavra em menos de um segundo enquanto o modelo ainda está gerando o restante
  • O método RunStreamingAsync retorna IAsyncEnumerable, permitindo consumir cada fragmento de texto à medida que o modelo o produz
  • A granularidade do streaming no Microsoft Agent Framework é no nível de token, cada update contém aproximadamente 3 a 5 caracteres
  • Streaming com AgentThread mantém o contexto conversacional intacto, exatamente como no modo não-streaming
  • A decisão entre streaming e resposta completa é arquitetural, interfaces conversacionais pedem streaming enquanto pipelines de processamento pedem resposta completa

Streaming entrega respostas token a token em tempo real

Streaming no Microsoft Agent Framework é a capacidade de receber a resposta de um agente de IA fragmento por fragmento, à medida que o modelo gera cada token, em vez de aguardar a resposta completa. Isso transforma a experiência do usuário de uma espera silenciosa em um acompanhamento fluido e em tempo real, similar ao comportamento do ChatGPT e do Claude.

A espera silenciosa prejudica a adoção de agentes de IA

Quando você chama RunAsync em um agente, o fluxo é síncrono do ponto de vista do usuário: a aplicação envia a pergunta, aguarda o modelo processar todos os tokens da resposta, e só então exibe o resultado completo. Para respostas curtas, isso funciona. Para respostas longas, o usuário enfrenta uma espera que pode variar de dois a trinta segundos sem qualquer feedback visual. Essa latência prejudica diretamente a adoção de qualquer solução baseada em agentes de IA.

Esse comportamento é idêntico ao de carregar uma página web inteira antes de renderizar qualquer elemento. Tecnicamente correto, mas péssimo para a experiência do usuário.

Streaming elimina a espera com entrega progressiva

O streaming inverte a lógica. Em vez de esperar a resposta completa, o modelo envia cada token (ou pequeno grupo de tokens) assim que o gera. A aplicação exibe cada fragmento imediatamente, criando a sensação de que o agente está “digitando” a resposta em tempo real.

sequenceDiagram
    participant U as Usuário
    participant A as Aplicação
    participant M as Modelo LLM

    U->>A: "Explique o GC no .NET"
    A->>M: Requisição com streaming

    M-->>A: "O "
    A-->>U: "O "
    M-->>A: "Garbage "
    A-->>U: "Garbage "
    M-->>A: "Collector "
    A-->>U: "Collector "
    M-->>A: "é o componente..."
    A-->>U: "é o componente..."

    Note over U,M: Cada token é exibido assim que gerado

A diferença na percepção é significativa:

AspectoSem StreamingCom Streaming
Tempo até o primeiro token5-15 segundos< 1 segundo
Percepção de velocidadeLentaRápida
Feedback visualNenhum durante geraçãoContínuo
Experiência do usuárioEspera frustranteAcompanhamento fluido
Tempo total de respostaIdênticoIdêntico

O tempo total de geração é o mesmo. O que muda é quando o usuário começa a receber informação.

RunStreamingAsync é o método central do streaming em .NET

O método RunStreamingAsync é o equivalente streaming do RunAsync. Ele retorna IAsyncEnumerable, que você consome com await foreach. Essa é a forma padrão de implementar Microsoft Agent Framework streaming em aplicações .NET.

Execute com dotnet run e observe: o texto aparece progressivamente, palavra por palavra, em vez de surgir todo de uma vez.

Cada update carrega texto, role e metadados do agente

Cada iteração do await foreach entrega um objeto AgentResponseUpdate. Esse objeto contém mais do que apenas texto:

Text: O fragmento de texto gerado nesta iteração. Tipicamente 3 a 5 caracteres.

AuthorName: Nome do agente. Útil quando múltiplos agentes participam da conversa.

Role: Role da mensagem, geralmente Assistant para respostas do agente.

Contents: Lista de objetos AIContent. Permite identificar o tipo de conteúdo (texto, chamada de função, resultado de função).

O método ToString() de AgentResponseUpdate retorna o conteúdo de Text, por isso Console.Write(update) funciona diretamente.

Python usa o parâmetro stream=True para ativar streaming

Em Python, o streaming usa o parâmetro stream=True no método run:

Configure as variáveis de ambiente antes de executar:

Execute:

Python oferece três padrões para consumir streaming

O streaming em Python oferece flexibilidade para diferentes cenários:

Padrão 1: Iteração assíncrona (exibição em tempo real)

Padrão 2: Finalização direta (resposta completa sem iterar)

Padrão 3: Combinado (iterar e depois obter resultado agregado)

O padrão 3 é útil quando você precisa exibir a resposta progressivamente mas também armazenar o resultado completo para processamento posterior.

AgentThread mantém o contexto mesmo com streaming ativo

O Microsoft Agent Framework streaming com AgentThread combina duas capacidades fundamentais: entrega progressiva de tokens e manutenção do histórico conversacional. Essa combinação entrega a melhor experiência possível em agentes de IA: respostas progressivas que levam em conta o contexto anterior, exatamente como você esperaria de um chatbot profissional.

A implementação é direta. O AgentThread armazena todas as mensagens trocadas, e o RunStreamingAsync simplesmente muda o mecanismo de entrega sem afetar o gerenciamento de estado.

Implementação em .NET com AgentThread e RunStreamingAsync

Observe que a única diferença em relação ao chat sem streaming do artigo anterior é a substituição de RunAsync por RunStreamingAsync e o uso de await foreach para consumir os fragmentos. O AgentThread funciona exatamente da mesma forma.

Implementação em Python com sessão e streaming

O primeiro token chega 29 vezes mais rápido com streaming

Para demonstrar quantitativamente o impacto do streaming, vamos medir o tempo até o primeiro token e o tempo total:

Resultado típico:

[Sem Streaming] Tempo total: 8432ms
[Sem Streaming] Tamanho: 2847 caracteres

[Com Streaming] Primeiro token: 287ms
[Com Streaming] Tempo total: 8391ms
[Com Streaming] Tamanho: 2847 caracteres

O tempo total é praticamente idêntico. A diferença está no tempo até o primeiro token: 287 milissegundos contra 8.432 milissegundos. O usuário começa a receber informação 29 vezes mais rápido.

Aplicações web exigem Server-Sent Events para streaming

Em aplicações console, Console.Write exibe o streaming diretamente. Em aplicações web, o mecanismo muda: você precisa de Server-Sent Events (SSE) ou WebSockets para enviar fragmentos ao navegador em tempo real. O Microsoft Agent Framework streaming integra-se naturalmente com o ASP.NET Core Minimal API, tornando a implementação direta e limpa.

A seguir, demonstramos a abordagem com SSE, que é o padrão adotado pelos principais assistentes de IA do mercado, incluindo ChatGPT e Claude.

SSE com ASP.NET Core segue o mesmo padrão do ChatGPT

Server-Sent Events é a abordagem mais simples e compatível com o padrão que ChatGPT e outros chatbots utilizam:

JavaScript nativo consome SSE sem bibliotecas adicionais

O JavaScript nativo suporta Server-Sent Events sem bibliotecas adicionais:

Essa é exatamente a mesma abordagem que ChatGPT, Claude e outros assistentes utilizam para exibir respostas progressivamente.

Erros em streaming podem ocorrer a qualquer momento da geração

Streaming introduz complexidade adicional no tratamento de erros porque a conexão é mantida aberta durante toda a geração. Erros podem ocorrer a qualquer momento: no início, no meio ou no final da resposta.

O try-catch envolve todo o await foreach. Se um erro ocorrer no meio do streaming, o texto parcial já exibido permanece visível e a mensagem de erro aparece após o conteúdo parcial.

CancellationToken permite interromper a geração a qualquer momento

Em cenários onde o usuário pode cancelar a geração (como um botão “Parar”), use CancellationToken:

Em aplicações web, vincule o CancellationToken ao HttpContext.RequestAborted para cancelar automaticamente quando o cliente desconecta:

A decisão entre streaming e resposta completa é arquitetural

Streaming não é sempre a melhor escolha. A decisão depende do cenário de uso:

CenárioRecomendaçãoMotivo
Chatbot conversacionalUsar streamingExperiência do usuário é prioridade
Interface web interativaUsar streamingFeedback visual imediato
Pipeline de processamentoNão usarResultado completo é necessário para próxima etapa
Geração de relatórios em batchNão usarSem interação humana
API intermediária para outros serviçosDependeSe o consumidor suporta streaming, usar
Aplicação mobileUsar streamingConexões móveis são lentas

A transição de código entre os dois modos é mínima

A transição entre modo completo e streaming é mínima:

Em Python:

A simplicidade dessa transição é intencional. O Microsoft Agent Framework mantém a mesma interface para ambos os modos, mudando apenas o mecanismo de entrega. Essa decisão de design permite adotar streaming gradualmente, sem refatorar código existente.

Um assistente de código funcional demonstra streaming em produção

Vamos construir um exemplo completo utilizando Microsoft Agent Framework streaming: um assistente de código que explica, sugere e corrige snippets em tempo real.

FAQ – Perguntas Frequentes

1. O streaming consome mais tokens do que a resposta completa?

Não. O número de tokens gerados é exatamente o mesmo. Streaming apenas muda o mecanismo de entrega: em vez de receber tudo de uma vez, você recebe fragmento por fragmento. O custo com o provedor de modelo (Azure OpenAI, OpenAI) é idêntico.

2. O AgentThread funciona normalmente com RunStreamingAsync?

Sim. O AgentThread mantém o histórico da conversa independentemente do modo de entrega. Você pode alternar entre RunAsync e RunStreamingAsync na mesma conversa sem perder contexto. O thread acumula as mensagens da mesma forma em ambos os casos.

3. Posso converter o streaming em resposta completa se precisar?

Sim. O Microsoft Agent Framework oferece métodos de extensão para isso. Em .NET, use ToAgentResponseAsync() no IAsyncEnumerable para obter um AgentResponse completo. Isso é útil quando você quer exibir streaming para o usuário mas também precisa da resposta completa para processamento posterior.

4. O que acontece se a conexão cair no meio do streaming?

O streaming é interrompido e uma exceção é lançada. O texto parcial que já foi entregue permanece visível para o usuário. Não há mecanismo automático de retomada. Se precisar de resiliência, implemente lógica de retry que reenvia a pergunta original e descarta os tokens já recebidos.

5. Server-Sent Events ou WebSockets para streaming web?

Para a maioria dos casos de chatbot, Server-Sent Events (SSE) é suficiente e mais simples. SSE é unidirecional (servidor para cliente), perfeito para streaming de respostas. WebSockets são bidirecionais e mais adequados quando o cliente também precisa enviar dados em tempo real durante o streaming, como sinais de cancelamento ou mensagens simultâneas. ChatGPT, Claude e a maioria dos assistentes de IA utilizam SSE.

Compartilhe:

Tiago Tartari

Tiago Tartari

Eu ajudo e capacito pessoas e organizações a transformar problemas complexos em soluções práticas usando a tecnologia para atingir resultados extraordinários.

Qual é o desafio
que você tem hoje?