Un agent vocal en temps réel pour Delphi et .NET

· Composants
Un agent vocal en temps réel pour Delphi et .NET

Taper une question et lire la réponse, c'est ainsi que la plupart des applications parlent aujourd'hui à un modèle de langage. Les API temps réel changent cela. OpenAI Realtime et Gemini Live prennent la voix de l'utilisateur au moment où elle est prononcée et répondent avec leur propre voix, assez vite pour tenir une conversation. Y arriver à la main veut dire diffuser de l'audio dans les deux sens, décider quand l'utilisateur a fini de parler, arrêter l'agent quand il est interrompu et exécuter des outils au milieu de tout cela.

sgcWebSockets 2026.10 ajoute TsgcAIVoiceAgent, un composant qui fait tout cela. Il écoute, détecte quand l'utilisateur a fini un tour de parole, répond et appelle vos outils. Il est disponible pour Delphi 7 à 13 et pour .NET.

TsgcAIVoiceAgent en action : détection des tours de parole, interruption, transcriptions en direct et appels d'outils. Également sur YouTube.

Démarrage rapide

Choisissez le fournisseur, définissez la clé API, une voix et les instructions, gérez OnTranscript et appelez Start. Voilà un assistant vocal fonctionnel.

oVoice := TsgcAIVoiceAgent.Create(nil);
oVoice.Provider := vapOpenAI;
oVoice.OpenAIOptions.ApiKey := 'API_KEY';
oVoice.OpenAIOptions.Voice := 'alloy';
oVoice.Instructions := 'You are a friendly assistant. Answer briefly.';
oVoice.OnTranscript := oVoiceTranscript;
oVoice.OnError := oVoiceError;
oVoice.Start;

OnTranscript livre des transcriptions en direct des deux côtés de la conversation, l'utilisateur et l'agent, pour que l'écran puisse suivre ce qui se dit :

procedure TForm1.oVoiceTranscript(Sender: TObject;
  aRole: TsgcAIVoiceAgentRole; const aText: string; aFinal: Boolean);
begin
  if aFinal then
  begin
    if aRole = varUser then
      Memo1.Lines.Add('You: ' + aText)
    else
      Memo1.Lines.Add('Agent: ' + aText);
  end;
end;

OpenAI Realtime ou Gemini Live, WebSocket ou WebRTC

Deux fournisseurs sont pris en charge, OpenAI Realtime et Gemini Live. Le changement se fait avec une seule propriété et les options correspondantes :

oVoice.Provider := vapGemini;
oVoice.GeminiOptions.ApiKey := 'GEMINI_API_KEY';
oVoice.Start;

La session fonctionne sur WebSocket. Avec OpenAI, elle peut aussi fonctionner sur WebRTC :

oVoice.Provider := vapOpenAI;
oVoice.Transport := vatWebRTC;
oVoice.OpenAIOptions.ApiKey := 'API_KEY';
oVoice.Start;

Audio intégré, ou le vôtre

Sous Windows, Android et iOS, le microphone et le haut-parleur sont intégrés, si bien que le démarrage rapide ci-dessus suffit. Sur toute autre plateforme, ou quand l'audio vient d'ailleurs, désactivez le périphérique. Votre application envoie l'audio avec SendAudio et reçoit la réponse dans OnAudioOutput.

oVoice.Audio.UseDevice := False;
oVoice.Audio.Format := vafPCM16;
oVoice.Audio.SampleRate := 16000;
oVoice.Start;

// ... 20 ms of PCM16 mono at 16 kHz = 640 bytes
SetLength(vChunk, 640);
FillChar(vChunk[0], Length(vChunk), 0);
oVoice.SendAudio(vChunk);

Répondre au téléphone

La téléphonie parle G.711, tout comme l'agent vocal. Réglez le format audio sur mu-law ou A-law à 8 kHz, et les octets d'un appel entrent et sortent directement, sans aucune conversion dans votre code.

oVoice.Provider := vapOpenAI;
oVoice.OpenAIOptions.ApiKey := 'API_KEY';
oVoice.Instructions := 'You answer the phone of a restaurant and take bookings.';
oVoice.Audio.UseDevice := False;
oVoice.Audio.Format := vafG711ULaw;
oVoice.OnAudioOutput := oVoicePhoneAudioOutput;
oVoice.Start;
procedure TForm1.DoPhoneAudioReceived(const aData: TBytes);
begin
  // ... G.711 mu-law, 8 kHz, as it arrives from the RTP stream of the call
  oVoice.SendAudio(aData);
end;

procedure TForm1.oVoicePhoneAudioOutput(Sender: TObject;
  const aData: TBytes);
begin
  // ... already G.711 mu-law at 8 kHz: send it back to the caller
  SendToPhone(aData);
end;

Détection des tours de parole

La détection des tours de parole décide quand l'utilisateur a fini de parler et que l'agent doit répondre. Il existe quatre modes.

oVoice.TurnDetection.Mode := vtdServerVAD;
oVoice.TurnDetection.Threshold := 0.6;
oVoice.TurnDetection.PrefixPaddingMs := 300;
oVoice.TurnDetection.SilenceDurationMs := 700;
oVoice.TurnDetection.Mode := vtdSemanticVAD;
oVoice.TurnDetection.Eagerness := 'low';

Avec le mode presser-pour-parler, validez l'audio et demandez une réponse quand l'utilisateur relâche le bouton :

procedure TForm1.btnTalkMouseUp(Sender: TObject);
begin
  // ... TurnDetection.Mode is vtdManual
  oVoice.CommitAudio;
  oVoice.CreateResponse;
end;

Interruption

Les gens interrompent, et un bon agent vocal les laisse faire. Avec l'interruption, quand l'utilisateur commence à parler, l'agent arrête de parler et la réponse est annulée. La réponse est aussi tronquée à ce que l'utilisateur a réellement entendu, de sorte que l'historique de la conversation ne prétend pas que l'agent a dit des mots qui n'ont jamais été joués.

oVoice.BargeIn := True;
oVoice.OnTurnStarted := oVoiceTurnStarted;
oVoice.OnTurnEnded := oVoiceTurnEnded;
oVoice.OnBargeIn := oVoiceBargeIn;
procedure TForm1.oVoiceBargeIn(Sender: TObject);
begin
  Memo1.Lines.Add('The user interrupted the agent.');
end;

Outils

Un agent vocal devient utile quand il peut faire des choses. Déclarez un outil avec un nom, une description et un schéma JSON pour ses paramètres, et répondez-y dans OnToolCall.

procedure TForm1.StartWeatherAssistant;
var
  oTool: TsgcAIVoiceAgentTool;
begin
  oVoice.Provider := vapOpenAI;
  oVoice.OpenAIOptions.ApiKey := 'API_KEY';
  oVoice.Instructions := 'You are a weather assistant. ' +
    'Call get_weather to know the weather of a city before answering.';

  oVoice.Tools.Clear;
  oTool := oVoice.Tools.Add;
  oTool.Name := 'get_weather';
  oTool.Description := 'Returns the current weather of a city.';
  oTool.Parameters := '{"type":"object","properties":' +
    '{"city":{"type":"string","description":"Name of the city"}},' +
    '"required":["city"]}';

  oVoice.OnToolCall := oVoiceWeatherToolCall;
  oVoice.OnTranscript := oVoiceTranscript;
  oVoice.Start;
end;
procedure TForm1.oVoiceWeatherToolCall(Sender: TObject;
  const aCallId, aName, aArguments: string; var aResult: string;
  var aHandled: Boolean);
var
  oJSON: TsgcJSON;
  vCity: string;
begin
  if aName <> 'get_weather' then
    Exit; // ... not mine: the MCP client, if any, gets the call

  oJSON := TsgcJSON.Create(nil);
  Try
    oJSON.Read(aArguments);
    vCity := '';
    if Assigned(oJSON.Node['city']) then
      vCity := oJSON.Node['city'].Value;
  Finally
    oJSON.Free;
  End;

  // ... replace with a call to your weather service
  aResult := '{"city":"' + vCity + '","temperature":21,"sky":"clear"}';
  aHandled := True;
end;

Les outils peuvent aussi venir d'un serveur MCP. Affectez un client MCP et ses outils sont proposés à l'agent automatiquement :

oMCP := TsgcWSAPIClient_MCP.Create(nil);
oMCP.MCPOptions.HttpOptions.URL := 'https://localhost:5001/mcp';

oVoice.MCPClient := oMCP;
oVoice.Start;

La démo : un assistant vocal de boutique

La démo Delphi dans Demos\15.AI\02.Applications\09.VoiceAgent est un assistant de boutique à qui vous parlez. Elle fonctionne sur un catalogue de produits tenu dans un TDataSet et donne à l'agent quatre outils : find_products pour chercher dans le catalogue, get_product pour lire un produit, update_stock pour changer le stock, et count_low_stock pour lister les produits qui ont besoin d'être réapprovisionnés. Demandez-lui ce qui vient à manquer, puis dites-lui de réapprovisionner un produit, et regardez la grille changer pendant qu'il répond.

Disponibilité

TsgcAIVoiceAgent arrive dans sgcWebSockets 2026.10, pour Delphi 7 à 13 et pour .NET, dans l'édition Enterprise. Le reste des nouveautés de la version se trouve dans l'article sgcWebSockets 2026.10, et les autres composants d'IA sont sur la page produit IA.

À lire aussi

Des questions ou des retours ? Contactez-nous. Vous recevrez une réponse des personnes qui ont écrit le code.