질문을 입력하고 답을 읽는 것이 오늘날 대부분의 애플리케이션이 언어 모델과 대화하는 방식입니다. 실시간 API는 이것을 바꿉니다. OpenAI Realtime과 Gemini Live는 사용자의 음성을 말하는 그대로 받아들이고, 대화를 이어갈 수 있을 만큼 빠르게 자신의 목소리로 응답합니다. 이것을 직접 구현하려면 양방향으로 오디오를 스트리밍하고, 사용자가 말을 마쳤는지 판단하고, 끼어들었을 때 에이전트를 멈추고, 그 와중에 도구를 실행해야 합니다.
sgcWebSockets 2026.10은 이 모든 것을 하는 하나의 컴포넌트인 TsgcAIVoiceAgent를 추가합니다. 듣고, 사용자가 한 턴을 마쳤는지 감지하고, 응답하며, 여러분의 도구를 호출합니다. Delphi 7부터 13까지, 그리고 .NET용으로 제공됩니다.
TsgcAIVoiceAgent가 작동하는 모습입니다: 턴 감지, barge-in, 실시간 전사, 도구 호출까지. YouTube에서도 보기.
빠른 시작
제공자를 선택하고, API 키와 목소리, 지시사항을 설정한 다음 OnTranscript를 처리하고 Start를 호출하세요. 그것만으로 작동하는 음성 어시스턴트가 됩니다.
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는 대화 양쪽, 즉 사용자와 에이전트의 실시간 전사를 전달하므로 화면이 말해지는 내용을 따라갈 수 있습니다:
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 또는 Gemini Live, WebSocket 또는 WebRTC
두 개의 제공자가 지원됩니다, OpenAI Realtime과 Gemini Live입니다. 전환은 속성 하나와 그에 맞는 옵션만으로 이루어집니다:
oVoice.Provider := vapGemini;
oVoice.GeminiOptions.ApiKey := 'GEMINI_API_KEY';
oVoice.Start;
세션은 WebSocket을 통해 실행됩니다. OpenAI를 사용하면 WebRTC로도 실행할 수 있습니다:
oVoice.Provider := vapOpenAI;
oVoice.Transport := vatWebRTC;
oVoice.OpenAIOptions.ApiKey := 'API_KEY';
oVoice.Start;
내장 오디오, 또는 직접 구현하는 오디오
Windows, Android, iOS에서는 마이크와 스피커가 내장되어 있으므로 위의 빠른 시작만으로 충분합니다. 다른 플랫폼이나 오디오가 다른 곳에서 오는 경우에는 장치를 꺼두세요. 애플리케이션은 SendAudio로 오디오를 전달하고 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);
전화 받기
전화는 G.711로 말하며, 음성 에이전트도 마찬가지입니다. 오디오 형식을 8 kHz의 mu-law 또는 A-law로 설정하면 통화의 바이트가 코드에서 어떤 변환도 거치지 않고 그대로 들어오고 나갑니다.
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;
턴 감지
턴 감지는 사용자가 말을 마쳤고 에이전트가 응답해야 하는 시점을 결정합니다. 네 가지 모드가 있습니다.
- Server VAD는 제공자가 음성 활동 감지로 턴의 끝을 감지하도록 합니다.
- Semantic VAD는 침묵뿐 아니라 말해지는 내용을 듣기 때문에, 문장 중간의 멈춤이 사용자의 말을 끊지 않습니다.
- Client VAD는 클라이언트에서 턴을 감지합니다.
- Manual은 누르고 말하기 방식입니다: 애플리케이션이 턴이 끝나는 지점을 결정합니다.
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';
누르고 말하기 방식에서는 사용자가 버튼을 놓으면 오디오를 커밋하고 응답을 요청합니다:
procedure TForm1.btnTalkMouseUp(Sender: TObject);
begin
// ... TurnDetection.Mode is vtdManual
oVoice.CommitAudio;
oVoice.CreateResponse;
end;
Barge-in
사람들은 끼어들기 마련이고, 좋은 음성 에이전트는 그것을 허용합니다. barge-in을 사용하면 사용자가 말하기 시작할 때 에이전트가 말을 멈추고 응답이 취소됩니다. 응답은 사용자가 실제로 들은 부분까지만 잘리므로, 대화 기록에 실제로 재생되지 않은 말을 에이전트가 했다고 남지 않습니다.
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;
도구
음성 에이전트는 무언가를 할 수 있을 때 쓸모 있어집니다. 이름, 설명, 매개변수를 위한 JSON 스키마로 도구를 선언하고, 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;
도구는 MCP 서버에서 가져올 수도 있습니다. MCP 클라이언트를 지정하면 그 도구들이 자동으로 에이전트에 제공됩니다:
oMCP := TsgcWSAPIClient_MCP.Create(nil);
oMCP.MCPOptions.HttpOptions.URL := 'https://localhost:5001/mcp';
oVoice.MCPClient := oMCP;
oVoice.Start;
데모: 음성 쇼핑 도우미
Demos\15.AI\02.Applications\09.VoiceAgent에 있는 Delphi 데모는 대화로 이용하는 쇼핑 도우미입니다. TDataSet에 담긴 제품 카탈로그를 기반으로 동작하며, 에이전트에게 네 가지 도구를 제공합니다: 카탈로그를 검색하는 find_products, 제품 하나를 읽는 get_product, 재고를 변경하는 update_stock, 재입고가 필요한 제품을 나열하는 count_low_stock입니다. 무엇이 부족한지 물어본 다음 제품을 재입고하라고 말해보고, 응답하는 동안 그리드가 바뀌는 모습을 지켜보세요.
제공 시기
TsgcAIVoiceAgent는 Delphi 7부터 13까지, 그리고 .NET용으로 sgcWebSockets 2026.10의 Enterprise 에디션에 등장합니다. 이번 릴리스의 다른 새로운 기능은 sgcWebSockets 2026.10 게시물에서, 나머지 AI 컴포넌트는 AI 제품 페이지에서 확인할 수 있습니다.
다음 읽을거리
- Delphi와 .NET을 위한 AI 문서 추출, 역시 2026.10에 새로 추가되는 기능입니다
- Delphi와 .NET을 위한 Server-Sent Events 클라이언트
질문이나 의견이 있으신가요? 문의하기. 코드를 직접 작성한 사람들이 답변해 드립니다.
