Nello scorso script abbiamo introdotto il concetto di plugin in Semantic Kernel, un sistema estremamente potente per estendere il comportamento di un ChatBot in base ai servizi esposti dalla nostra applicazione. Abbiamo visto, per esempio, come possiamo interfacciarlo con una funzione di ricerca ordini che accede al nostro database.
Tra le altre cose, Semantic Kernel espone un sistema per intercettare queste chiamate, i FunctionInvocationFilter, così che possiamo ispezionarle, modificarne il contenuto, o persino la risposta. Una semplice casistica in cui questo strumento risulta utile è per esempio quello di loggare la chiamata a funzione.
Creiamo allora il nostro FunctionLogFilter come segue:
public class FunctionLogFilter : IFunctionInvocationFilter { string NORMAL = Console.IsOutputRedirected ? "" : "\x1b[39m"; string YELLOW = Console.IsOutputRedirected ? "" : "\x1b[93m"; string BOLD = Console.IsOutputRedirected ? "" : "\x1b[1m"; string NOBOLD = Console.IsOutputRedirected ? "" : "\x1b[22m"; private object _lock = new object(); public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { lock (_lock) { Console.WriteLine( $"Function {YELLOW}{BOLD}{context.Function.Name}{NOBOLD}{NORMAL} requested with parameters:"); foreach (var parameter in context.Arguments) { Console.WriteLine($"{BOLD}{parameter.Key}{NOBOLD}: {parameter.Value}"); } } var watch = Stopwatch.StartNew(); await next(context); watch.Stop(); lock (_lock) { Console.WriteLine( $"Function {YELLOW}{BOLD}{context.Function.Name}{NOBOLD}{NORMAL} " + $"completed in {watch.ElapsedMilliseconds}ms with result:"); var resultSerialized = JsonSerializer.Serialize( context.Result.GetValue<object>(), new JsonSerializerOptions() { WriteIndented = true }); Console.WriteLine(resultSerialized); } } }
Si tratta di una classe che implementa l'interfaccia IFunctionInvocationFilter e, nello specifico, il metodo OnFunctionInvocationAsync. Questo metodo funziona in maniera assolutamente analoga ai middleware di ASP.NET Core, e riceve in ingresso due parametri:
- FunctionInvocationContext, che contiene i dati relativi alla richiesta in corso, come i parametri, il nome della funzione, e il suo risultato (dopo che l'esecuzione è stata completata);
- una Function che rappresenta il successivo middleware nella catena di esecuzione.
Il nostro compito è piuttosto semplice: preleviamo alcuni dati (nome della funzione e parametri) dal context e li stampiamo nella console. Successivamente invochiamo il resto della pipeline di esecuzione tramite la function passata, e poi ne visualizziamo il risultato.
Ovviamente, per semplicità nel codice, abbiamo sfruttato direttamente Console.Writeline, ma in un caso reale è magari più indicato usare un ILogger, così da astrarre il sink su cui conferiscono tutti i log.
Affinché il filtro funzioni, dobbiamo registrarlo nell'IoC container di ASP.NET Core:
builder.Services.AddSingleton<IFunctionInvocationFilter, FunctionLogFilter>();
Se abbiamo svolto i passaggi correttamente, potremo verificare come, a una domanda del tipo "Chi ha ordinato un laptop?", seguirà un'esecuzione di SearchOrders simile alla seguente:

Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Ottimizzare le pull con Artifact Cache di Azure Container Registry
Esporre i propri servizi applicativi con Semantic Kernel e ASP.NET Web API
Utilizzare il metodo IntersectBy per eseguire l'intersection di due liste
Utilizzare il metodo Index di LINQ per scorrere una lista sapendo anche l'indice dell'elemento
Generare una User Delegation SAS in .NET per Azure Blob Storage
Utilizzare l nesting nativo dei CSS
Gestione ciclo di vita in .NET Aspire
Utilizzare una qualunque lista per i parametri di tipo params in C#
Path addizionali per gli asset in ASP.NET Core MVC
Gestire eccezioni nei plugin di Semantic Kernel in ASP.NET Core Web API
Ordine e importanza per @layer in CSS
Usare i servizi di Azure OpenAI e ChatGPT in ASP.NET Core con Semantic Kernel
I più letti di oggi
- Gestione ciclo di vita in .NET Aspire
- Sfruttare i nuovi overload di TimeSpan.From* per creare timespan usando numeri interi
- vuoi scoprire tutte le novità per #wpdev in #wp81? iscriviti subito al nostro evento dell'08/07 a Milano! https://aspit.co/wp81-day
- #MAUI per sviluppare applicazioni #Windows e #XPlatf con #net5 con @leoncini117 Seguiteci live su #aspilive da https://aspit.co/ReBuild-20
- ecco tutte le novità pubblicate sui nostri siti questa settimana: https://aspit.co/wkly buon week-end!
- Centralizzare gli endpoint AI Foundry con Azure API Management