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
RunStreamingAsyncretornaIAsyncEnumerable, 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
AgentThreadmanté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.
|
1 2 3 4 |
// Modo tradicional: resposta completa var response = await agent.RunAsync("Explique como funciona o garbage collector no .NET"); Console.WriteLine(response); // O usuário esperou 8 segundos sem ver nada. Depois, tudo apareceu de uma vez. |
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 geradoA diferença na percepção é significativa:
| Aspecto | Sem Streaming | Com Streaming |
|---|---|---|
| Tempo até o primeiro token | 5-15 segundos | < 1 segundo |
| Percepção de velocidade | Lenta | Rápida |
| Feedback visual | Nenhum durante geração | Contínuo |
| Experiência do usuário | Espera frustrante | Acompanhamento fluido |
| Tempo total de resposta | Idêntico | Idê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.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; var client = new AzureOpenAIClient( new Uri("https://SEU-RECURSO.openai.azure.com/"), new AzureCliCredential()); AIAgent agent = client .GetChatClient("gpt-4o-mini") .CreateAIAgent( instructions: "Você é um assistente técnico especializado em .NET. Responda de forma detalhada.", name: "TechAssistant"); Console.Write("TechAssistant: "); await foreach (var update in agent.RunStreamingAsync("Explique o que é dependency injection em três parágrafos.")) { Console.Write(update); } Console.WriteLine(); |
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:
|
1 2 3 4 5 6 7 8 9 10 |
await foreach (var update in agent.RunStreamingAsync("Qual é a diferença entre Task e Thread?")) { // Texto do fragmento atual Console.Write(update.Text); // Informações adicionais disponíveis: // update.AuthorName - Nome do agente que gerou // update.Role - Role da mensagem (Assistant, User, etc.) // update.Contents - Lista de conteúdos (TextContent, FunctionCallContent, etc.) } |
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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import asyncio from agent_framework.azure import AzureAIClient from azure.identity.aio import AzureCliCredential async def main(): async with ( AzureCliCredential() as credential, AzureAIClient(async_credential=credential).create_agent( instructions="Você é um assistente técnico especializado em .NET. Responda de forma detalhada.", name="TechAssistant" ) as agent, ): print("TechAssistant: ", end="", flush=True) async for chunk in agent.run( "Explique o que é dependency injection em três parágrafos.", stream=True ): if chunk.text: print(chunk.text, end="", flush=True) print() if __name__ == "__main__": asyncio.run(main()) |
Configure as variáveis de ambiente antes de executar:
|
1 2 3 4 5 6 7 |
# Windows set AZURE_AI_PROJECT_ENDPOINT=https://seu-projeto.services.ai.azure.com/ set AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o-mini # macOS/Linux export AZURE_AI_PROJECT_ENDPOINT=https://seu-projeto.services.ai.azure.com/ export AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o-mini |
Execute:
|
1 |
python streaming_basico.py |
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)
|
1 2 3 |
async for chunk in agent.run("Conte uma história curta.", stream=True): if chunk.text: print(chunk.text, end="", flush=True) |
Padrão 2: Finalização direta (resposta completa sem iterar)
|
1 2 3 |
response_stream = agent.run("Conte uma história curta.", stream=True) final = await response_stream.get_final_response() print(final.text) |
Padrão 3: Combinado (iterar e depois obter resultado agregado)
|
1 2 3 4 5 6 7 8 9 10 |
response_stream = agent.run("Conte uma história curta.", stream=True) # Exibe em tempo real async for chunk in response_stream: if chunk.text: print(chunk.text, end="", flush=True) # Depois acessa a resposta completa final = await response_stream.get_final_response() print(f"\n\nTotal de caracteres: {len(final.text)}") |
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; var client = new AzureOpenAIClient( new Uri("https://SEU-RECURSO.openai.azure.com/"), new AzureCliCredential()); AIAgent agent = client .GetChatClient("gpt-4o-mini") .CreateAIAgent( instructions: """ Você é um tutor de arquitetura de software. Explique conceitos de forma progressiva, construindo sobre o que já foi discutido. Use exemplos práticos em .NET quando apropriado. Mantenha respostas focadas e objetivas. """, name: "ArchTutor"); AgentThread thread = new(); Console.WriteLine("=== Tutor de Arquitetura - Streaming ==="); Console.WriteLine("Digite suas perguntas ou 'sair' para encerrar.\n"); while (true) { Console.Write("Você: "); var input = Console.ReadLine(); if (string.IsNullOrWhiteSpace(input) || input.Equals("sair", StringComparison.OrdinalIgnoreCase)) break; Console.Write("\nArchTutor: "); await foreach (var update in agent.RunStreamingAsync(input, thread)) { Console.Write(update); } Console.WriteLine("\n"); } Console.WriteLine("Até logo!"); |
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import asyncio from agent_framework.azure import AzureAIClient from azure.identity.aio import AzureCliCredential async def main(): async with ( AzureCliCredential() as credential, AzureAIClient(async_credential=credential).create_agent( instructions=""" Você é um tutor de arquitetura de software. Explique conceitos de forma progressiva, construindo sobre o que já foi discutido. Use exemplos práticos em Python quando apropriado. Mantenha respostas focadas e objetivas. """, name="ArchTutor" ) as agent, ): session_id = None print("=== Tutor de Arquitetura - Streaming ===") print("Digite suas perguntas ou 'sair' para encerrar.\n") while True: user_input = input("Você: ").strip() if not user_input or user_input.lower() == "sair": break print("\nArchTutor: ", end="", flush=True) async for chunk in agent.run(user_input, session_id=session_id, stream=True): if chunk.text: print(chunk.text, end="", flush=True) if session_id is None and hasattr(chunk, "session_id"): session_id = chunk.session_id print("\n") print("Até logo!") if __name__ == "__main__": asyncio.run(main()) |
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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
using System.Diagnostics; using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; var client = new AzureOpenAIClient( new Uri("https://SEU-RECURSO.openai.azure.com/"), new AzureCliCredential()); AIAgent agent = client .GetChatClient("gpt-4o-mini") .CreateAIAgent( instructions: "Responda de forma detalhada.", name: "Benchmark"); var question = "Explique os princípios SOLID com exemplos em C#."; // Medição SEM streaming var swFull = Stopwatch.StartNew(); var fullResponse = await agent.RunAsync(question); swFull.Stop(); Console.WriteLine($"[Sem Streaming] Tempo total: {swFull.ElapsedMilliseconds}ms"); Console.WriteLine($"[Sem Streaming] Tamanho: {fullResponse.ToString().Length} caracteres"); Console.WriteLine(); // Medição COM streaming var swStream = Stopwatch.StartNew(); long firstTokenMs = 0; int charCount = 0; await foreach (var update in agent.RunStreamingAsync(question)) { if (firstTokenMs == 0 && !string.IsNullOrEmpty(update.Text)) { firstTokenMs = swStream.ElapsedMilliseconds; } charCount += update.Text?.Length ?? 0; } swStream.Stop(); Console.WriteLine($"[Com Streaming] Primeiro token: {firstTokenMs}ms"); Console.WriteLine($"[Com Streaming] Tempo total: {swStream.ElapsedMilliseconds}ms"); Console.WriteLine($"[Com Streaming] Tamanho: {charCount} caracteres"); |
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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); var client = new AzureOpenAIClient( new Uri(builder.Configuration["AzureOpenAI:Endpoint"] ?? "https://SEU-RECURSO.openai.azure.com/"), new AzureCliCredential()); app.MapPost("/api/chat/stream", async (HttpContext context, ChatRequest request) => { AIAgent agent = client .GetChatClient("gpt-4o-mini") .CreateAIAgent( instructions: "Você é um assistente prestativo. Responda em português.", name: "WebAssistant"); context.Response.ContentType = "text/event-stream"; context.Response.Headers.CacheControl = "no-cache"; context.Response.Headers.Connection = "keep-alive"; await foreach (var update in agent.RunStreamingAsync(request.Message)) { if (!string.IsNullOrEmpty(update.Text)) { await context.Response.WriteAsync($"data: {update.Text}\n\n"); await context.Response.Body.FlushAsync(); } } await context.Response.WriteAsync("data: [DONE]\n\n"); await context.Response.Body.FlushAsync(); }); app.Run(); record ChatRequest(string Message); |
JavaScript nativo consome SSE sem bibliotecas adicionais
O JavaScript nativo suporta Server-Sent Events sem bibliotecas adicionais:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
async function streamChat(message) { const outputElement = document.getElementById("response"); outputElement.textContent = ""; const response = await fetch("/api/chat/stream", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message }) }); const reader = response.body.getReader(); const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; const text = decoder.decode(value); const lines = text.split("\n"); for (const line of lines) { if (line.startsWith("data: ") && line !== "data: [DONE]") { const content = line.substring(6); outputElement.textContent += content; } } } } |
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.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; var client = new AzureOpenAIClient( new Uri("https://SEU-RECURSO.openai.azure.com/"), new AzureCliCredential()); AIAgent agent = client .GetChatClient("gpt-4o-mini") .CreateAIAgent( instructions: "Você é um assistente útil.", name: "SafeAssistant"); try { var tokenCount = 0; await foreach (var update in agent.RunStreamingAsync("Explique microservices.")) { Console.Write(update); tokenCount++; } Console.WriteLine($"\n\n[Info] Tokens recebidos: {tokenCount}"); } catch (Azure.RequestFailedException ex) when (ex.Status == 429) { Console.WriteLine("\n\n[Erro] Limite de requisições excedido. Aguarde e tente novamente."); } catch (Azure.RequestFailedException ex) when (ex.Status == 401) { Console.WriteLine("\n\n[Erro] Autenticação falhou. Execute 'az login'."); } catch (OperationCanceledException) { Console.WriteLine("\n\n[Info] Streaming cancelado pelo usuário."); } catch (Exception ex) { Console.WriteLine($"\n\n[Erro] Falha durante streaming: {ex.Message}"); } |
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:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using var cts = new CancellationTokenSource(); // Simular cancelamento após 3 segundos cts.CancelAfter(TimeSpan.FromSeconds(3)); try { await foreach (var update in agent.RunStreamingAsync( "Escreva um ensaio longo sobre arquitetura de software.", cancellationToken: cts.Token)) { Console.Write(update); } } catch (OperationCanceledException) { Console.WriteLine("\n\n[Streaming cancelado]"); } |
Em aplicações web, vincule o CancellationToken ao HttpContext.RequestAborted para cancelar automaticamente quando o cliente desconecta:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
app.MapPost("/api/chat/stream", async (HttpContext context, ChatRequest request) => { var cancellation = context.RequestAborted; // ...setup do agente... await foreach (var update in agent.RunStreamingAsync(request.Message, cancellationToken: cancellation)) { if (!string.IsNullOrEmpty(update.Text)) { await context.Response.WriteAsync($"data: {update.Text}\n\n", cancellation); await context.Response.Body.FlushAsync(cancellation); } } }); |
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ário | Recomendação | Motivo |
|---|---|---|
| Chatbot conversacional | Usar streaming | Experiência do usuário é prioridade |
| Interface web interativa | Usar streaming | Feedback visual imediato |
| Pipeline de processamento | Não usar | Resultado completo é necessário para próxima etapa |
| Geração de relatórios em batch | Não usar | Sem interação humana |
| API intermediária para outros serviços | Depende | Se o consumidor suporta streaming, usar |
| Aplicação mobile | Usar streaming | Conexõ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:
|
1 2 3 4 5 6 7 8 9 |
// Modo completo var response = await agent.RunAsync("Pergunta"); ProcessarResposta(response.ToString()); // Modo streaming await foreach (var update in agent.RunStreamingAsync("Pergunta")) { ProcessarFragmento(update.Text); } |
Em Python:
|
1 2 3 4 5 6 7 |
# Modo completo result = await agent.run("Pergunta") processar_resposta(result.text) # Modo streaming async for chunk in agent.run("Pergunta", stream=True): processar_fragmento(chunk.text) |
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.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
using Azure.AI.OpenAI; using Azure.Identity; using Microsoft.Agents.AI; namespace CodeAssistant; class Program { static async Task Main(string[] args) { var endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") ?? "https://SEU-RECURSO.openai.azure.com/"; var deployment = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT") ?? "gpt-4o-mini"; var client = new AzureOpenAIClient( new Uri(endpoint), new AzureCliCredential()); var instructions = """ Você é um assistente de código especializado em C# e .NET. COMPORTAMENTO: - Analise código enviado pelo usuário e sugira melhorias - Explique erros de forma clara e direta - Sempre forneça código corrigido quando encontrar problemas - Use boas práticas e padrões modernos do .NET FORMATO DE RESPOSTA: - Para análise de código: Problema → Explicação → Código Corrigido - Para perguntas conceituais: Explicação → Exemplo → Referência - Mantenha respostas focadas e objetivas LIMITAÇÕES: - Foque em C# e .NET - Não sugira bibliotecas de terceiros sem justificativa clara """; AIAgent agent = client .GetChatClient(deployment) .CreateAIAgent( instructions: instructions, name: "CodeAssistant"); AgentThread thread = new(); Console.WriteLine("=== Code Assistant - Streaming ==="); Console.WriteLine("Envie código ou perguntas sobre C#/.NET."); Console.WriteLine("Digite 'sair' para encerrar.\n"); while (true) { Console.Write("Você: "); var input = Console.ReadLine(); if (string.IsNullOrWhiteSpace(input) || input.Equals("sair", StringComparison.OrdinalIgnoreCase)) break; Console.Write("\nCodeAssistant: "); try { await foreach (var update in agent.RunStreamingAsync(input, thread)) { Console.Write(update); } } catch (Exception ex) { Console.Write($"\n[Erro] {ex.Message}"); } Console.WriteLine("\n"); } Console.WriteLine("Até logo!"); } } |
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.