Bouw een AI-chatbot in Delphi: gespreksgeheugen en streaming

· Componenten

Snel antwoord: een chatbot is een chat completion plus geheugen. sgcWebSockets levert een speciaal TsgcAIChat-component dat de volledige berichtgeschiedenis voor je bewaart, zodat elke beurt wordt verstuurd met het gesprek tot dan toe en het model in context kan antwoorden. Plaats het component, stel de API-sleutel en het model in, en roep dan Chat aan voor een antwoord of ChatStream voor een live, token-voor-token typ-effect. Het gesprek wissen is één aanroep van ClearHistory.

Als je al eens een LLM vanuit Delphi hebt aangeroepen, is je waarschijnlijk iets vervelends opgevallen: stel een vervolgvraag en het model doet alsof het je nog nooit gesproken heeft. Dat is geen bug, zo werkt een one-shot completion nu eenmaal. Dat omzetten in een echte conversationele assistent is het verschil tussen speelgoed en een chatbot, en het komt neer op één idee: geheugen.

One-shot completion versus een chatbot

Een LLM-API is stateless. Elke aanvraag is onafhankelijk, dus het model weet alleen wat je in die aanvraag stopt. Een one-shot-aanroep stuurt een enkele prompt:

// User: "What is the capital of France?"  ->  "Paris."
// User: "And its population?"             ->  "Population of what?"

De tweede vraag mislukt omdat de server niets uit de eerste heeft bewaard. Een chatbot lost dit op door het hele gesprek elke keer opnieuw te versturen: het systeembericht, elke gebruikersbeurt en elk assistent-antwoord, in volgorde. Het model leest de geschiedenis, ziet dat "its" naar Parijs verwijst, en antwoordt correct. Je hebt hiervoor geen database of sessieserver nodig, alleen een berichtenlijst die met het gesprek meegroeit. Het enige echte werk is die lijst opbouwen en onderhouden, en dat is precies wat het chatbotcomponent voor je doet.

Het TsgcAIChat-component: geheugen voor je geregeld

In plaats van met de hand een berichtenarray op te tuigen, plaats je een TsgcAIChat op je formulier. Het beheert de gespreksgeschiedenis intern, voegt elk gebruikersbericht en elk assistent-antwoord automatisch toe, en stuurt de opgebouwde context bij elke aanroep mee. Je stelt de provider, de API-sleutel en een model in, en roept dan gewoon Chat aan.

uses
  sgcAI, sgcAI_Chat;

var
  Bot: TsgcAIChat;
begin
  Bot := TsgcAIChat.Create(nil);
  Bot.Provider := aicpOpenAI;
  Bot.ChatOptions.ApiKey := 'sk-...';
  Bot.ChatOptions.Model  := 'gpt-4o-mini';
  Bot.SystemMessage := 'You are a concise assistant for Delphi developers.';

  // Each call adds to the same conversation:
  ShowMessage(Bot.Chat('What is the capital of France?'));  // "Paris."
  ShowMessage(Bot.Chat('And its population?'));             // answers about Paris
end;

Omdat het component de eerste beurt onthoudt, werkt de vervolgvraag gewoon. De SystemMessage bepaalt de persona van de assistent en wordt bij elke aanvraag meegestuurd. Wanneer je een vers gesprek wilt, roep je Bot.ClearHistory aan; om te inspecteren of te bewaren wat er gezegd is, geeft Bot.GetHistory de berichtenlijst terug. Je kunt het geheugen ook begrenzen met MaxHistoryMessages zodat een lang gesprek niet onbeperkt groeit (oudere beurten worden automatisch gesnoeid).

Hetzelfde component praat met elke provider die sgcWebSockets ondersteunt. Zet Provider op aicpAnthropic, aicpGemini, aicpDeepSeek, aicpOllama, aicpGrok of aicpMistral, wijzig de modelnaam, en de rest van je chatbotcode blijft identiek. Zie de pagina van het ChatBot-component en de hub met AI & LLM-componenten.

Een live antwoord streamen

Enkele seconden wachten tot een volledig antwoord verschijnt voelt traag. Echte chatbots streamen het antwoord zodat woorden verschijnen terwijl ze worden gegenereerd, het vertrouwde typ-effect. TsgcAIChat stelt dit beschikbaar via ChatStream plus de OnChatStream-gebeurtenis, die voor elk tekstfragment afvuurt zodra het binnenkomt.

Bot.OnChatStream := BotChatStream;
Bot.ChatStream('Explain WebSockets in two sentences.');

procedure TForm1.BotChatStream(Sender: TObject; const aChunk: string;
  var Cancel: Boolean);
begin
  Memo1.Text := Memo1.Text + aChunk;  // append each token as it arrives
  // set Cancel := True to stop the response early
end;

De fragmenten worden onder de motorkap incrementeel geleverd via Server-Sent Events, maar je raakt het SSE-leidingwerk nooit aan. Wanneer de stream voltooid is, wordt het volledige assistent-antwoord aan de geschiedenis toegevoegd, net als bij een niet-gestreamde aanroep, zodat de volgende beurt nog steeds de volledige context heeft. De Cancel-parameter laat je een "stoppen met genereren"-knop implementeren. Er is ook OnChatMessage voor het uiteindelijke samengestelde bericht en OnChatError om eventuele API-fouten naar voren te brengen.

Een spraakchatbot, van begin tot eind

Als je wilt dat de assistent luistert en praat, omvat het TsgcAIOpenAIChatBot-component de hele lus: het neemt microfoonaudio op, transcribeert die met Whisper, stuurt de tekst naar Chat Completions, en spreekt het antwoord terug via een text-to-speech-provider. Plug een audiorecorder en een text-to-speech-engine in, stel de sleutel in, en roep Start aan.

uses
  sgcAI, sgcAI_OpenAI_Audio_ChatBot,
  sgcAI_AudioRecorder_MCI, sgcAI_TextToSpeech_System;

var
  ChatBot: TsgcAIOpenAIChatBot;
begin
  ChatBot := TsgcAIOpenAIChatBot.Create(nil);
  ChatBot.OpenAIOptions.ApiKey := 'sk-...';
  ChatBot.AudioRecorder := TsgcAudioRecorderMCI.Create(nil);
  ChatBot.TextToSpeech  := TsgcTextToSpeechSystem.Create(nil);
  ChatBot.OnChatCompletion := ChatBotChatCompletion;

  ChatBot.Start;                          // begin listening; Stop ends it
  ChatBot.ChatAsUser('Tell me a joke');   // or push a turn programmatically
end;

De OnChatCompletion-gebeurtenis geeft je de rol en inhoud van elk antwoord, en OnTranscription laat je inspecteren of bewerken wat er gehoord werd voordat het wordt verstuurd. Het is hetzelfde conversationele idee als TsgcAIChat, alleen met audio aan beide kanten.

Liever de lijst zelf beheren?

Je hoeft het chatbotcomponent niet te gebruiken. Als je volledige controle wilt, houd je je eigen lijst van { role, content }-berichten bij en stuur je die bij elke aanroep mee met TsgcHTTP_API_OpenAI._CreateChatCompletion. Voeg het gebruikersbericht toe, stuur de array, en voeg dan het antwoord van de assistent terug aan dezelfde lijst toe vóór de volgende beurt. Dat is precies de boekhouding die TsgcAIChat intern doet, dus de meeste mensen laten het component het regelen. De lager gelegen API komt aan bod in de tutorial OpenAI API in Delphi en op de pagina van het OpenAI-component.

Aan de slag

Alles hier zit in sgcWebSockets. Pak de gratis proefversie, plaats een TsgcAIChat op een formulier, stel de API-sleutel en het model in, en je hebt een contextbewuste chatbot die vervolgvragen beantwoordt in een paar regels. Voeg ChatStream toe voor het live typ-effect wanneer je er klaar voor bent.

Vragen, feedback of hulp bij het inbouwen in je app? Neem contact op — je krijgt antwoord van de mensen die de code geschreven hebben.

Gerelateerd