Een taalmodel een vraag stellen is het makkelijke deel. Een agent gaat verder. Het model bepaalt welke van je functies het aanroept, je toepassing voert ze uit, de resultaten gaan terug naar het model en de loop gaat door totdat het model een antwoord heeft. Die loop schrijven is pas het begin. Een toepassing in productie moet het model ook weghouden van wat het niet mag aanraken, persoonsgegevens op de machine houden, de dag overleven waarop een provider een model buiten gebruik stelt, en weten wat elke gebruiker kost.
sgcWebSockets 2026.11 voegt de onderdelen daarvoor toe aan de sgcAI-componenten, voor Delphi 7 tot 13:
TsgcAIAgentdraait de toolloop op alle zeven providers vanTsgcAIChat: OpenAI, Anthropic, Gemini, DeepSeek, Ollama, Grok en Mistral. De tools zijn je Delphi-methoden en de tools van je MCP-servers, en het vraagt toestemming voordat een tool gegevens wijzigt.TsgcAIGuardrailsbepaalt welke tools mogen draaien, maskeert persoonsgegevens vóór het verzoek en herstelt ze in het antwoord, en controleert gestructureerde antwoorden tegen een JSON-schema.- MCP tool pinning blokkeert een servertool waarvan de definitie verandert nadat je hem had vertrouwd.
- Modelaliassen en provider-fallback zorgen dat een uitgeleverde exe blijft antwoorden als een model buiten gebruik wordt gesteld of een provider uitvalt.
TsgcAIUsageMetertelt tokens, prijst ze, hanteert budgetten en stuurt elk verzoek naar OpenTelemetry.
De agentloop in één component
Geef de agent een geconfigureerde TsgcAIChat, beschrijf een tool en roep Run aan. De agent stuurt de prompt met de tooldefinities mee, voert elke tool uit die het model vraagt, stuurt de resultaten terug en herhaalt dit totdat het model in gewone tekst antwoordt. Run geeft dat antwoord terug.
oChat := TsgcAIChat.Create(nil);
oChat.Provider := aicpAnthropic;
oChat.ChatOptions.ApiKey := 'API_KEY';
oChat.ChatOptions.Model := 'claude-opus-5-5';
oAgent := TsgcAIAgent.Create(nil);
oAgent.Chat := oChat;
oTool := oAgent.Tools.Add('get_weather', 'Current weather of a city');
oTool.AddParam('city', aatpString, 'City name');
oTool.ReadOnly := True;
oTool.OnExecute := DoGetWeather;
Memo1.Lines.Add(oAgent.Run('Do I need an umbrella in Paris today?'));
De tool ontvangt zijn argumenten als het JSON-object dat het model heeft geschreven, en geeft zijn resultaat terug als tekst, meestal JSON:
procedure TForm1.DoGetWeather(Sender: TObject; const aArguments: string;
var aResult: string);
begin
// aArguments is a JSON object, for example {"city":"Paris"}
aResult := '{"city":"Paris","celsius":18,"sky":"rain"}';
end;
De agent werkt bovenop TsgcAIChat.ChatWithTools, dat nu tool calling ondersteunt op elke provider. Van een gehost model overstappen op een lokaal Ollama-model is een wijziging van Provider en Model, en de tools blijven dezelfde. Wil je de loop liever zelf draaien, dan zijn ChatWithTools en AddToolResult ook publiek.
Houd de loop onder controle
Options.MaxSteps begrenst het aantal modelverzoeken in één run, Cancel stopt een run vanaf een knop, een timer of een andere thread, en StopReason vertelt waarom de run eindigde. Wordt een antwoord door de tokenlimiet afgekapt, dan stopt de run met aasrMaxTokens en worden de toolaanroepen van dat antwoord niet uitgevoerd, omdat hun argumenten onvolledig kunnen zijn. OnStep meldt elke stap terwijl die gebeurt, zodat de gebruiker kan zien wat de agent doet.
oAgent.Options.MaxSteps := 5;
oAgent.OnStep := oAgentStep;
vAnswer := oAgent.Run('Plan a three day trip to Rome');
case oAgent.StopReason of
aasrCompleted:
Memo1.Lines.Add(vAnswer);
aasrMaxSteps:
Memo1.Lines.Add('Stopped after 5 model requests');
aasrMaxTokens:
Memo1.Lines.Add('Cut by the token limit (' + oChat.LastFinishReason + ')');
aasrCancelled:
Memo1.Lines.Add('Cancelled');
end;
procedure TForm1.oAgentStep(Sender: TObject; const aStep: TsgcAIAgentStep);
begin
case aStep.Kind of
aaskToolCall:
Memo1.Lines.Add('Calling ' + aStep.ToolName + ' ' + aStep.Arguments);
aaskToolResult, aaskToolDenied:
Memo1.Lines.Add(aStep.ToolName + ': ' + aStep.Content);
aaskFinal:
Memo1.Lines.Add('Answer: ' + aStep.Content);
end;
end;
Een tool die mislukt, beëindigt de run niet. Zijn exception gaat als foutmelding terug naar het model, dat iets anders kan proberen. Een fout van de chat zelf, bijvoorbeeld een netwerkstoring, beëindigt de run met aasrError en de exception bereikt je code. OnUsage meldt de tokens van elk modelverzoek van de run.
Je Delphi-methoden als tools
Een tool is een naam, een beschrijving, getypeerde parameters en een handler, zoals hierboven. Heb je al een methode die het werk doet, dan publiceert AddMethod die zoals ze is. De toolnaam is de methodenaam, en de parameters, hun typen en het resultaat worden gelezen met extended RTTI, dus deze vereist Delphi 2010 of nieuwer.
function TForm1.Forecast(const aCity: string; aDays: Integer): string;
begin
Result := Format('%s: sunny for %d days', [aCity, aDays]);
end;
oAgent.Tools.AddMethod(Self, 'Forecast',
'Weather forecast of a city for the next days');
Voor een tool met een rijkere invoer stel je InputSchema in op je eigen JSON-schema. Enabled haalt een tool uit de volgende runs zonder hem te verwijderen.
Tools van je MCP-servers
Voeg een of meer MCP-clients toe aan MCPClients en de agent biedt de tools van die servers aan naast je eigen tools. Ze worden één keer per run opgevraagd en via de client aangeroepen wanneer het model erom vraagt. Een tool die de server markeert met readOnlyHint telt als alleen-lezen, en als een lokale tool en een servertool dezelfde naam hebben, wint de lokale.
oMCP := TsgcWSAPIClient_MCP.Create(nil);
oMCP.MCPOptions.HttpOptions.URL := 'http://127.0.0.1:8080/mcp';
oAgent.MCPClients.Add(oMCP);
Memo1.Lines.Add(oAgent.Run('List the support tickets opened today'));
Goedkeuring voordat er iets verandert
Elke tool heeft een ReadOnly-vlag. Alleen-lezen tools worden uitgevoerd zodra het model erom vraagt. Een tool die gegevens wijzigt, een mail verstuurt of geld uitgeeft, blijft niet alleen-lezen, en de agent vraagt OnToolApproval voordat hij draait, met de toolnaam en de exacte argumenten die het model koos.
oTool := oAgent.Tools.Add('send_reminder', 'Sends a payment reminder');
oTool.AddParam('invoice', aatpString, 'Invoice number');
oTool.OnExecute := DoSendReminder; // ReadOnly is False: it changes data
oAgent.OnToolApproval := oAgentToolApproval;
procedure TForm1.oAgentToolApproval(Sender: TObject; const aToolName,
aArguments: string; var aApproved: Boolean);
begin
aApproved := MessageDlg('Run ' + aToolName + ' with ' + aArguments + '?',
mtConfirmation, [mbYes, mbNo], 0) = mrYes;
end;
Een geweigerde tool is geen fout. Het model hoort dat de tool niet is goedgekeurd en kan de gebruiker antwoorden zonder die tool. Met guardrails gekoppeld, zoals in de volgende sectie, wordt een tool die goedkeuring nodig heeft maar geen handler vindt geweigerd, zodat een vergeten event nooit een schrijvende tool doorlaat.
Guardrails
TsgcAIGuardrails is een apart component dat je aan de chat koppelt, zodat dezelfde regels gelden voor de agent en voor gewone chatverzoeken. Het heeft drie taken.
- Toolregels.
AllowedToolsenDeniedToolsnemen namen met*-jokertekens, en een weigering wint altijd. Een tool die niet is toegestaan, wordt nooit aan het model aangeboden.ToolApprovalkiest welke tools goedkeuring nodig hebben: geen, de tools die niet alleen-lezen zijn, of alle. - Persoonsgegevens. Met
PII.Enabledworden e-mailadressen, telefoonnummers, kaartnummers, IBAN's, IP-adressen en je eigen patronen vervangen door plaatshouders voordat het verzoek de toepassing verlaat, en worden de originelen in het antwoord teruggezet. - Antwoordschema. Met
Schema.Enabledwordt een gestructureerd antwoord dat niet bij het JSON-schema past afgewezen met het pad van de fout, bijvoorbeeld$.price: expected number, enOnSchemaViolationmeldt het je.
oGuard := TsgcAIGuardrails.Create(nil);
// tools: reading is allowed, deleting never
oGuard.AllowedTools.Add('get_*');
oGuard.AllowedTools.Add('list_*');
oGuard.AllowedTools.Add('send_reminder');
oGuard.DeniedTools.Add('*delete*');
oGuard.ToolApproval := aigaNotReadOnly;
// personal data never leaves the application
oGuard.PII.Enabled := True;
oGuard.PII.CustomPatterns.Add('EmployeeId=EMP-####');
// structured answers must match the schema
oGuard.Schema.Enabled := True;
oGuard.Schema.JSONSchema := '{"type":"object","properties":' +
'{"price":{"type":"number","minimum":0}},"required":["price"]}';
oChat.Guardrails := oGuard;
Je kunt de maskering zelf aanroepen om te zien wat de provider ontvangt en wat je toepassing terugkrijgt:
vText := oGuard.Redact('Mail bob@example.com, call +34 600 111 222, ' +
'employee EMP-1234');
// Mail [EMAIL_1], call [PHONE_1], employee [EMPLOYEEID_1]
vText := oGuard.Restore(vText);
// Mail bob@example.com, call +34 600 111 222, employee EMP-1234
Pin de tools van je MCP-servers
De beschrijving en het invoerschema van een MCP-tool worden door het model gelezen als instructies. Een server die ze wijzigt nadat je hem vertrouwde, omdat hij gecompromitteerd is of omdat de eigenaar hem heeft aangepast, kan nieuwe instructies verbergen in een tool die je al gebruikt. Dit staat bekend als tool poisoning.
Met tool pinning bewaart TsgcWSAPIClient_MCP een SHA-256-vingerafdruk van naam, beschrijving, invoerschema en annotaties van elke tool de eerste keer dat hij die opvraagt. Geeft een latere lijst een andere definitie terug, dan wordt de tool geblokkeerd. Hij wordt uit de lijst gehaald, de agent biedt hem nooit aan, en een aanroep ervan geeft een exception voordat er iets wordt verstuurd. Met OnMCPToolChanged kun je de wijziging accepteren of de tool geblokkeerd houden voor controle, en de andere tools van de server blijven werken.
oMCP.MCPOptions.ToolPinning.Enabled := True;
oMCP.MCPOptions.ToolPinning.FileName := 'mcp_pins.json';
oMCP.OnMCPToolChanged := oMCPMCPToolChanged;
procedure TForm1.oMCPMCPToolChanged(Sender: TObject; const aToolName,
aOldHash, aNewHash: string; var aAccept: Boolean);
begin
// the definition of a trusted tool changed: keep it blocked
// until somebody reviews it, then call oMCP.PinTool(aToolName)
Memo1.Lines.Add('Tool ' + aToolName + ' changed, blocked for review');
aAccept := False;
end;
Een buiten gebruik gesteld model stopt een uitgeleverde exe niet
Modelnamen veranderen sneller dan geïnstalleerde toepassingen. Een modelnaam die in een exe is gecompileerd, werkt niet meer op de dag dat de provider het model buiten gebruik stelt, en elke klant heeft een nieuwe build nodig. Met modelaliassen vraagt de code om smart of fast, en de alias bepaalt het model en, als je wilt, de provider. De lijst wordt geladen uit een bestand, uit JSON of van een URL, dus overstappen op een nieuw model is een wijziging op je server, geen nieuwe release.
oChat.ModelAliases.Add('fast=gpt-6-luna');
oChat.ModelAliases.Add('smart=anthropic:claude-opus-5-5');
oChat.ProviderApiKeys.Add('anthropic=ANTHROPIC_KEY');
oChat.ChatOptions.Model := 'smart';
// or keep the list on your server and change it without a new build
oChat.LoadModelAliasesFromURL('https://config.example.com/ai/models.json');
Fallbacks vangen storingen op. Als de provider faalt met een verbindingsfout, een HTTP 5xx, een HTTP 429 of een model dat hij niet kent, gaat hetzelfde verzoek naar de volgende provider en het volgende model van de lijst. De gespreksgeschiedenis, de tools en JSON-antwoorden gaan mee, en OnFallback meldt dat het gebeurde. Een lokaal Ollama-model is een goed laatste redmiddel.
oChat.Provider := aicpOpenAI;
oChat.ChatOptions.ApiKey := 'OPENAI_KEY';
oChat.ChatOptions.Model := 'gpt-6-luna';
oFallback := oChat.Fallbacks.Add;
oFallback.Provider := aicpGemini;
oFallback.Model := 'gemini-3-flash';
oFallback.ApiKey := 'GEMINI_KEY';
oFallback := oChat.Fallbacks.Add;
oFallback.Provider := aicpOllama;
oFallback.Model := 'llama3.1';
oChat.OnFallback := oChatFallback;
procedure TForm1.oChatFallback(Sender: TObject; const aFromProvider,
aFromModel, aToProvider, aToModel, aError: string);
begin
Memo1.Lines.Add(aFromProvider + ' ' + aFromModel + ' failed (' + aError +
'), trying ' + aToProvider + ' ' + aToModel);
end;
Weet wat elk verzoek kost
TsgcAIUsageMeter registreert de tokens van elk verzoek, inclusief gecachete invoertokens, prijst ze met de prijslijst die je opgeeft en houdt totalen per gebruiker en per sessie bij. Koppel het aan de chat en stel UserId en SessionId in. Omdat de meter op de chat zit, wordt ook elk modelverzoek van een agentrun geteld.
oMeter := TsgcAIUsageMeter.Create(nil);
// example prices per million tokens, use the ones of your contract
oPrice := oMeter.Prices.Add;
oPrice.Provider := 'openai';
oPrice.Model := 'gpt-6*';
oPrice.InputPerMillion := 1.25;
oPrice.OutputPerMillion := 10;
oPrice.CachedPerMillion := 0.125;
oChat.UserId := 'user-1';
oChat.SessionId := 's-42';
oChat.UsageMeter := oMeter;
oChat.Chat('Hello');
Memo1.Lines.Add(Format('user-1 spent %.6f %s and %d tokens',
[oMeter.TotalCost('user-1'), oMeter.Currency, oMeter.TotalTokens('user-1')]));
De prijslijst koppelt provider en model met jokertekens en kan uit JSON of uit een bestand worden geladen, dus ook een prijswijziging vraagt geen nieuwe build. Budgetten maken van de meter een limiet. Een budget geldt voor de hele toepassing, voor elke gebruiker of voor elke sessie, voor een dag, een maand of in totaal, in geld of in tokens. Als een budget op is, wordt het volgende verzoek geweigerd voordat het wordt verstuurd, zodat een budget ook een agentrun halverwege kan stoppen.
// every user can spend 2 USD a day
oBudget := oMeter.Budgets.Add;
oBudget.Scope := aubsUser;
oBudget.Period := aubpDay;
oBudget.MaxCost := 2;
// the whole application can spend 500 USD a month
oBudget := oMeter.Budgets.Add;
oBudget.Scope := aubsGlobal;
oBudget.Period := aubpMonth;
oBudget.MaxCost := 500;
try
Memo1.Lines.Add(oAgent.Run('Remind every customer with an unpaid invoice'));
except
on E: EsgcAIChat do
// Usage budget "each user, day" for user-1 exceeded: ...
Memo1.Lines.Add(E.Message);
end;
Zet BlockWhenExceeded op False voor een budget dat alleen OnBudgetExceeded afvuurt als waarschuwing. Voor rapporten gaan de records naar een CSV-bestand of naar elke TDataSet. Met OpenTelemetry ingeschakeld wordt elk verzoek een span die de GenAI semantic conventions volgt en op de achtergrond naar je OTLP/HTTP-collector gaat, zonder de chat te vertragen.
oMeter.OpenTelemetry.Endpoint := 'http://localhost:4318';
oMeter.OpenTelemetry.ServiceName := 'invoice-assistant';
oMeter.OpenTelemetry.Enabled := True;
oMeter.SaveToCSV('usage.csv');
De demo: een factuurassistent
De nieuwe Delphi-demo in Demos\15.AI\02.Applications\11.AIAgent is een assistent voor een lijst met onbetaalde facturen. De agent heeft drie tools. list_unpaid_invoices en get_invoice zijn alleen-lezen, terwijl send_reminder gegevens wijzigt en eerst om je goedkeuring vraagt. De agent draait in een achtergrondthread, zodat het venster responsief blijft terwijl hij werkt. Met de demo kun je ook de rest van dit artikel uitproberen: geweigerde tools en PII-maskering, een budget per sessie met een live token- en kostenteller en een CSV-export, modelaliassen, een fallback-provider en een MCP-server waarvan de tools zijn vastgepind.
Documentatie die je codeeragent kan lezen
Een codeeragent schrijft alleen goede code voor de bibliotheken die hij kent. Elk component van sgcAI, sgcHTML, sgcCrypto en de andere packs van sgcWebSockets heeft nu een eigen Markdown-pagina voor taalmodellen, 224 pagina's in totaal. Elke pagina vertelt wat het component doet en verwijst naar de productpagina, en de meeste noemen de belangrijkste properties, methoden en events. De pagina van TsgcAIAgent, ai-agent.md, bevat ook een volledig gebruiksvoorbeeld. llms.txt wijst Claude Code, Copilot, Cursor en andere agents naar deze pagina's, zodat de agent de echte API leest voordat hij een regel schrijft.
De agentskills van sgcWebSockets leren de nieuwe componenten ook kennen. De AI-skill van 2026.11.0 heeft kant-en-klare patronen voor de agentloop, toolgoedkeuring, guardrails, modelaliassen met fallback en de usage meter, en elk patroon komt uit code die compileert. AI-agentskills voor Delphi, C++Builder en .NET legt uit hoe je ze installeert.
Beschikbaarheid
TsgcAIAgent, TsgcAIGuardrails, TsgcAIUsageMeter, de modelaliassen en fallbacks van TsgcAIChat en MCP tool pinning komen in sgcWebSockets 2026.11.0, voor Delphi 7 tot 13 en C++Builder. Ze maken deel uit van de sgcAI-componenten, inbegrepen in de edities Enterprise en All-Access en in het standalone product sgcAI. De help documenteert elke eigenschap, methode en gebeurtenis, met pagina's over de agentloop, over tools, over aliassen en fallbacks, over PII-maskering, over schemavalidatie en over tool pinning.
Lees meer op de componentpagina's van TsgcAIAgent, TsgcAIGuardrails en TsgcAIUsageMeter.
Lees verder
- Bouw een AI-agent in Delphi: function calling en MCP-tool use, de agentloop stap voor stap uitgelegd
- Delphi MCP-client
- OpenAI vs Anthropic vs Gemini in Delphi: Welke AI-API moet je gebruiken?
Vragen of feedback? Neem contact op. Je krijgt een antwoord van de mensen die de code hebben geschreven.
