B.BAI - Casi d'uso
Connessione a Ollama
using B.BAI;
using static B.BAI.BAIGlobal;
BAIConnection connection = new BAIConnection(eBAITipoProvider.Ollama);
connection.Settings.ModelName = "llama3.2";
bool connected = await connection.ConnectAsync();
if (!connected)
{
foreach (Exception exception in connection.ExceptionList)
Console.WriteLine(exception.Message);
return;
}
Per Ollama, lasciando Settings.Url vuoto viene usato l'URL predefinito del provider.
Connessione a un provider remoto
using B.BAI;
using static B.BAI.BAIGlobal;
BAIConnectionSettings settings = new BAIConnectionSettings();
settings.TipoProvider = eBAITipoProvider.OpenAI;
settings.ApiKey = apiKey;
settings.ModelName = "gpt-5";
BAIConnection connection = new BAIConnection(settings);
bool connected = await connection.ConnectAsync(cancellationToken);
La stessa struttura viene utilizzata per Anthropic e Gemini cambiando TipoProvider, chiave e nome modello. Il provider viene scelto dal costruttore: per cambiarlo creare una nuova connection.
Recuperare e selezionare un modello
List<BAIModel> models = await connection.GetModelsAsync(cancellationToken);
if (models.Count > 0)
{
connection.Settings.ModelName = models[0].Name;
bool connected = await connection.ConnectAsync(cancellationToken);
}
Verificare che la lista non sia vuota prima di selezionare il primo elemento. Gli errori sono disponibili in ExceptionList e Log.
Conversazione con Rules e Context
connection.Messages.Rules.Add("Rispondi in italiano.");
connection.Messages.Rules.Add("Sii sintetico.");
connection.Messages.Context.Add("L'applicazione gestisce ordini e fatture.");
connection.Messages.Add("Come posso annullare un ordine?");
BAIResponse firstResponse = await connection.SendAsync();
connection.Messages.Add("E se la fattura è già stata emessa?");
BAIResponse secondResponse = await connection.SendAsync();
SendAsync usa la history esistente. Dopo ciascun invio riuscito, aggiunge automaticamente una sola risposta Assistant a Messages.
Selezionare Rules e Context
BAIResponse all = await connection.SendAsync(eSendOption.All);
BAIResponse onlyRules = await connection.SendAsync(eSendOption.Rules);
BAIResponse onlyContext = await connection.SendAsync(eSendOption.Context);
BAIResponse noSystemData = await connection.SendAsync(eSendOption.None);
eSendOption controlla soltanto Rules e Context; non cambia la natura conversazionale o stateless del metodo scelto.
Richiesta singola stateless
BAIMessage message = new BAIMessage(eBAIRole.User, "Riassumi questo testo.");
BAIResponse response = await connection.SendSingleAsync(message, eSendOption.Rules);
SendSingleAsync non aggiunge il messaggio e non aggiunge la risposta a Messages.
Richiedere un risultato tipizzato
public class Persona
{
public string Nome { get; set; } = "";
public int Eta { get; set; }
}
connection.Messages.Add("Estrai nome ed età: Mario ha 42 anni.");
BTypeResult<Persona> result = await connection.ExecuteAsync<Persona>(
eSendOption.All, cancellationToken);
Controllare result.IsNothing; quando è false, il valore richiesto è in result.Value.
Impostare la generazione
connection.GenerationSettings.SetCreativity(eBAICreativity.Conservative);
connection.GenerationSettings.MaxTokens = 500;
connection.GenerationSettings.TopP = 0.9;
connection.GenerationSettings.Stop.Add("[END]");
BAIResponse response = await connection.SendSingleAsync(
new BAIMessage(eBAIRole.User, "Genera una descrizione breve."));
Le proprietà lasciate a null non vengono imposte da BAI. SetCreativity modifica esclusivamente Temperature.
connection.GenerationSettings.ResetCreativity();
ResetCreativity riporta Temperature a null senza modificare TopP o TopK.
Timeout e cancellazione
connection.Timeout = TimeSpan.FromSeconds(30);
using CancellationTokenSource cancellation = new CancellationTokenSource();
BAIResponse response = await connection.SendSingleAsync(
new BAIMessage(eBAIRole.User, "Elabora il documento."),
eSendOption.All, cancellation.Token);
Il timeout vale per la singola operazione. Il token permette al chiamante di annullarla esplicitamente. Gli errori e le cancellazioni vengono riportati secondo il normale contratto BAI.
Streaming conversazionale
using B.BAI.BAIParser;
connection.Messages.Add("Scrivi una breve storia.");
await foreach (BAIStreamChunk chunk in connection.SendStreamingAsync(cancellationToken: cancellationToken))
{
if (!string.IsNullOrEmpty(chunk.Content)) Console.Write(chunk.Content);
if (!string.IsNullOrEmpty(chunk.Thinking)) Console.Write(chunk.Thinking);
}
Al completamento riuscito, SendStreamingAsync ricostruisce e aggiunge una sola risposta Assistant a Messages.
Streaming stateless
BAIMessage message = new BAIMessage(eBAIRole.User, "Elenca tre titoli.");
await foreach (BAIStreamChunk chunk in connection.SendSingleStreamingAsync(message))
Console.Write(chunk.Content);
SendSingleStreamingAsync resta stateless e non modifica Messages. Il chunk con Done = true contiene la risposta completa.
Messaggio con allegato
BAIMessage message = new BAIMessage(eBAIRole.User, "Descrivi questa immagine.");
message.AddAttachment("C:\\Documenti\\foto.png");
BAIResponse response = await connection.SendSingleAsync(message);
È possibile aggiungere anche contenuto già disponibile:
message.AddAttachment(imageBytes, "foto.png", "image/png");
message.AddAttachment(file);
Gli allegati sono tradotti nel formato multimodale nativo del provider nelle operazioni Send e SendSingle, comprese le varianti streaming.
Structured Output generic
using B.BUtility;
BTypeResult<Persona> result = await connection.ExecuteSingleAsync<Persona>(
new BAIMessage(eBAIRole.User, "Estrai nome ed età: Mario ha 42 anni."));
if (!result.IsNothing)
Console.WriteLine(result.Value?.Nome);
I metodi generic usano internamente BAIRequestOutput e il wrapper BTypeResult<T>.
Function Calling
static double GetOrderTotal(int orderId)
{
return 125.50d;
}
connection.AddFunction(
"get_order_total",
"Restituisce il totale di un ordine.",
(Func<int, double>)GetOrderTotal);
connection.Messages.Add("Qual è il totale dell'ordine 25?");
BAIResponse response = await connection.SendAsync();
BAI descrive la funzione nel formato nativo del provider, interpreta le tool call, invoca il delegate e prosegue il flusso previsto dalla connection. Il provider e il modello selezionato devono supportare il Function Calling nativo; una chiamata scritta dal modello come semplice testo non viene eseguita. Usare RemoveFunction(name) o ClearFunctions() per rimuovere funzioni registrate.
Function Calling streaming
using B.BAI.BAIParser;
connection.AddFunction(
"get_order_total",
"Restituisce il totale di un ordine.",
(Func<int, double>)GetOrderTotal);
connection.Messages.Add("Qual è il totale dell'ordine 25?");
await foreach (BAIStreamChunk chunk in connection.SendStreamingAsync(cancellationToken: cancellationToken))
{
if (!string.IsNullOrEmpty(chunk.Content)) Console.Write(chunk.Content);
if (!string.IsNullOrEmpty(chunk.Thinking)) Console.Write(chunk.Thinking);
if (chunk.Done && chunk.Response != null)
Console.WriteLine("Operazione completata: " + chunk.Response.Success);
}
Il flusso resta nativo anche quando il modello richiede una funzione. BAI rileva la tool call, esegue il delegate, invia il risultato e continua fino alla risposta conclusiva.
Per una richiesta stateless utilizzare SendSingleStreamingAsync: il ciclo delle funzioni è identico, ma Messages non viene modificata.
Creare un embedding
connection.Settings.EmbeddingModelName = "nomic-embed-text";
List<double> embedding = await connection.GetEmbeddingAsync(
"Documento da trasformare in vettore.",
cancellationToken);
EmbeddingModelName è indipendente dal modello generativo e deve identificare un modello con capacità embeddings. Ollama, OpenAI e Gemini usano gli endpoint nativi; Anthropic restituisce una lista vuota e registra il mancato supporto in ExceptionList e Log.
Confrontare matematicamente due embeddings
using B.BStatics;
List<double> first = await connection.GetEmbeddingAsync("primo testo");
List<double> second = await connection.GetEmbeddingAsync("secondo testo");
double similarity = BMaths.CosineSimilarity(first, second);
CosineSimilarity esegue soltanto il calcolo matematico. Restituisce 0d per vettori nulli, vuoti, di dimensioni diverse o con norma zero.
Controllare una risposta e gli errori
BAIResponse response = await connection.ExecuteAsync("Comando");
if (response.Success)
{
Console.WriteLine(response.Content);
return;
}
foreach (Exception exception in connection.ExceptionList)
Console.WriteLine(exception.Message);
Non usare eccezioni come normale controllo di flusso applicativo: verificare il risultato e consultare ExceptionList e Log quando l'operazione non riesce.
