B-Arts DocumentationIndice generale

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.