Wpisanie pytania i przeczytanie odpowiedzi to sposób, w jaki większość aplikacji dziś rozmawia z modelem językowym. API czasu rzeczywistego to zmieniają. OpenAI Realtime i Gemini Live przyjmują głos użytkownika w chwili, gdy jest wypowiadany, i odpowiadają własnym głosem, na tyle szybko, by podtrzymać rozmowę. Zbudowanie tego ręcznie oznacza przesyłanie strumieniowe audio w obu kierunkach, decydowanie, kiedy użytkownik skończył, zatrzymywanie agenta, gdy zostanie przerwany, oraz uruchamianie narzędzi w trakcie tego wszystkiego.
sgcWebSockets 2026.10 dodaje TsgcAIVoiceAgent, jeden komponent, który robi to wszystko. Słucha, wykrywa, kiedy użytkownik skończył turę, odpowiada głosem i wywołuje twoje narzędzia. Jest dostępny dla Delphi od 7 do 13 oraz dla .NET.
TsgcAIVoiceAgent w akcji: wykrywanie tur, barge-in, transkrypcje na żywo i wywołania narzędzi. Również w serwisie YouTube.
Szybki start
Wybierz dostawcę, ustaw klucz API, głos i instrukcje, obsłuż OnTranscript i wywołaj Start. To już działający asystent głosowy.
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 dostarcza transkrypcje na żywo obu stron rozmowy, użytkownika i agenta, dzięki czemu ekran może śledzić to, co jest mówione:
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 lub Gemini Live, WebSocket lub WebRTC
Obsługiwani są dwaj dostawcy, OpenAI Realtime i Gemini Live. Przełączenie to jedna właściwość i odpowiadające jej opcje:
oVoice.Provider := vapGemini;
oVoice.GeminiOptions.ApiKey := 'GEMINI_API_KEY';
oVoice.Start;
Sesja działa przez WebSocket. Z OpenAI może też działać przez WebRTC:
oVoice.Provider := vapOpenAI;
oVoice.Transport := vatWebRTC;
oVoice.OpenAIOptions.ApiKey := 'API_KEY';
oVoice.Start;
Wbudowane audio lub własne
W systemach Windows, Android i iOS mikrofon i głośnik są wbudowane, więc powyższy szybki start to wszystko, czego potrzebujesz. Na każdej innej platformie, lub gdy audio pochodzi skądinąd, wyłącz urządzenie. Twoja aplikacja dostarcza audio za pomocą SendAudio i odbiera odpowiedź w 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);
Odbieranie telefonu
Telefonia mówi G.711, podobnie jak agent głosowy. Ustaw format audio na mu-law lub A-law przy 8 kHz, a bajty połączenia wchodzą i wychodzą bezpośrednio, bez żadnej konwersji w twoim kodzie.
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;
Wykrywanie tury
Wykrywanie tury decyduje, kiedy użytkownik skończył mówić i agent powinien odpowiedzieć. Są cztery tryby.
- Server VAD pozwala dostawcy wykryć koniec tury za pomocą detekcji aktywności głosowej.
- Semantic VAD nasłuchuje tego, co jest mówione, a nie tylko ciszy, więc pauza w środku zdania nie ucina wypowiedzi użytkownika.
- Client VAD wykrywa turę po stronie klienta.
- Manual to push-to-talk: twoja aplikacja decyduje, gdzie kończy się tura.
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';
Przy push-to-talk zatwierdź audio i poproś o odpowiedź, gdy użytkownik zwolni przycisk:
procedure TForm1.btnTalkMouseUp(Sender: TObject);
begin
// ... TurnDetection.Mode is vtdManual
oVoice.CommitAudio;
oVoice.CreateResponse;
end;
Barge-in
Ludzie przerywają, a dobry agent głosowy im na to pozwala. Dzięki barge-in, gdy użytkownik zaczyna mówić, agent przestaje mówić, a odpowiedź zostaje anulowana. Odpowiedź jest też ucinana do tego, co użytkownik faktycznie usłyszał, więc historia rozmowy nie twierdzi, że agent powiedział słowa, które nigdy nie zostały odtworzone.
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;
Narzędzia
Agent głosowy staje się użyteczny, gdy potrafi coś zrobić. Zadeklaruj narzędzie z nazwą, opisem i schematem JSON dla jego parametrów, a następnie odpowiedz na nie w 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;
Narzędzia mogą też pochodzić z serwera MCP. Przypisz klienta MCP, a jego narzędzia zostaną automatycznie zaoferowane agentowi:
oMCP := TsgcWSAPIClient_MCP.Create(nil);
oMCP.MCPOptions.HttpOptions.URL := 'https://localhost:5001/mcp';
oVoice.MCPClient := oMCP;
oVoice.Start;
Demo: asystent głosowy sklepu
Demo Delphi w Demos\15.AI\02.Applications\09.VoiceAgent to asystent sklepu, z którym rozmawiasz. Działa na katalogu produktów przechowywanym w TDataSet i daje agentowi cztery narzędzia: find_products do przeszukiwania katalogu, get_product do odczytu jednego produktu, update_stock do zmiany stanu magazynowego oraz count_low_stock do wylistowania produktów wymagających uzupełnienia. Zapytaj go, czego zaczyna brakować, a potem każ mu uzupełnić dany produkt i obserwuj, jak siatka zmienia się w trakcie odpowiedzi.
Dostępność
TsgcAIVoiceAgent pojawia się w sgcWebSockets 2026.10, dla Delphi od 7 do 13 i dla .NET, w edycji Enterprise. Co jeszcze nowego jest w tym wydaniu, znajdziesz we wpisie o sgcWebSockets 2026.10, a reszta komponentów AI jest na stronie produktu AI.
Czytaj dalej
- Ekstrakcja dokumentów AI dla Delphi i .NET, również nowość w 2026.10
- Klient Server-Sent Events dla Delphi i .NET
Masz pytania lub uwagi? Skontaktuj się z nami. Otrzymasz odpowiedź od osób, które napisały ten kod.
