> ## Content Index
> Fetch the complete content index at: https://www.azurebrasil.cloud/llms.txt
> Use this file to discover other available public pages before exploring further.

# Foundry IQ: a camada de conhecimento dos agentes no Microsoft Foundry - Parte 4
- URL: https://www.azurebrasil.cloud/blog/foundry-iq-a-camada-de-conhecimento-dos-agentes-no-microsoft-foundry-parte-4/
- Published: 2026-02-12T12:30:01.000Z
- Updated: 2026-02-12T12:30:01.000Z
- Author: Talles Valiatti
- Tags: Foundry IQ

No post de hoje, vamos utilizar o agente criado na [Parte 3](https://www.azurebrasil.cloud/blog/foundry-iq-a-camada-de-conhecimento-dos-agentes-no-microsoft-foundry-parte-3/) dentro de uma aplicação console, permitindo interagir com ele de forma direta via terminal, com visual bonito e respostas em tempo real.

> Antes de seguir na parte 4, recomendo a leitura das partes anteriores!  
>  
> [Leia a parte 1](https://www.azurebrasil.cloud/blog/foundry-iq-a-camada-de-conhecimento-dos-agentes-no-microsoft-foundry-parte-1/), [parte 2](https://www.azurebrasil.cloud/blog/foundry-iq-a-camada-de-conhecimento-dos-agentes-no-microsoft-foundry-parte-2/) e [parte 3](https://www.azurebrasil.cloud/blog/foundry-iq-a-camada-de-conhecimento-dos-agentes-no-microsoft-foundry-parte-3/)!

Para começar, vamos criar uma nova aplicação console com o seguinte comando:

```bash
dotnet new console -n FoundryIqOverview.Console

```

Com o projeto criado, iremos adicionar os pacotes necessários via NuGet:

```bash
dotnet add package Azure.AI.Projects --version 1.2.0-beta.5
dotnet add package Azure.AI.Projects.OpenAI --version 1.0.0-beta.5
dotnet add package Azure.Identity --version 1.17.1
dotnet add package Spectre.Console --version 0.54.0
```

Esses pacotes permitem comunicação com o Microsoft Foundry e deixam o terminal mais elegante com o uso do [Spectre.Console](https://spectreconsole.net/?ref=azurebrasil.cloud).

Agora, vamos criar uma classe de serviço que irá abstrair a implementação do SDK:

```csharp
using Azure.AI.Projects;
using Azure.Identity;

namespace FoundryIqOverview.Console.Services;

public partial class MicrosoftFoundryService(string projectEndpoint)
{
    private AIProjectClient CreateAiProjectClient()
    {
        return new(new Uri(projectEndpoint), new AzureCliCredential());
    }
}
```

Aqui, usamos `new AzureCliCredential()` para autenticar a aplicação via CLI do Azure. Isso significa que o usuário que estiver autenticado localmente com `az login` será utilizado para acessar os recursos no Azure, simplificando bastante o desenvolvimento local sem a necessidade de configurar segredos ou credenciais adicionais.

Estamos estruturando essa classe como [***partial***](https://learn.microsoft.com/pt-br/dotnet/csharp/programming-guide/classes-and-structs/partial-classes-and-methods?ref=azurebrasil.cloud) para que cada funcionalidade do serviço (criação de agente, recuperação de agente, criação de conversa etc.) fique isolada em um arquivo diferente. Essa abordagem melhora a organização, facilita a manutenção e permite que cada método tenha foco em uma única responsabilidade.

A seguir, criaremos os métodos individuais:

**Criar agente**

```csharp
using Azure.AI.Projects;
using Azure.AI.Projects.OpenAI;

namespace FoundryIqOverview.Console.Services;

public partial class MicrosoftFoundryService
{
    public async Task<AgentRecord> CreateAgentAsync(string agentName, string instructions, string model)
    {
        var projectClient = CreateAiProjectClient();

        AgentDefinition agentDefinition = new PromptAgentDefinition(model)
        {
            Instructions = instructions,
        };

        await projectClient.Agents.CreateAgentVersionAsync(
            agentName,
            options: new AgentVersionCreationOptions(agentDefinition));

        return await GetAgentAsync(agentName);
    }
}
```

**Buscar agente por nome**

```csharp
using Azure.AI.Projects.OpenAI;

namespace FoundryIqOverview.Console.Services;

public partial class MicrosoftFoundryService
{
    public async Task<AgentRecord> GetAgentAsync(string agentName)
    {
        var projectClient = CreateAiProjectClient();
        var agent = await projectClient.Agents.GetAgentAsync(agentName);
        return agent == null ? throw new Exception("Not possible to retrieve the agent") : agent.Value;
    }
};
```

**Criar conversa**

```csharp
using Azure.AI.Projects.OpenAI;

namespace FoundryIqOverview.Console.Services;

public partial class MicrosoftFoundryService
{
    public async Task<ProjectConversation> CreateConversationAsync()
    {
        var projectClient = CreateAiProjectClient();
        
        ProjectConversation conversation = await projectClient.OpenAI.Conversations.CreateProjectConversationAsync();
        return conversation;
    }
};
```

Agora, vamos definir alguns records para representar os eventos que queremos rastrear durante a execução do agente (resposta, conclusão, metadados etc.):

```csharp
namespace FoundryIqOverview.Console.Services.Models;

public abstract record ResponseEvent;

public record ResponseContentEvent(string Id, string Message) : ResponseEvent;

public record ResponseCompletedEvent(string ResponseId) : ResponseEvent;

public record ResponseNonMappedEvent(string EventType) : ResponseEvent;

public record ResponseMetadataEvent(
    int InputTokenCount,
    int OutputTokenCount,
    IEnumerable<string> Tools) : ResponseEvent;
```

Com isso, iremos criar o método `RunAsync` do nosso serviço:

```csharp
using System.Diagnostics.CodeAnalysis;
using Azure.AI.Projects;
using Azure.AI.Projects.OpenAI;
using Azure.Identity;
using FoundryIqOverview.Console.Services.Models;
using OpenAI.Responses;

namespace FoundryIqOverview.Console.Services;

public partial class MicrosoftFoundryService
{
    [Experimental("OPENAI001")]
    public async Task RunAsync(
        string agentName, 
        string agentVersion, 
        string conversationId,
        string input,
        Func<ResponseEvent, Task> onUpdate)
    {
        var projectClient = CreateAiProjectClient();
        
        AgentReference agentReference = new(name: agentName, version: agentVersion);
        
        ProjectResponsesClient responseClient = projectClient.OpenAI.GetProjectResponsesClientForAgent(
            agentReference,
            defaultConversationId: conversationId);
        
        var updates = responseClient.CreateResponseStreamingAsync(input);
        
        var responseId = string.Empty;
        await foreach (var update in updates)
        {
            switch (update)
            {
                
                case StreamingResponseOutputTextDeltaUpdate deltaUpdate: 
                    await onUpdate(new ResponseContentEvent(deltaUpdate.ItemId, deltaUpdate.Delta));
                    break;
                case StreamingResponseCompletedUpdate completedUpdate:
                    responseId = completedUpdate.Response.Id;
                    await onUpdate(new ResponseCompletedEvent());
                    break;
                default:
                    await onUpdate(new ResponseNonMappedEvent(update.GetType().ToString()));
                    break;
            }
        }

        var result = await responseClient.GetResponseAsync(
            new GetResponseOptions(responseId));

        await onUpdate(new ResponseMetadataEvent(
            result.Value.Usage.InputTokenCount,
            result.Value.Usage.OutputTokenCount,
            result.Value.Tools.Select(x => x.GetType().ToString())));
    }
}

```

O parâmetro `Func<ResponseEvent, Task> onUpdate` permite que o método `RunAsync` comunique os diferentes eventos da execução (texto parcial, finalização, metadados etc.) de forma assíncrona com a camada de apresentação. Ao chamar `await onUpdate(...)`, passamos eventos para serem processados em tempo real, por exemplo, atualizando o conteúdo mostrado no terminal.

Agora com o serviço pronto, vamos para a classe principal do console app, onde tudo se conecta:

```csharp
using System.Text;
using Spectre.Console;
using FoundryIqOverview.Console.Services;
using FoundryIqOverview.Console.Services.Models;
using Spectre.Console.Rendering;

#pragma warning disable OPENAI001

var agentName = "contoso-software-agent";
var projectUrl = "<your-project-url>";

AnsiConsole.Clear();

var prompt = """
             Explain (1) the Azure Key Vault authentication scenario called “Application-plus-user” (what it means, how the auth flow works, and what permissions the application identity and the user each need), and (2) Contoso’s SLA policy, including availability commitments, incident severity levels, and its operational details.
             """;

AnsiConsole.Write(
    new FigletText("Contoso Agent")
        .Centered()
        .Color(Color.CornflowerBlue));

AnsiConsole.MarkupLine(
    $"[yellow]Prompt:[/] [cyan]{prompt} [/]\n"); 

if (string.IsNullOrWhiteSpace(projectUrl))
{
    AnsiConsole.MarkupLine("[red]PROJECT_ENDPOINT environment variable not set.[/]");
    return;
}

var service = new MicrosoftFoundryService(projectUrl);
var agent = await service.GetAgentAsync(agentName);
var conversation = await service.CreateConversationAsync();

var buffer = new StringBuilder();

// Initial renderable (recommended by the official tutorial)
IRenderable BuildPanel(string content) =>
    new Panel(new Markup(content))
    {
        Header = new PanelHeader("[bold green]Agent Response[/]"),
        Border = BoxBorder.Rounded,
        Padding = new Padding(1, 1)
    };

await AnsiConsole.Live(BuildPanel("[grey]Waiting for agent response...[/]"))
    .AutoClear(true)
    .Overflow(VerticalOverflow.Visible)
    .StartAsync(async ctx =>
    {
        await service.RunAsync(
            agent.Name,
            agent.Versions.Latest.Version,
            conversation,
            prompt,
            async ev =>
            {
                switch (ev)
                {
                    case ResponseContentEvent update:
                        buffer.Append(Markup.Escape(update.Message));
                        ctx.UpdateTarget(BuildPanel(buffer.ToString()));
                        break;

                    case ResponseCompletedEvent:
                        buffer.Append("\n\n[bold green]✔ Response completed[/]");
                        ctx.UpdateTarget(BuildPanel(buffer.ToString()));
                        break;

                    case ResponseMetadataEvent usage:
                        buffer.Append($"""
                            
                            [grey]────────────────────────────[/]
                            [bold yellow]Usage[/]
                            • Input Tokens: [cyan]{usage.InputTokenCount}[/]
                            • Output Tokens: [cyan]{usage.OutputTokenCount}[/]
                            
                            """);

                        foreach (var tool in usage.Tools)
                        {
                            buffer.Append($"• Tool used: [magenta]{tool}[/]\n");
                        }

                        ctx.UpdateTarget(BuildPanel(buffer.ToString()));
                        break;
                }

                await Task.CompletedTask;
            });
    });
AnsiConsole.Write(BuildPanel(buffer.ToString()));

```

O que esse código faz:

1. **Configura o agente**
  - Define o nome do agente (`contoso-software-agent`)
  - Define a URL do projeto no **Microsoft Foundry**
2. **Limpa e prepara o console**
  - Limpa a tela (`AnsiConsole.Clear()`)
  - Mostra um título grande estilizado com **FigletText**
  - Exibe o *prompt* que será enviado ao agente
3. **Valida configuração básica**
  - Verifica se a URL do projeto existe
  - Encerra o programa se estiver inválida
4. **Inicializa os serviços do Foundry**
  - Cria o `MicrosoftFoundryService`
  - Obtém o agente pelo nome
  - Cria uma nova conversa (***conversation***)
5. **Prepara o buffer de saída**
  - Usa um `StringBuilder` para juntas os textos da resposta do agente
6. **Cria um painel visual reutilizável**
  - Envolve o texto da resposta em um `Panel`
  - Esse painel é atualizado em tempo real
7. **Exibe a resposta em tempo real (streaming)**
  - Usa `AnsiConsole.Live(...)`
  - Enquanto o agente responde:
    - Atualiza o painel conforme novos eventos são recebidos
    - Mostra a resposta sendo incrementada no console
8. **Processa os eventos retornados pelo agente**
  - **ResponseContentEvent**
    - Recebe texto parcial da resposta
    - Atualiza o painel imediatamente
  - **ResponseCompletedEvent**
    - Indica que o agente terminou de responder
    - Adiciona uma mensagem de conclusão
  - **ResponseMetadataEvent**
    - Mostra métricas de uso:
      - Tokens de entrada
      - Tokens de saída
    - Lista ferramentas utilizadas pelo agente
9. **Renderiza o resultado final**
  - Após o streaming terminar, exibe o painel final com todo o conteúdo acumulado

Estamos utilizando a biblioteca [Spectre.Console](https://spectreconsole.net/?ref=azurebrasil.cloud) para deixar o aplicativo de terminal mais bonito, interativo e fácil de acompanhar. Com ela, conseguimos criar painéis, animações e estilos visuais que enriquecem a experiência de uso, especialmente ao lidar com respostas de agentes em tempo real.

O valor de `projectUrl`, necessário para conectar ao projeto do Microsoft Foundry, pode ser encontrado na página principal do portal do Foundry:

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2026/01/Screenshot-2026-01-29-at-16.56.17.png)

O prompt utilizado neste exemplo é exatamente o mesmo da Parte 3, garantindo consistência nos testes e permitindo comparar os resultados obtidos por diferentes interfaces:

```
Explain (1) the Azure Key Vault authentication scenario called 
“Application-plus-user” (what it means, how the auth flow works, and what permissions the 
application identity and the user each need), and (2) Contoso’s SLA policy, including 
availability commitments, incident severity levels, and its operational details. 
```

Antes de executar a aplicação, devemos nos autenticar na *tenant* do Azure diretamente pelo terminal, garantindo que as credenciais locais estejam disponíveis para o SDK:

```
az login
```

Com a autenticação feita, vamos executar o console app:

```
dotnet run
```

Temos então o resultado da execução, com o agente respondendo diretamente no terminal em tempo real:

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2026/01/Screenshot-2026-01-29-at-16.54.39.png)

No final da execução, o app também exibe os metadados da *ru*n, como quantidade de tokens usados e ferramentas utilizadas durante a resposta:

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2026/01/Screenshot-2026-01-29-at-16.54.07.png)

Além disso, podemos acessar novamente o portal do Foundry para verificar os dados da execução diretamente na interface web, incluindo histórico da conversa, uso de tokens e muito mais:

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2026/01/Screenshot-2026-01-29-at-17.00.04.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2026/01/Screenshot-2026-01-29-at-17.00.24.png)

Você já pode baixar o projeto por esse [link](https://github.com/TallesValiatti/FoundryIqOverview?ref=azurebrasil.cloud), e não esquece de me seguir no [LinkedIn](https://www.linkedin.com/in/tallesvaliatti/?ref=azurebrasil.cloud)!

Até a próxima, abraços!