GPT-OSS - A nova era da IA privada na plataforma .NET


  Hoje vou apresentar o GPT-OSS e como usá-lo na plataforma .NET.

GPT-OSS é o primeiro modelo de código aberto da OpenAI desde o GPT-2, e é um divisor de águas para desenvolvedores que desejam uma IA poderosa sem dependência da nuvem.

Você obtém duas versões – gpt-oss-120b e gpt-oss-20b – ambas oferecendo desempenho sólido em codificação, matemática e uso de ferramentas, mantendo seus dados completamente privados. O modelo 20B é interessante porque roda com apenas 16 GB de memória, tornando-o perfeito para desenvolvimento e experimentação local. Confira o anúncio oficial da OpenAI para ver como esses modelos estão colocando um sério poder de IA diretamente nas mãos dos desenvolvedores.



Rodar o GPT-OSS localmente abre novas possibilidades de experimentação, eficiência de custos e privacidade. Neste guia, você aprenderá a usar o modelo de código aberto GPT-OSS com o Ollama para construir recursos de IA rápidos, privados e com capacidade offline usando C#.

O que você vai precisar:

- Uma máquina com pelo menos 16 GB de RAM e uma GPU capaz (ou um Mac com Apple Silicon).
- O .NET 8 SDK ou superior instalado.
- O Ollama instalado e em execução. (baixe no site https://ollama.com)
- O modelo GPT-OSS:20b baixado com ollama pull gpt-oss:20b

A Microsoft facilitou o trabalho com modelos de IA usando as bibliotecas Microsoft.Extensions.AI. Essas bibliotecas fornecem um conjunto unificado de abstrações, permitindo que você escreva um código que pode funcionar com diferentes provedores de IA – como Ollama, Azure AI ou OpenAI – sem alterar sua lógica central.

Criando um projeto Console

Você pode criar o projeto console no VS 2022 ou terminal usando os comandos:

dotnet new console -n OllamaGPTOSS
cd
OllamaGPTOSS

Para poder se conectar ao Ollama usando Microsoft.Extensions.AI, você precisará de dois pacotes principais. O pacote Microsoft.Extensions.AI fornece as abstrações centrais, enquanto o pacote OllamaSharp atua como o provedor que implementa essas abstrações para o Ollama.

dotnet add package Microsoft.Extensions.AI
dotnet add package OllamaSharp

O OllamaSharp é o responsável pela conexão e Interação com o Ollama. Ele Simplifica a comunicação entre seu código C# (.NET) e o serviço Ollama (que gerencia e executa os modelos de IA localmente, como o GPT-OSS). Ele lida com os detalhes das chamadas de API (como requisições HTTP) para que você possa focar apenas na lógica da sua aplicação.

O OllamaSharp implementa as interfaces de abstração genéricas (IChatClient, IEmbeddingGenerator) fornecidas pela biblioteca Microsoft.Extensions.AI. Isso significa que:

- Seu código se torna portável. Você escreve sua lógica usando a interface (IChatClient), e pode trocar o provedor (por exemplo, de OllamaApiClient para um cliente Azure AI ou OpenAI) sem precisar reescrever a lógica central do seu chat.

- Ele funciona como o "provedor" que liga o Ollama a esse conjunto unificado de ferramentas de IA da Microsoft.

Criando o código na classe Program

Abra Program.cs e substitua seu conteúdo pelo código a seguir. Este exemplo mantém um histórico de chat contínuo e transmite as respostas em tempo real.

using Microsoft.Extensions.AI;
using OllamaSharp;
// Inicializa o cliente da API Ollama visando o modelo "gpt-oss:20b"
IChatClient clienteChat = new OllamaApiClient(new Uri("http://localhost:11434/"), "gpt-oss:20b");
// Mantém o histórico da conversa
List<ChatMessage> historicoChat = new();
Console.WriteLine("Chat GPT-OSS - Digite 'sair' para encerrar");
Console.WriteLine();
// Solicita entrada do usuário em um loop
while (true)
{
    Console.Write("Voce: ");
    var entradaUsuario = Console.ReadLine();
    if (entradaUsuario?.ToLower() == "sair")
        break;
    if (string.IsNullOrWhiteSpace(entradaUsuario))
        continue;
    // Adiciona a mensagem do usuário ao histórico do chat
    historicoChat.Add(new ChatMessage(ChatRole.User, entradaUsuario));
    // Transmite a resposta da IA e exibe em tempo real
    Console.Write("Assistente: ");
    var respostaAssistente = "";
    await foreach (var atualizacao in clienteChat.GetStreamingResponseAsync(historicoChat))
    {
        Console.Write(atualizacao.Text);
        respostaAssistente += atualizacao.Text;
    }
    // Adiciona a mensagem do assistente ao histórico do chat
    historicoChat.Add(new ChatMessage(ChatRole.Assistant, respostaAssistente));
    Console.WriteLine();
    Console.WriteLine();
}

Entendendo o código:

1. Inicialização e Abstração do Cliente de IA

Este é o ponto de partida para a comunicação com o modelo de IA.

O código usa a interface IChatClient que é fornecida pela biblioteca Microsoft.Extensions.AI e permite que o código use qualquer provedor de chat (Ollama, Azure OpenAI, etc.) sem mudar a lógica principal.

A classe OllamaApiClient (do pacote OllamaSharp) implementa essa interface, conectando o aplicativo ao serviço Ollama que está rodando localmente na porta padrão (http://localhost:11434/).

Ao inicializar o cliente, definimos qual modelo queremos usar ("gpt-oss:20b"), garantindo que estamos conversando com a IA correta.

2. Manutenção do Histórico de Conversa

Modelos de IA, por si só, não "lembram" interações anteriores. É responsabilidade do seu código fornecer o contexto.

Estrutura do Histórico: Uma lista de objetos ChatMessage (List<ChatMessage> historicoChat = new();) é usada para armazenar todas as mensagens (tanto do usuário quanto do assistente) em ordem cronológica.

Adição de Mensagens:

Sempre que o usuário digita algo, a mensagem é adicionada ao histórico com o papel (ChatRole.User).

Após a IA responder, a resposta completa da IA também é adicionada ao histórico com o papel (ChatRole.Assistant).

O Contexto: Ao chamar o método de resposta da IA (GetStreamingResponseAsync), passamos todo o historicoChat. É isso que permite que o modelo gere uma resposta que faça sentido no contexto da conversa.

3. Loop Principal e Tratamento de Entrada

O código roda em um loop while (true) para simular uma sessão de chat contínua.
Ele verifica se o usuário digitou "sair" para encerrar o loop com break.

Validação: Usa string.IsNullOrWhiteSpace(entradaUsuario) e continue para ignorar entradas vazias e pedir uma nova entrada, garantindo que apenas mensagens válidas sejam processadas.

4. Respostas em Tempo Real (Streaming)

Este é o ponto mais avançado e que demonstra a experiência de usuário moderna com LLMs.

A chave para o streaming. Em vez de esperar pela resposta completa do modelo, o método clienteChat.GetStreamingResponseAsync(historicoChat) retorna uma sequência assíncrona. O await foreach processa cada pedaço da resposta (cada "token") assim que ele chega.

A linha Console.Write(atualizacao.Text); exibe cada pedacinho da resposta na tela em tempo real, dando a impressão de que a IA está "digitando" a resposta.

Simultaneamente, os pedaços são concatenados em respostaAssistente += atualizacao.Text; para formar a mensagem completa, que será salva no histórico.

5. Finalização e Atualização do Histórico

A última etapa garante que o contexto esteja pronto para a próxima rodada do chat.

Após o await foreach terminar (o que significa que a resposta completa foi recebida), a resposta completa é salva na variável respostaAssistente.

Esta resposta é, então, adicionada ao historicoChat como uma mensagem do ChatRole.Assistant.

O loop recomeça, e o modelo terá acesso à conversa inteira, incluindo a última resposta que ele mesmo gerou.

Este código é um modelo básico, mas robusto, para qualquer aplicação que envolva interação contínua com modelos de IA. Ele ensina sobre persistência de contexto, abstração de provedores e programação assíncrona.

Executando o projeto teremos o seguinte resultado:

Temos um chat funcional acessando uma LLM Local com grande poder de fogo.

Este é apenas o começo. As bibliotecas Microsoft.Extensions.AI também suportam chamada de função (function calling), permitindo que você dê ao seu LLM local acesso aos seus métodos C#, APIs e dados. É aqui que você pode construir aplicativos verdadeiramente poderosos e "agênticos" (agentic).

Pegue o projeto aqui: OllamaGPTOSS.zip

E estamos conversados...  

"Porque Deus não nos destinou para a ira, mas para a aquisição da salvação, por nosso Senhor Jesus Cristo,
¹⁰ Que morreu por nós, para que, quer vigiemos, quer durmamos, vivamos juntamente com ele."
1 Tessalonicenses 5:9,10

Referências:


José Carlos Macoratti