Negli scorsi esempi abbiamo introdotto il ruolo della classe ChatHistory di Semantic Kernel per rappresentare una sessione di chat con un modello GPT. Tuttavia, per semplificare gli esempi, ci siamo limitati a utilizzarne una istanza static.
Ovviamente, in un'applicazione reale, la history deve essere persistita su uno storage durevole, sia esso SQL Server, Cosmos DB, file system, e via discorrendo. Nel nostro caso useremo Entity Framework e Azure SQL Database.
Innanzi tutto dobbiamo costruire una classe wrapper, che chiameremo ChatSession, così che possiamo decorare la history con delle informazioni ulteriori, quali per esempio l'ID, il nome dell'utente "proprietario" della chat, il titolo, e quant'altro.
public class ChatSession
{
public int Id { get; set; }
public string User { get; set; }
public string Title { get; set; }
public ChatHistory History { get; set; }
}ChatHistory non è ovviamente un tipo nativo di .NET, e pertanto non può essere salvato direttamente. Tuttavia si tratta di un oggetto serializzabile, quindi possiamo semplicemente configurare un value converter nel mapping per poterne gestire il salvataggio su database:
public class ChatDbContext : DbContext
{
public ChatDbContext (DbContextOptions<ChatDbContext> options)
: base(options)
{
}
public DbSet<ChatSession> ChatSession { get; set; } = default!;
override protected void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ChatSession>().ToTable("ChatSession")
.Property(x => x.History)
.HasColumnType("varchar(max)")
.HasConversion(
h => JsonSerializer.Serialize(h, new JsonSerializerOptions() { }),
s => JsonSerializer.Deserialize<ChatHistory>(s, new JsonSerializerOptions() { }));
}
}Nell'esempio in alto, abbiamo specificato che la proprietà History deve essere salvata su una colonna di tipo varchar(max), effettuandone la serializzazione in JSON in fase di scrittura. Per contro, durante la lettura, sarà sufficiente deserializzare il JSON letto.
A questo punto, possiamo modificare il nostro ChatController, aggiungendo un endpoint per creare una nuova chat:
[HttpPost]
public async Task<IActionResult> CreateChat()
{
var session = new ChatSession()
{
User = "some user", // dovrebbe essere letto dallo User corrente
Title = "Placeholder Title", // sarebbe da generare tramite GPT una dopo la prima risposta
History = new ChatHistory("You are a useful AI who answers questions using rhymes.")
};
_dbContext.ChatSession.Add(session);
await _dbContext.SaveChangesAsync();
return Ok(session.Id);
}Il codice è davvero elementare, e si limita a creare una nuova istanza di ChatSession, salvarla tramite DbContext e restituirne l'ID al chiamante.
Questo ID dovrà poi essere passato come parametro all'endpoint PostMessage, che abbiamo visto nello scorso script, e che quindi dovremo modificare come nell'esempio in basso:
[HttpPost("{sessionId}/messages")]
public async IAsyncEnumerable<string> PostMessage(int sessionId, [FromBody] string message)
{
var session = await _dbContext.ChatSession.FindAsync(sessionId);
// session null, return 404
if (session == null)
{
Response.StatusCode = StatusCodes.Status404NotFound;
yield break;
}
// set the entity as modified
session.History.AddUserMessage(message);
var result = _chatCompletionService.GetStreamingChatMessageContentsAsync(session.History);
string responseMessage = string.Empty;
await foreach (var messageContent in result)
{
responseMessage += messageContent.Content;
yield return messageContent.Content;
}
session.History.AddAssistantMessage(responseMessage);
_dbContext.Entry(session).State = EntityState.Modified;
await _dbContext.SaveChangesAsync();
}Abbiamo quindi aggiunto il parametro sessionId al path e, come primo passo, recuperiamo la ChatSession corrispondente dal database, restituendo 404 in caso non esista. Ovviamente in un caso reale, dovremo anche verificare che l'utente corretto sia il "proprietario" di questa sessione di chat.
Poi gestiamo la richiesta a GPT come abbiamo visto in precedenza, e infine, dopo aver aggiunto il messaggio di risposta alla history, andiamo ad aggiornare il dato su database.
Commenti
Per inserire un commento, devi avere un account.
Fai il login e torna a questa pagina, oppure registrati alla nostra community.
Approfondimenti
Integrare Agenti A2A in Azure API Management
Utilizzare Containers in .NET Aspire
Ottimizzare la content-visibility in CSS specificando lo spazio da occupato dall'area non renderizzata
Costruire endpoint SSE in ASP.NET Core
Interazione con ReconnectModal in Blazor
Supporto nativo a JSON in SQL Server 2025
Abilitare automaticamente il force push di un gruppo su Azure DevOps
Introduzione a GitHub Copilot CLI
Evitare memory leaks nelle closure JavaScript
Il nuovo persistent state in Blazor
Creare un agente A2Acon Azure Logic Apps
Importare un servizio esterno in .NET Aspire


