Poser une question à un modèle de langage, c'est la partie facile. Un agent va plus loin. Le modèle décide laquelle de vos fonctions appeler, votre application les exécute, les résultats retournent au modèle, et la boucle continue jusqu'à ce que le modèle ait une réponse. Écrire cette boucle n'est que le début. Une application livrée doit aussi tenir le modèle à l'écart de ce qu'il ne doit pas toucher, garder les données personnelles sur la machine, survivre au jour où un fournisseur retire un modèle, et savoir ce que coûte chaque utilisateur.
sgcWebSockets 2026.11 ajoute aux composants sgcAI tout ce qu'il faut pour cela, pour Delphi 7 à 13 :
TsgcAIAgentexécute la boucle d'outils avec les sept fournisseurs deTsgcAIChat: OpenAI, Anthropic, Gemini, DeepSeek, Ollama, Grok et Mistral. Ses outils sont vos méthodes Delphi et les outils de vos serveurs MCP, et il demande une approbation avant qu'un outil ne modifie des données.TsgcAIGuardrailsdécide quels outils peuvent s'exécuter, masque les données personnelles avant la requête et les rétablit dans la réponse, et vérifie les réponses structurées par rapport à un JSON Schema.- Le pinning d'outils MCP bloque un outil de serveur dont la définition change après que vous lui avez fait confiance.
- Les alias de modèle et le fallback entre fournisseurs permettent à un exe livré de continuer à répondre quand un modèle est retiré ou qu'un fournisseur est en panne.
TsgcAIUsageMetercompte les tokens, les chiffre, impose des budgets et envoie chaque requête à OpenTelemetry.
La boucle d'agent dans un seul composant
Donnez à l'agent un TsgcAIChat configuré, décrivez un outil et appelez Run. L'agent envoie le prompt avec les définitions d'outils, exécute chaque outil demandé par le modèle, renvoie les résultats et recommence jusqu'à ce que le modèle réponde en texte simple. Run retourne cette réponse.
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?'));
L'outil reçoit ses arguments sous la forme de l'objet JSON écrit par le modèle, et retourne son résultat sous forme de texte, en général du 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;
L'agent s'appuie sur TsgcAIChat.ChatWithTools, qui prend désormais en charge le tool calling avec tous les fournisseurs. Passer d'un modèle hébergé à un modèle Ollama local revient à changer Provider et Model, et les outils restent les mêmes. Si vous préférez exécuter la boucle vous-même, ChatWithTools et AddToolResult sont publics eux aussi.
Contrôler la boucle
Options.MaxSteps plafonne le nombre de requêtes au modèle d'une exécution, Cancel arrête une exécution depuis un bouton, un timer ou un autre thread, et StopReason indique pourquoi l'exécution s'est terminée. Quand une réponse est coupée par la limite de tokens, l'exécution s'arrête avec aasrMaxTokens et les appels d'outil de cette réponse ne sont pas exécutés, car leurs arguments peuvent être incomplets. OnStep signale chaque étape au moment où elle se produit, afin que l'utilisateur voie ce que fait l'agent.
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;
Un outil qui échoue ne termine pas l'exécution. Son exception retourne au modèle sous forme de texte d'erreur, et le modèle peut essayer autre chose. Une erreur du chat lui-même, une panne réseau par exemple, termine l'exécution avec aasrError et l'exception parvient à votre code. OnUsage signale les tokens de chaque requête au modèle de l'exécution.
Vos méthodes Delphi comme outils
Un outil est un nom, une description, des paramètres typés et un gestionnaire, comme ci-dessus. Quand vous avez déjà une méthode qui fait le travail, AddMethod la publie telle quelle. Le nom de l'outil est le nom de la méthode, et les paramètres, leurs types et le résultat sont lus avec la RTTI étendue, ce qui exige Delphi 2010 ou une version plus récente.
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');
Pour un outil dont l'entrée est plus riche, définissez InputSchema avec votre propre JSON Schema. Enabled retire un outil des exécutions suivantes sans le supprimer.
Les outils de vos serveurs MCP
Ajoutez un ou plusieurs clients MCP à MCPClients et l'agent propose les outils de ces serveurs à côté des vôtres. Ils sont listés une fois par exécution et appelés via le client quand le modèle les demande. Un outil que le serveur marque avec readOnlyHint compte comme en lecture seule, et quand un outil local et un outil de serveur portent le même nom, l'outil local l'emporte.
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'));
Une approbation avant tout changement
Chaque outil possède un indicateur ReadOnly. Les outils en lecture seule s'exécutent dès que le modèle les demande. Un outil qui modifie des données, envoie un e-mail ou dépense de l'argent n'est pas en lecture seule, et l'agent interroge OnToolApproval avant de l'exécuter, avec le nom de l'outil et les arguments exacts choisis par le modèle.
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;
Un outil refusé n'est pas une erreur. Le modèle est informé que l'outil n'a pas été approuvé et peut répondre à l'utilisateur sans lui. Avec des guardrails affectés, comme dans la section suivante, un outil qui exige une approbation et ne trouve aucun gestionnaire est refusé, si bien qu'un événement oublié ne laisse jamais passer un outil d'écriture.
Guardrails
TsgcAIGuardrails est un composant distinct que vous affectez au chat, de sorte que les mêmes règles s'appliquent à l'agent et aux simples requêtes de chat. Il a trois missions.
- Règles sur les outils.
AllowedToolsetDeniedToolsacceptent des noms avec des jokers*, et un refus l'emporte toujours. Un outil qui n'est pas autorisé n'est jamais proposé au modèle.ToolApprovalchoisit quels outils exigent une approbation : aucun, ceux qui ne sont pas en lecture seule, ou tous. - Données personnelles. Avec
PII.Enabled, les adresses e-mail, numéros de téléphone, numéros de carte, IBAN, adresses IP et vos propres motifs sont remplacés par des marqueurs avant que la requête ne quitte l'application, et les originaux sont rétablis dans la réponse. - Schéma de réponse. Avec
Schema.Enabled, une réponse structurée qui ne correspond pas au JSON Schema est rejetée avec le chemin de l'erreur, par exemple$.price: expected number, etOnSchemaViolationvous en informe.
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;
Vous pouvez appeler vous-même le masquage pour voir ce que le fournisseur recevra, et ce que votre application récupère :
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
Épingler les outils de vos serveurs MCP
La description et le schéma d'entrée d'un outil MCP sont lus par le modèle comme des instructions. Un serveur qui les modifie après que vous lui avez fait confiance, parce qu'il a été compromis ou parce que son propriétaire l'a changé, peut cacher de nouvelles instructions dans un outil que vous utilisez déjà. On appelle cela le tool poisoning.
Avec le pinning d'outils, TsgcWSAPIClient_MCP conserve une empreinte SHA-256 du nom, de la description, du schéma d'entrée et des annotations de chaque outil la première fois qu'il le liste. Quand une liste ultérieure retourne une définition différente, l'outil est bloqué. Il est retiré de la liste, l'agent ne le propose jamais, et un appel à cet outil lève une exception avant que quoi que ce soit ne soit envoyé. OnMCPToolChanged vous permet d'accepter le changement ou de garder l'outil bloqué pour examen, et les autres outils du serveur continuent de fonctionner.
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;
Un modèle retiré n'arrêtera pas un exe livré
Les noms de modèles changent plus vite que les applications installées. Un nom de modèle compilé dans un exe cesse de fonctionner le jour où le fournisseur le retire, et chaque client a besoin d'une nouvelle build. Avec les alias de modèle, le code demande smart ou fast, et l'alias détermine le modèle et, si vous le souhaitez, le fournisseur. La liste se charge depuis un fichier, du JSON ou une URL, de sorte que passer à un nouveau modèle est un changement sur votre serveur, pas une nouvelle version.
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');
Les fallbacks couvrent les pannes. Quand le fournisseur échoue avec une erreur de connexion, un HTTP 5xx, un HTTP 429 ou un modèle qu'il ne connaît pas, la même requête passe au fournisseur et au modèle suivants de la liste. L'historique de la conversation, les outils et les réponses JSON sont conservés, et OnFallback vous signale la bascule. Un modèle Ollama local constitue un bon dernier recours.
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;
Savoir ce que coûte chaque requête
TsgcAIUsageMeter enregistre les tokens de chaque requête, tokens d'entrée en cache compris, les chiffre avec la liste de prix que vous lui donnez, et conserve des totaux par utilisateur et par session. Affectez-le au chat et définissez UserId et SessionId. Comme le compteur se trouve sur le chat, chaque requête au modèle d'une exécution d'agent est comptée elle aussi.
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')]));
La liste de prix associe fournisseur et modèle avec des jokers et peut être chargée depuis du JSON ou un fichier, si bien qu'un changement de prix n'exige pas non plus une nouvelle build. Les budgets transforment le compteur en limite. Un budget couvre toute l'application, chaque utilisateur ou chaque session, pour un jour, un mois ou au total, en argent ou en tokens. Quand un budget est épuisé, la requête suivante est refusée avant d'être envoyée, un budget peut donc aussi interrompre une exécution d'agent à mi-chemin.
// 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;
Définissez BlockWhenExceeded à False pour un budget qui ne fait que déclencher OnBudgetExceeded comme alerte. Pour les rapports, les enregistrements vont vers un fichier CSV ou vers n'importe quel TDataSet. Avec OpenTelemetry activé, chaque requête devient un span qui suit les conventions sémantiques GenAI et part vers votre collecteur OTLP/HTTP en arrière-plan, sans ralentir le chat.
oMeter.OpenTelemetry.Endpoint := 'http://localhost:4318';
oMeter.OpenTelemetry.ServiceName := 'invoice-assistant';
oMeter.OpenTelemetry.Enabled := True;
oMeter.SaveToCSV('usage.csv');
La démo : un assistant de factures
La nouvelle démo Delphi de Demos\15.AI\02.Applications\11.AIAgent est un assistant pour une liste de factures impayées. L'agent dispose de trois outils. list_unpaid_invoices et get_invoice sont en lecture seule, tandis que send_reminder modifie des données et demande d'abord votre approbation. L'agent s'exécute dans un thread d'arrière-plan, la fenêtre reste donc réactive pendant qu'il travaille. La démo permet aussi d'essayer le reste de cet article : outils refusés et masquage des PII, un budget par session avec un compteur de tokens et de coût en direct et un export CSV, des alias de modèle, un fournisseur de fallback, et un serveur MCP dont les outils sont épinglés.
Une documentation que votre agent de code sait lire
Un agent de code n'écrit du bon code que pour les bibliothèques qu'il connaît. Chaque composant de sgcAI, sgcHTML, sgcCrypto et des autres packs sgcWebSockets dispose désormais de sa propre page Markdown pour les modèles de langage, 224 pages au total. Chaque page décrit ce que fait le composant et renvoie vers sa page produit, et la plupart listent ses principales propriétés, méthodes et événements. La page de TsgcAIAgent, ai-agent.md, contient aussi un exemple d'utilisation complet. llms.txt oriente Claude Code, Copilot, Cursor et d'autres agents vers ces pages, si bien que l'agent lit la vraie API avant d'écrire une ligne.
Les compétences d'agent de sgcWebSockets apprennent elles aussi les nouveaux composants. La compétence IA de 2026.11.0 fournit des modèles prêts à l'emploi pour la boucle de l'agent, l'approbation des outils, les guardrails, les alias de modèle avec fallback et le compteur d'usage, et chaque modèle provient d'un code qui compile. Compétences d'agent IA pour Delphi, C++Builder et .NET explique comment les installer.
Disponibilité
TsgcAIAgent, TsgcAIGuardrails, TsgcAIUsageMeter, les alias de modèle et les fallbacks de TsgcAIChat et le pinning d'outils MCP arrivent dans sgcWebSockets 2026.11.0, pour Delphi 7 à 13 et C++Builder. Ils font partie des composants sgcAI, inclus dans les éditions Enterprise et All-Access et dans le produit autonome sgcAI. L'aide documente chaque propriété, méthode et événement, avec des pages sur la boucle d'agent, sur les outils, sur les alias et les fallbacks, sur le masquage des PII, sur la validation de schéma et sur le pinning d'outils.
Pour en savoir plus, consultez les pages des composants TsgcAIAgent, TsgcAIGuardrails et TsgcAIUsageMeter.
À lire ensuite
- Créer un agent IA en Delphi : appel de fonctions et utilisation d'outils MCP, la boucle d'agent expliquée pas à pas
- Client MCP Delphi
- OpenAI vs Anthropic vs Gemini dans Delphi : quelle API d'IA choisir ?
Des questions ou des retours ? Contactez-nous. Vous recevrez une réponse de la part des personnes qui ont écrit le code.
