Nello scorso script abbiamo iniziato ad occuparci dei Large Language Model, e in particolare di come integrare la nostra applicazione con essi tramite Semantic Kernel.
L'esempio che abbiamo realizzato sfrutta il cosiddetto endpoint sincrono: è di semplice utilizzo, ma ha il difetto di restituire la risposta solo dopo che questa sia stata interamente elaborata del modello AI. Ciò si traduce in tempi di attesa per l'utente, durante i quali effettivamente non accade nulla.
Un'alternativa - che richiede alcune minime modifiche al codice - è quella di utilizzare lo streaming endpoint, che invece ritorna uno stream di parole che, dal nostro controller, possiamo direttamente girare al client.
Riprendiamo l'esempio che abbiamo visto in precedenza, e modifichiamo la action in questo modo:
[HttpPost] public async IAsyncEnumerable<string> PostMessage([FromBody] string message) { ChatHistory.AddUserMessage(message); var result = _chatCompletionService.GetStreamingChatMessageContentsAsync(ChatHistory); string responseMessage = string.Empty; await foreach (var messageContent in result) { responseMessage += messageContent.Content; yield return messageContent.Content; } ChatHistory.AddAssistantMessage(responseMessage); }
Come possiamo notare, innanzi tutto il tipo restituito dal codice in alto è ora un IAsyncEnumerable, ossia un array di string che viene ritornato al client sotto forma di stream. Ci siamo occupati di questa funzionalità di ASP.NET Core in un precedente script (https://www.aspitalia.com/script/1458/Effettuare-Stream-Risposta-ASP.NET-Core-Tramite-IAsyncEnumerable.aspx).
Inoltre, questa volta abbiamo invocato il metodo GetStreamingChatMessageContentsAsync, che invece dell'intera risposta, ritorna a sua volta un oggetto IAsyncEnumerable, che possiamo iterare tramite un costrutto aync foreach.
Ognuno degli elementi della risposta, che altro non sono i vari token generati dal modello, può essere poi restituito al client tramite yield return. Come possiamo notare, però, è anche importante accumulare tutti gli elementi all'interno di una variable responseMessage, così che una volta che la risposta sia terminata, possiamo aggiungerla interamente alla history in modo da mantenerne traccia nella successive interazioni.
Se ora proviamo a eseguire questo codice, vedremo semplicemente che il risultato ottenuto è questa volta un array di elementi string. Ma se proviamo a invocarlo da un client che supporti lo streaming, come per esempio il codice di questo script (https://www.aspitalia.com/script/1459/Sfruttare-Streaming-Chiamata-Http-Blazor.aspx) noteremo come effettivamente la risposta di ChatGPT si componga gradualmente, man mano che viene generata dal modello in Azure.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Conoscere il rendering Server o WebAssembly a runtime in Blazor
Utilizzare QuickGrid di Blazor con Entity Framework
Effettuare il log delle chiamate a function di GPT in ASP.NET Web API
Estrarre dati randomici da una lista di oggetti in C#
Creare alias per tipi generici e tuple in C#
Utilizzare l'espressione if inline in una pipeline di Azure DevOps
Popolare una classe a partire dal testo, con Semantic Kernel e ASP.NET Core Web API
Effettuare il binding di date in Blazor
Usare i servizi di Azure OpenAI e ChatGPT in ASP.NET Core con Semantic Kernel
Combinare Container Queries e Media Queries
Generare un hash con SHA-3 in .NET