AI

Gemma 4 con Ollama e .NET Aspire: LLM in locale con il visualizzatore GenAI completo

Dario Fadda Maggio 5, 2026

Se hai già usato l’integrazione Azure Foundry o Azure OpenAI in .NET Aspire, conosci già quella funzionalità: il visualizzatore GenAI che mostra le conversazioni con il modello all’interno del dashboard di tracing. Fai clic su una traccia, scorri fino alla chiamata LLM, e appare una piccola icona “sparkles”. Cliccandola, si apre un pannello con il log completo: system prompt, messaggio utente, risposta del modello, tool call, conteggio token e finish reason.

A prima vista sembra una funzionalità esclusiva di Azure Foundry. Non lo è. Il dashboard Aspire non controlla se gen_ai.system == "openai" né fa chiamate ad Azure. Si basa sulle OpenTelemetry GenAI semantic conventions: qualsiasi backend che emette span gen_ai.* con la forma corretta ottiene lo stesso trattamento. Ollama, LM Studio, llama.cpp — se espone un’API compatibile con OpenAI Chat Completions, può illuminare lo stesso popup.

Questo articolo mostra come configurare Ollama con il modello Gemma 4 in locale e ottenere il visualizzatore GenAI completo nel dashboard Aspire, senza Azure e senza costi cloud.

Perché eseguire LLM in locale?

I motivi sono diversi a seconda del contesto:

  • Compliance e data privacy: i dati non escono dall’infrastruttura aziendale.
  • Costi prevedibili: niente fatture cloud che raddoppiano durante i picchi di sviluppo AI.
  • Sviluppo offline: il modello funziona anche senza connessione Internet.
  • Iterazione rapida: nessun rate limit durante i test intensivi.

Il meccanismo: OpenTelemetry GenAI conventions

Il dashboard Aspire renderizza il visualizzatore GenAI quando trova span con questi attributi OpenTelemetry:

  • gen_ai.operation.name (es. chat, embedding)
  • gen_ai.request.model — il modello richiesto
  • gen_ai.response.model — il modello che ha risposto
  • gen_ai.input.messages — il prompt, serializzato in JSON
  • gen_ai.output.messages — la risposta, serializzata in JSON
  • gen_ai.usage.input_tokens / gen_ai.usage.output_tokens
  • gen_ai.response.finish_reasons

Quando uno span con questi attributi appare nella vista traces sull’activity source Experimental.Microsoft.Extensions.AI, il dashboard aggiunge l’icona sparkles e mostra il popup.

IChatClient è l’astrazione di Microsoft.Extensions.AI per qualsiasi cosa a cui si possano inviare messaggi chat. Azure OpenAI, Ollama e i modelli locali la implementano direttamente o si adattano ad essa. Il wrapper OpenTelemetry che ci interessa sa solo come tracciare oggetti con la forma di IChatClient.

Le quattro cose da configurare

Per ottenere il visualizzatore GenAI con un modello locale servono esattamente quattro elementi:

  1. Un’integrazione di hosting che esegue il backend del modello come risorsa Aspire e produce una connection string.
  2. Un IChatClient nel servizio consumer, decorato con UseOpenTelemetry() e content capture abilitato.
  3. La registrazione della sorgente di tracing in ServiceDefaults per far fluire gli span GenAI nel tracer provider.
  4. Il toggle di content capture, tramite variabile d’ambiente o via UseOpenTelemetry. Senza di esso il popup appare ma i messaggi sono vuoti.

Mancarne anche solo uno produce output silenziosamente degradato: senza il punto 3 gli span non escono dal processo; senza il punto 4 il popup è vuoto; senza il punto 2 si ottengono solo span HTTP generici.

1. Integrazione hosting: Ollama come risorsa Aspire

Aspire non include un’integrazione Ollama out of the box. Il Community Toolkit ne ha una, ma costruirla da zero mostra il pattern applicabile a qualsiasi backend OpenAI-compatible.

In una nuova class library referenziata dall’AppHost (con il package Aspire.Hosting):

public sealed class OllamaResource(string name)
    : ContainerResource(name), IResourceWithConnectionString
{
    internal const string PrimaryEndpointName = "http";
    private EndpointReference? _primaryEndpoint;

    public EndpointReference PrimaryEndpoint =>
        _primaryEndpoint ??= new EndpointReference(this, PrimaryEndpointName);

    public ReferenceExpression ConnectionStringExpression =>
        ReferenceExpression.Create(
            $"Endpoint={PrimaryEndpoint.Property(EndpointProperty.Url)}");
}

public static class OllamaResourceBuilderExtensions
{
    public static IResourceBuilder<OllamaResource> AddOllama(
        this IDistributedApplicationBuilder builder,
        string name,
        int? port = null)
    {
        var resource = new OllamaResource(name);
        return builder.AddResource(resource)
            .WithImage("ollama/ollama", "latest")
            .WithHttpEndpoint(port: port ?? 11434, targetPort: 11434, 
                             name: OllamaResource.PrimaryEndpointName)
            .WithVolume("ollama-data", "/root/.ollama");
    }
}

Nell’AppHost si aggiunge la risorsa Ollama e si collega al servizio che la usa:

var ollama = builder.AddOllama("ollama")
    .WithModel("gemma4:e2b");

var api = builder.AddProject<Projects.ScrumSummary_Api>("api")
    .WithReference(ollama)
    .WaitFor(ollama);

2. IChatClient con OpenTelemetry nel servizio consumer

Nel servizio che usa il modello, si registra il client con la catena di decoratori corretta:

builder.Services.AddSingleton(sp =>
{
    var connectionString = builder.Configuration.GetConnectionString("ollama")!;
    var endpoint = new Uri(connectionString.Replace("Endpoint=", ""));

    return new OllamaApiClient(endpoint)
        .AsChatClient("gemma4:e2b")
        .AsBuilder()
        .UseOpenTelemetry(configure: options =>
        {
            // Abilita la cattura del contenuto dei messaggi
            options.EnableSensitiveData = true;
        })
        .Build();
});

Il flag EnableSensitiveData = true è il toggle critico. Senza di esso, il popup nel dashboard compare ma non mostra i messaggi (per motivi di privacy è disabilitato di default).

3. Registrazione della sorgente di tracing in ServiceDefaults

Nel progetto ServiceDefaults, aggiungere la sorgente activity di Microsoft.Extensions.AI:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing =>
    {
        tracing
            .AddAspNetCoreInstrumentation()
            .AddHttpClientInstrumentation()
            // Questa è la riga chiave
            .AddSource("Experimental.Microsoft.Extensions.AI");
    });

Senza questa riga gli span GenAI vengono emessi ma non raggiungono l’exporter e non appaiono nel dashboard.

4. Alternative al flag EnableSensitiveData

In ambienti dove non si vuole abilitare il flag nel codice (ad esempio per motivi di compliance), si può usare la variabile d’ambiente:

DOTNET_EXTENSIONS_AI_TELEMETRY_ENABLE_SENSITIVE_DATA=true

Oppure impostarla nell’AppHost solo per i progetti in development:

var api = builder.AddProject<Projects.ScrumSummary_Api>("api")
    .WithReference(ollama)
    .WithEnvironment("DOTNET_EXTENSIONS_AI_TELEMETRY_ENABLE_SENSITIVE_DATA", "true");

Il risultato nel dashboard

Una volta configurati tutti e quattro gli elementi, ogni chiamata al modello Ollama locale appare nelle traces di Aspire come span GenAI. Cliccando sull’icona sparkles si apre il pannello con il log completo della conversazione: system prompt, messaggi utente, risposta del modello con i token usati e il finish reason — esattamente come con Azure OpenAI o Azure Foundry, ma con il modello che gira sulla propria macchina.

Generalizzare ad altri backend

Il pattern si applica a qualsiasi backend compatibile con l’API OpenAI Chat Completions: LM Studio, llama.cpp con server HTTP, vLLM. L’unico requisito è che il client implementi o si adatti a IChatClient e che il wrapper UseOpenTelemetry() venga applicato. Il resto — la registrazione del tracing, il flag di content capture — rimane identico.

Conclusione

Il visualizzatore GenAI di .NET Aspire non è un’esclusiva di Azure. È un’interfaccia costruita sopra gli standard OpenTelemetry, accessibile a chiunque emetta gli span corretti. Quattro configurazioni, nessun servizio cloud obbligatorio, e si ottiene la stessa esperienza di debugging degli LLM che si avrebbe con Azure Foundry — con Gemma 4, Ollama e tutto in locale.

Fonte: Run Gemma 4 with Ollama locally, and keep the Aspire LLM Insights (sparkles and all) — Erik Lieben

💬 Unisciti alla discussione!


Questo è un blog del Fediverso: puoi trovare quindi questo articolo ovunque con @blog@spcnet.it e ogni commento/risposta apparirà qui sotto.

Se vuoi commentare su Gemma 4 con Ollama e .NET Aspire: LLM in locale con il visualizzatore GenAI completo, utilizza la discussione sul Forum.
Condividi la tua esperienza, confrontati con altri professionisti e approfondisci i dettagli tecnici nel nostro 👉 forum community