ASP .NET - Interagindo com o Ollama via API


    Hoje vou mostrar como interagir com o Ollama local usando uma API ASP.NET Core.

Neste tutorial veremos como criar uma API Asp.NET e interagir com o Ollama uando os recursos do Microsoft.SemanticKernel e do OllamaSharp.



Criando o projeto

UmAbra o VS 2022 e crie um novo projeto usando o template ASP.NET Core Web API informando o nome OllamaAPI usando o .NET 9.0 , sem autenticação, habilitando o suporte a OpenAPI e usando Controllers.

Após remover o controlador e classe criados no projeto teremos a seguinte estrutura:

No arquivo de projeto vamos incluir a tag  <NoWarn>SKEXP0070</NoWarn> para inibir o aviso SKEXP0070 relacionado ao Semantic Kernel evitando que mensagens específicas de advertências apareçam durante a compilação do projeto.

A seguir vamos incluir no projeto os seguintes pacotes :

1. Microsoft.SemanticKernel
Framework da Microsoft para criação de aplicações de IA semântica. Permite orquestrar LLMs, plugins, memórias e agentes inteligentes dentro de aplicações .NET, integrando IA com dados e lógica de negócio.

2. Microsoft.SemanticKernel.Connectors.Ollama
Extensão oficial do Semantic Kernel para conectar com modelos locais do Ollama. Facilita a configuração de services no Kernel para envio de prompts e recebimento de respostas diretamente dos modelos rodando na máquina.

3. OllamaSharp
Cliente .NET leve para interação direta com a API HTTP do Ollama. Permite executar modelos, enviar prompts, receber respostas streaming e gerenciar modelos, sem a camada de abstração do Semantic Kernel.

4. Swashbuckle.AspNetCore

Fornece uma interface web interativa para explorar e testar APIs ASP.NET Core criando   automaticamente uma página web (/swagger) onde todos os endpoints da API podem ser visualizados e testados. (Removi o pacote da OpenAPI e vou usar a interface do Swagger)

O OllamaSharp faz a comunicação direta com o Ollama e o SemanticKernel mais os conectores Ollama fazem a orquestração avançada de IA dentro das ASP.NET Core.

O Semantic Kernel(SK) não executa o modelo — quem faz isso é o Ollama via OllamaSharp. O SK funciona como camada de orquestração, preparando o contexto e expondo um ponto único de acesso para sua aplicação consumir a IA de forma estruturada.

Agora vamos criar no projeto as pastas Models e Services.

Na pasta Models vamos criar duas classes :

1-  OllamaSettings

public class OllamaSettings
{
   public string ModelName { get; set; } = string.Empty;
   public string Uri { get; set; } = string.Empty;
}

Esta classe encapsula configurações de acesso ao servidor Ollama.

ModelName
→ define qual modelo será usado (ex.: llama3.1:8b).
Uri → URL do servidor Ollama (ex.: http://localhost:11434/).

Ela permite que você configure o modelo e a URI via appsettings.json e injete essas configurações nos serviços (AddOllamaChatCompletion, OllamaService).

Com isso estamos separando a  configuração do código, facilitando manutenção e mudança de modelos ou servidor sem recompilar.

2- ChatPrompt

public class ChatPrompt
{
  public string Message { get; set; } = string.Empty;
}

Esta classe representa a mensagem enviada pelo usuário ao chat.

Message → conteúdo do texto que o usuário quer que a IA responda.

Ela representa o objeto enviado no corpo da requisição POST para o ChatController que iremos criar.

Com isso estamos padronizando o formato de entrada para o chat, permitindo fácil validação e expansão futura (ex.: adicionar ModelName ou outros parâmetros).

Na pasta Services vamos criar um serviço para encapsular a comunicação com o servidor Ollama usando OllamaApiClient criando a interface IOllamaService e a sua implementação em OllamaService:

1-IOllamaService

using OllamaSharp.Models;

public interface IOllamaService
{
     Task<IEnumerable<Model>> GetLocalModelsAsync(CancellationToken
                                                  cancellationToken = default);
}

Define um contrato para qualquer serviço que forneça acesso aos modelos disponíveis localmente no Ollama.

A implementação do método GetLocalModelsAsync vai retornar todos os modelos instalados no servidor Ollama.

Ela permite que os Controllers ou outros serviços usem um serviço de modelo sem conhecer detalhes de implementação (OllamaService ou outro).

Seu uso facilita testar e trocar a implementação sem alterar o código que depende dessa interface.

2-OllmaService

using OllamaSharp;
using OllamaSharp.Models;
namespace OllamaAPI.Services;
public class OllamaService : IOllamaService
{
    private readonly OllamaApiClient _ollamaClient;
    public OllamaService(OllamaApiClient ollamaClient)
    {
        _ollamaClient = ollamaClient;
    }
    public async Task<IEnumerable<Model>> GetLocalModelsAsync(CancellationToken
                                                              cancellationToken = default)
    {
        return await _ollamaClient.ListLocalModelsAsync();
    }
}

Esta implementação vai encapsular a comunicação com o servidor Ollama usando OllamaApiClient.

- Ela fornece métodos de acesso aos modelos locais (GetLocalModelsAsync).
- Centraliza as chamadas HTTP para o Ollama, evitando que o Controller ou outros serviços lidem diretamente com OllamaApiClient.
- Permite adicionar lógica de negócio adicional antes ou depois da chamada ao Ollama (validações, logs, caching, métricas, etc.).
- Mantém a aplicação desacoplada do cliente de API do Ollama.

Vamos definir no arquivo appsettings.json a informação de qual modelo vamos acessar , a Uri da API do Ollama e configurar as mensagens do prompt e do assistente de IA definindo o código abaixo neste arquivo:

{
 "Logging": {
   "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
   }
  },
  "AllowedHosts": "*",
  "OllamaSettings": {
     "ModelName": "llama3.1",
     "Uri": "http://localhost:11434"
  },
  "ModelContextSettings": {
     "SystemMessage": "Sou o Mac, um desenvolvedor Senior na plataforma .NET",
     "AssistantMessage": "Sou o Mac como posso ajudar ?"
   }
}

Vamos agora configurar os serviços e registrá-los no container DI da ASP.NET Core classe Program:

using Microsoft.OpenApi.Models;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using OllamaAPI.Entities;
using OllamaAPI.Services;
using OllamaSharp;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var ollamaSettings = builder.Configuration.GetSection("OllamaSettings")
                                          .Get<OllamaSettings>();
// Registra o serviço IChatCompletionService com a URI e o ModelName do Ollama
builder.Services.AddOllamaChatCompletion(ollamaSettings!.ModelName,
                                             new Uri(ollamaSettings.Uri));
// Registra OllamaService como implementação de IOllamaService no ciclo
builder.Services.AddScoped<IOllamaService, OllamaService>(provider =>
                           new OllamaService(new OllamaApiClient(
                           new Uri(ollamaSettings.Uri))));
// Registra um ChatHistory como um serviço transient
builder.Services.AddTransient<ChatHistory>();
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "OllamaAPI",
        Version = "v1",
        Description = "API para integração com Ollama usando Semantic Kernel",
        Contact = new OpenApiContact
        {
            Name = "Macoratti",
            Email = "macoratti@yahoo"
        }
    });
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "OllamaAPI v1");
    });
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

A seguir vamos criar na pasta Controllers o controlador ChatController:

[ApiController]
[Route("api/chat")]
public class ChatController : ControllerBase
{
    private readonly IChatCompletionService _chatCompletionService;
    private readonly ChatHistory _history;
    private readonly IConfiguration _configuration;
    public ChatController(IChatCompletionService chatCompletionService,
                          ChatHistory history, IConfiguration configuration)
    {
        _chatCompletionService = chatCompletionService;
        _history = history;
        _configuration = configuration;
        // Adiciona as mensagens do sistema e do assistente a partir de appsettings.json
        var systemMessage = _configuration["ModelContextSettings:SystemMessage"];
        var assistantMessage = _configuration["ModelContextSettings:AssistantMessage"];
        if (!string.IsNullOrEmpty(systemMessage))
            _history.AddSystemMessage(systemMessage);
        if (!string.IsNullOrEmpty(assistantMessage))
            _history.AddAssistantMessage(assistantMessage);
    }
     //Envia uma mensagem a API do Ollama usando o modelo selecionado
    [HttpPost]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Post([FromBody] ChatPrompt chatPrompt)
    {
        if (string.IsNullOrEmpty(chatPrompt.Message))
            return BadRequest("Informe uma mensagem");
        _history.AddUserMessage(chatPrompt.Message);
        var response = await _chatCompletionService
                             .GetChatMessageContentAsync(chatPrompt.Message);
        if (response.Content == null)
        {
            return StatusCode(500, "Internal Server Error: " +
                                   "O serviço de Chat retornou uma resposta nula.");
        }
        _history.AddUserMessage(response.Content ?? string.Empty);
        return Ok(new { Message = response.Content });
    }
}

Fluxo arquitetural da integração entre API ASP.NET Core → Semantic Kernel → Ollama,

[Cliente Web/Mobile/Desktop]
        │ envia requisição HTTP (GET ou POST)

[Controller ASP.NET Core]
   ├─ ChatController (chat)
   │     ├─ injeta IChatCompletionService (Semantic Kernel)
   │     └─ injeta ChatHistory

   └─ OllamaController (opcional: lista modelos)
         └─ injeta IOllamaService (OllamaSharp)

[Semantic Kernel (IChatCompletionService)]
   ├─ Recebe mensagem do usuário
   ├─ Combina com ChatHistory para montar prompt completo
   └─ Encaminha prompt ao OllamaApiClient

[OllamaApiClient / OllamaSharp]
   ├─ Faz requisição HTTP para o servidor Ollama local
   └─ Envia prompt + parâmetros (modelo, contexto)

[Servidor Ollama Local]
   ├─ Executa modelo selecionado (ex.: llama3.1:8b)
   └─ Gera resposta de IA

[OllamaApiClient]
   └─ Retorna resposta ao Semantic Kernel

[Semantic Kernel]
   └─ Aplica pós-processamento se necessário

[Controller ASP.NET Core]
   ├─ Adiciona resposta ao ChatHistory
   └─ Retorna JSON para o cliente

[Cliente]
   └─ Exibe resposta contextualizada ao usuário

Resumo do papel de cada camada:

Controller ASP.NET Core → ponto de entrada da aplicação, gerencia requisições e respostas HTTP.
Semantic Kernel (IChatCompletionService) → orquestra o prompt, mantém histórico e gerencia a comunicação com a LLM.
OllamaApiClient / OllamaSharp → cliente .NET que envia/recebe dados do servidor Ollama.
Servidor Ollama Local → executa o modelo e retorna a resposta da IA.
ChatHistory → mantém o contexto da conversa para respostas coerentes.    

Agora odemos executar e testar a integração com a API do Ollama. Para isso o Ollama tem que estar instalado e em execução e o modelo llama3.1 tem que esta presente na sua máquina local :

Executando o projeto veremos na interface do Swagger:

Vamos acessar o endpint /api/chat e fornecer uma mesagem obtendo a resposta da API:

1- Mensagem

2 - Resposta

Podemos criar outro controller -  OllaController - para obter os modelos do Ollama usando este código:

using Microsoft.AspNetCore.Mvc;
using OllamaAPI.Services;
namespace OllamaAPI.Controllers;
[Route("api/ollama")]
[ApiController]
public class OllamaController : ControllerBase
{
    private readonly IOllamaService _ollamaService;
    public OllamaController(IOllamaService ollamaService)
    {
        _ollamaService = ollamaService;
    }
    [HttpGet]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Get(CancellationToken cancellationToken = default)
    {
        var models = await _ollamaService.GetLocalModelsAsync(cancellationToken);
        return Ok(models);
    }
}

Na interface do Swagger agora teremos os seguintes endpoints:

Pegue o projeto aqui: https://github.com/macoratti/OllamaAPI

E estamos conversados...  

"Na verdade, na verdade vos digo que aquele que crê em mim também fará as obras que eu faço, e as fará maiores do que estas, porque eu vou para meu Pai."
João 14:12

Referências:


José Carlos Macoratti