Probabilmente è persino superfluo spiegare i vantaggi e le peculiarità dei Large Language Model (LLM) nell'ambito delle applicazioni moderne. Grazie a OpenAI - o ad Azure OpenAI - possiamo aggiungere funzionalità alle nostre applicazioni che erano assolutamente impensabili fino a un paio di anni fa.
Nel corso di questo script, e dei successivi, vedremo come possiamo integrare questi servizi in un'applicazione ASP.NET Core.
Il primo passo è quello di determinare quale libreria vogliamo usare per interfacciarci con i modelli. Esistono diverse opzioni, dalle librerie native di OpenAI, all'Azure OpenAI SDK. Tuttavia, uno degli approcci più utilizzati è quello di sfruttare Semantic Kernel, una libreria open source di Microsoft che costituisce un livello di astrazione intermedio tra il nostro codice e le API native.
Quali sono i vantaggi?
- Innanzi tutto il fatto che abbiamo un'unica codebase, che possiamo usare per interagire con diversi modelli, spesso cambiando solo alcune righe di codice nella configurazione.
- Inoltre, Semantic Kernel ha una serie di primitive, di oggetti e componenti di alto livello, che implementano già molte delle logiche che altrimenti dovremmo realizzare autonomamente.
- Infine, la libreria tende a essere un po' più stabile in termini di breaking change tra una versione e l'altra, rispetto alle controparti native.
Iniziamo a vedere un esempio base, che poi renderemo via via più complesso nei prossimi script.
Come al solito, il primo passo è quello di aggiungere il pacchetto NuGet al progetto ASP.NET Core:
dotnet add package Microsoft.SemanticKernel
Il modo più semplice per sfruttare il Chat Completion endpoint di OpenAI è tramite un servizio chiamato IChatCompletionService, che possiamo registrare nella dependency injection di ASP.NET Core:
public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Altro codice qui... builder.Services.Configure<AzureConfig>(builder.Configuration.GetSection("AzureConfig")); builder.Services.AddSingleton<IChatCompletionService>(sp => { AzureConfig options = sp.GetRequiredService<IOptions<AzureConfig>>().Value; return new AzureOpenAIChatCompletionService( options.OpenAi.DeploymentName, options.OpenAi.OpenAiEndpoint, options.OpenAi.OpenAiKey); }); // .. altro codice qui .. }
Nel codice in alto abbiamo registrato IChatCompletionService come singleton, in particolare restituendo un'istanza di AzureOpenAIChatCompletionService, ossia la versione di questo servizio fatta per interfacciarsi con Azure OpenAI. Se invece stessimo utilizzando OpenAI, avremmo dovuto restituire un'istanza di OpenAIChatCompletionService.
Come possiamo notare, in fase di inizializzazione, abbiamo recuperato gli estremi del servizio dalla configurazione, e poi passato DeploymentName, Endpoint e Key al costruttore.
A questo punto siamo pronti per sfruttare questo oggetto in un ChatController:
[Route("api/[controller]")] [ApiController] public class ChatController : ControllerBase { private IChatCompletionService _chatCompletionService; public static ChatHistory ChatHistory { get; } = new ChatHistory( "You are a useful AI who answers questions using rhymes."); public ChatController(IChatCompletionService chatCompletionService) { _chatCompletionService = chatCompletionService; } }
Il controller in alto, oltre che iniettare un'istanza di IChatCompletionService, mantiene anche una ChatHistory (qui per semplicità, in un field static), che conterrà l'elenco dei messaggi scambiati con la AI: i modelli GPT sono infatti stateless, e pertanto l'unico modo con cui possono avere cognizione dei messaggi passati è quello di mandare l'intera history a ogni richiesta.
Quando creiamo questo oggetto, dobbiamo anche specificare il cosidetto System Prompt, ossia le istruzioni di base per il modello GPT su come comportarsi durante la chat.
Ora non ci resta che creare un'action tramite cui inviare e ricevere messaggi:
[HttpPost] public async Task<IActionResult> PostMessage([FromBody] string message) { ChatHistory.AddUserMessage(message); var result = await _chatCompletionService.GetChatMessageContentAsync(ChatHistory); string responseMessage = result.ToString(); ChatHistory.AddAssistantMessage(responseMessage); return Ok(responseMessage); }
Questa action, invocabile in POST, accetta un messaggio da parte dell'utente che, come prima cosa, viene aggiunto alla History. Successivamente invoca GetChatMessageContentAsync passando l'intera history, così che - come detto - il modello abbia conoscenza non solo dell'ultimo messaggio dell'utente, ma anche di tutto il resto della conversazione eseguita fino a quel momento.
Una volta ottenuta la risposta, non dobbiamo far altro che restituirla al client.

Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Migliorare l'organizzazione delle risorse con Azure Policy
Utilizzare Container Queries nominali
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Rinnovare il token di una GitHub App durante l'esecuzione di un workflow
Cambiare la chiave di partizionamento di Azure Cosmos DB
Creare una custom property in GitHub
Generare HTML a runtime a partire da un componente Razor in ASP.NET Core
Introduzione ai web component HTML
Cancellare una run di un workflow di GitHub
Migliorare i tempi di risposta di GPT tramite lo streaming endpoint in ASP.NET Core
Configurare il nome della run di un workflow di GitHub in base al contesto di esecuzione
I più letti di oggi
- Validazione automatica dei parametri in Web API con ASP.NET Core 2.1
- Recuperare i file utilizzati di recente in un'Universal App
- Applicare un'animazione al contenuto di un ContentControl nella Universal Windows Platform
- AI&ML Conference 2019 - Milano
- .NET Serverless Day - Online
- Rilasciata la versione Beta 2 di Silverlight 2.0