Einem Sprachmodell eine Frage zu stellen ist der einfache Teil. Ein Agent geht weiter. Das Modell entscheidet, welche Ihrer Funktionen es aufruft, Ihre Anwendung führt sie aus, die Ergebnisse gehen an das Modell zurück, und die Schleife läuft weiter, bis das Modell eine Antwort hat. Diese Schleife zu schreiben ist nur der Anfang. Eine ausgelieferte Anwendung muss das Modell auch von allem fernhalten, was es nicht anfassen darf, personenbezogene Daten auf dem Rechner behalten, den Tag überstehen, an dem ein Anbieter ein Modell abschaltet, und wissen, was jeder Benutzer kostet.
sgcWebSockets 2026.11 fügt den sgcAI-Komponenten für Delphi 7 bis 13 die Bausteine für all das hinzu:
TsgcAIAgentführt die Tool-Schleife bei allen sieben Anbietern vonTsgcAIChataus: OpenAI, Anthropic, Gemini, DeepSeek, Ollama, Grok und Mistral. Seine Tools sind Ihre Delphi-Methoden und die Tools Ihrer MCP-Server, und er fragt nach, bevor ein Tool Daten ändert.TsgcAIGuardrailsentscheidet, welche Tools laufen dürfen, schwärzt personenbezogene Daten vor der Anfrage und stellt sie in der Antwort wieder her und prüft strukturierte Antworten gegen ein JSON Schema.- MCP Tool Pinning blockiert ein Server-Tool, dessen Definition sich ändert, nachdem Sie ihm vertraut haben.
- Modellaliase und Anbieter-Fallback sorgen dafür, dass eine ausgelieferte Exe weiter antwortet, wenn ein Modell abgeschaltet wird oder ein Anbieter ausfällt.
TsgcAIUsageMeterzählt Token, bepreist sie, setzt Budgets durch und sendet jede Anfrage an OpenTelemetry.
Die Agent-Schleife in einer Komponente
Geben Sie dem Agenten einen konfigurierten TsgcAIChat, beschreiben Sie ein Tool und rufen Sie Run auf. Der Agent sendet den Prompt mit den Tool-Definitionen, führt jedes Tool aus, das das Modell anfordert, schickt die Ergebnisse zurück und wiederholt das, bis das Modell in Klartext antwortet. Run liefert diese Antwort.
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?'));
Das Tool erhält seine Argumente als das JSON-Objekt, das das Modell geschrieben hat, und liefert sein Ergebnis als Text zurück, meist als 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;
Der Agent arbeitet auf TsgcAIChat.ChatWithTools auf, das jetzt Tool Calling bei jedem Anbieter unterstützt. Der Wechsel von einem gehosteten Modell zu einem lokalen Ollama-Modell ist eine Änderung von Provider und Model, die Tools bleiben gleich. Wenn Sie die Schleife lieber selbst ausführen, sind ChatWithTools und AddToolResult ebenfalls öffentlich.
Die Schleife steuern
Options.MaxSteps begrenzt die Anzahl der Modellanfragen eines Laufs, Cancel stoppt einen Lauf über eine Schaltfläche, einen Timer oder einen anderen Thread, und StopReason sagt Ihnen, warum der Lauf endete. Wird eine Antwort durch das Token-Limit abgeschnitten, endet der Lauf mit aasrMaxTokens, und die Tool-Aufrufe dieser Antwort werden nicht ausgeführt, weil ihre Argumente unvollständig sein können. OnStep meldet jeden Schritt, während er geschieht, sodass der Benutzer sehen kann, was der Agent tut.
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;
Ein Tool, das fehlschlägt, beendet den Lauf nicht. Seine Exception geht als Fehlertext an das Modell zurück, und das Modell kann etwas anderes versuchen. Ein Fehler des Chats selbst, zum Beispiel ein Netzwerkausfall, beendet den Lauf mit aasrError, und die Exception erreicht Ihren Code. OnUsage meldet die Token jeder Modellanfrage des Laufs.
Ihre Delphi-Methoden als Tools
Ein Tool ist ein Name, eine Beschreibung, typisierte Parameter und ein Handler, wie oben. Wenn Sie bereits eine Methode haben, die die Aufgabe erledigt, veröffentlicht AddMethod sie unverändert. Der Tool-Name ist der Methodenname, und die Parameter, ihre Typen und das Ergebnis werden mit erweiterter RTTI gelesen, deshalb braucht dieser Weg Delphi 2010 oder neuer.
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');
Für ein Tool mit komplexerer Eingabe setzen Sie InputSchema auf Ihr eigenes JSON Schema. Enabled nimmt ein Tool aus den nächsten Läufen heraus, ohne es zu entfernen.
Tools aus Ihren MCP-Servern
Fügen Sie MCPClients einen oder mehrere MCP-Clients hinzu, und der Agent bietet die Tools dieser Server neben Ihren eigenen an. Sie werden einmal pro Lauf aufgelistet und über den Client aufgerufen, wenn das Modell sie anfordert. Ein Tool, das der Server mit readOnlyHint markiert, gilt als schreibgeschützt, und wenn ein lokales Tool und ein Server-Tool denselben Namen haben, gewinnt das 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'));
Freigabe, bevor sich etwas ändert
Jedes Tool hat ein Flag ReadOnly. Schreibgeschützte Tools laufen, sobald das Modell sie anfordert. Ein Tool, das Daten ändert, eine Mail sendet oder Geld ausgibt, bleibt nicht schreibgeschützt, und der Agent fragt OnToolApproval, bevor es läuft, mit dem Tool-Namen und den genauen Argumenten, die das Modell gewählt hat.
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;
Ein abgelehntes Tool ist kein Fehler. Das Modell erfährt, dass das Tool nicht freigegeben wurde, und kann dem Benutzer ohne es antworten. Wenn Guardrails zugewiesen sind, wie im nächsten Abschnitt, wird ein Tool, das eine Freigabe braucht und keinen Handler findet, abgelehnt, sodass ein vergessenes Ereignis nie ein schreibendes Tool durchlässt.
Guardrails
TsgcAIGuardrails ist eine eigene Komponente, die Sie dem Chat zuweisen, sodass dieselben Regeln für den Agenten und für einfache Chat-Anfragen gelten. Sie hat drei Aufgaben.
- Tool-Regeln.
AllowedToolsundDeniedToolsnehmen Namen mit*-Platzhaltern, und ein Verbot gewinnt immer. Ein nicht erlaubtes Tool wird dem Modell nie angeboten.ToolApprovalwählt, welche Tools eine Freigabe brauchen: keine, die nicht schreibgeschützten oder alle. - Personenbezogene Daten. Mit
PII.Enabledwerden E-Mail-Adressen, Telefonnummern, Kartennummern, IBANs, IP-Adressen und Ihre eigenen Muster durch Platzhalter ersetzt, bevor die Anfrage die Anwendung verlässt, und die Originale werden in der Antwort wieder eingesetzt. - Antwortschema. Mit
Schema.Enabledwird eine strukturierte Antwort, die nicht zum JSON Schema passt, mit dem Pfad des Fehlers abgewiesen, zum Beispiel$.price: expected number, undOnSchemaViolationinformiert Sie darüber.
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;
Sie können die Schwärzung selbst aufrufen, um zu sehen, was der Anbieter erhält und was Ihre Anwendung zurückbekommt:
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
Die Tools Ihrer MCP-Server anheften
Die Beschreibung und das Eingabeschema eines MCP-Tools liest das Modell als Anweisungen. Ein Server, der sie ändert, nachdem Sie ihm vertraut haben, weil er kompromittiert wurde oder weil sein Betreiber sie geändert hat, kann neue Anweisungen in einem Tool verstecken, das Sie bereits nutzen. Das ist als Tool Poisoning bekannt.
Mit Tool Pinning speichert TsgcWSAPIClient_MCP beim ersten Auflisten einen SHA-256-Fingerabdruck von Name, Beschreibung, Eingabeschema und Annotationen jedes Tools. Liefert eine spätere Liste eine andere Definition, wird das Tool blockiert. Es wird aus der Liste entfernt, der Agent bietet es nie an, und ein Aufruf löst eine Exception aus, bevor etwas gesendet wird. Mit OnMCPToolChanged können Sie die Änderung akzeptieren oder das Tool zur Prüfung blockiert lassen, und die anderen Tools des Servers arbeiten weiter.
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;
Ein abgeschaltetes Modell stoppt keine ausgelieferte Exe
Modellnamen ändern sich schneller als installierte Anwendungen. Ein in eine Exe einkompilierter Modellname funktioniert nicht mehr, sobald der Anbieter das Modell abschaltet, und jeder Kunde braucht einen neuen Build. Mit Modellaliasen verlangt der Code smart oder fast, und der Alias bestimmt das Modell und, wenn Sie möchten, den Anbieter. Die Liste lässt sich aus einer Datei, aus JSON oder von einer URL laden, der Wechsel zu einem neuen Modell ist also eine Änderung auf Ihrem Server und kein neues 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 decken Ausfälle ab. Wenn der Anbieter mit einem Verbindungsfehler, einem HTTP 5xx, einem HTTP 429 oder einem ihm unbekannten Modell scheitert, geht dieselbe Anfrage an den nächsten Anbieter und das nächste Modell der Liste. Gesprächsverlauf, Tools und JSON-Antworten werden übernommen, und OnFallback meldet, dass es passiert ist. Ein lokales Ollama-Modell ist eine gute letzte Rückfallebene.
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;
Wissen, was jede Anfrage kostet
TsgcAIUsageMeter erfasst die Token jeder Anfrage, einschließlich der zwischengespeicherten Eingabe-Token, bepreist sie mit der Preisliste, die Sie ihm geben, und führt Summen pro Benutzer und pro Sitzung. Weisen Sie ihn dem Chat zu und setzen Sie UserId und SessionId. Weil der Usage Meter am Chat hängt, wird auch jede Modellanfrage eines Agentenlaufs gezählt.
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')]));
Die Preisliste ordnet Anbieter und Modell mit Platzhaltern zu und lässt sich aus JSON oder aus einer Datei laden, eine Preisänderung braucht also ebenfalls keinen neuen Build. Budgets machen aus dem Usage Meter ein Limit. Ein Budget gilt für die ganze Anwendung, für jeden Benutzer oder für jede Sitzung, für einen Tag, einen Monat oder insgesamt, in Geld oder in Token. Ist ein Budget aufgebraucht, wird die nächste Anfrage abgewiesen, bevor sie gesendet wird, sodass ein Budget auch einen Agentenlauf mittendrin stoppen kann.
// 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;
Setzen Sie BlockWhenExceeded auf False für ein Budget, das nur OnBudgetExceeded als Warnung auslöst. Für Berichte gehen die Datensätze in eine CSV-Datei oder in ein beliebiges TDataSet. Mit aktiviertem OpenTelemetry wird jede Anfrage zu einem Span nach den GenAI Semantic Conventions und geht im Hintergrund an Ihren OTLP/HTTP-Collector, ohne den Chat zu bremsen.
oMeter.OpenTelemetry.Endpoint := 'http://localhost:4318';
oMeter.OpenTelemetry.ServiceName := 'invoice-assistant';
oMeter.OpenTelemetry.Enabled := True;
oMeter.SaveToCSV('usage.csv');
Die Demo: ein Rechnungsassistent
Die neue Delphi-Demo in Demos\15.AI\02.Applications\11.AIAgent ist ein Assistent für eine Liste unbezahlter Rechnungen. Der Agent hat drei Tools. list_unpaid_invoices und get_invoice sind schreibgeschützt, während send_reminder Daten ändert und zuerst Ihre Freigabe erfragt. Der Agent läuft in einem Hintergrund-Thread, sodass das Fenster während der Arbeit reaktionsfähig bleibt. Die Demo lässt Sie auch den Rest dieses Beitrags ausprobieren: verbotene Tools und PII-Schwärzung, ein Budget pro Sitzung mit einem Live-Zähler für Token und Kosten und einem CSV-Export, Modellaliase, einen Fallback-Anbieter und einen MCP-Server, dessen Tools angeheftet sind.
Dokumentation, die Ihr Coding-Agent lesen kann
Ein Coding-Agent schreibt nur für die Bibliotheken guten Code, die er kennt. Jede Komponente von sgcAI, sgcHTML, sgcCrypto und den übrigen sgcWebSockets-Packs hat jetzt eine eigene Markdown-Seite für Sprachmodelle, insgesamt 224 Seiten. Jede Seite beschreibt, was die Komponente tut, und verlinkt ihre Produktseite, die meisten führen außerdem die wichtigsten Eigenschaften, Methoden und Ereignisse auf. Die Seite von TsgcAIAgent, ai-agent.md, enthält zusätzlich ein vollständiges Anwendungsbeispiel. llms.txt verweist Claude Code, Copilot, Cursor und andere Agenten auf diese Seiten, damit der Agent die echte API liest, bevor er eine Zeile schreibt.
Auch die Agenten-Skills von sgcWebSockets lernen die neuen Komponenten kennen. Der KI-Skill von 2026.11.0 enthält fertige Muster für die Agent-Schleife, die Tool-Freigabe, Guardrails, Modellaliase mit Fallback und den UsageMeter, und jedes Muster stammt aus Code, der kompiliert. KI-Agenten-Skills für Delphi, C++Builder und .NET erklärt die Installation.
Verfügbarkeit
TsgcAIAgent, TsgcAIGuardrails, TsgcAIUsageMeter, die Modellaliase und Fallbacks von TsgcAIChat und MCP Tool Pinning erscheinen in sgcWebSockets 2026.11.0, für Delphi 7 bis 13 und C++ Builder. Sie gehören zu den sgcAI-Komponenten, die in den Editionen Enterprise und All-Access und im eigenständigen Produkt sgcAI enthalten sind. Die Hilfe dokumentiert jede Eigenschaft, Methode und jedes Ereignis, mit Seiten zur Agent-Schleife, zu Tools, zu Aliasen und Fallbacks, zur PII-Schwärzung, zur Schemavalidierung und zum Tool Pinning.
Mehr dazu auf den Komponentenseiten von TsgcAIAgent, TsgcAIGuardrails und TsgcAIUsageMeter.
Als Nächstes lesen
- Build an AI Agent in Delphi: Function Calling and MCP Tool Use, die Agent-Schleife Schritt für Schritt erklärt
- Delphi MCP Client
- OpenAI vs Anthropic vs Gemini in Delphi: Which AI API Should You Use?
Fragen oder Feedback? Nehmen Sie Kontakt auf. Sie erhalten eine Antwort von den Menschen, die den Code geschrieben haben.
