
Zbuduj agenta głosowego na OpenAI Realtime API: 7-etapowy przewodnik produkcyjny (2026)
Nasz testowy agent odebrał połączenie z Twilio i wypowiedział pierwsze słowo 1,1 sekundy po tym, jak rozmówca przestał mówić. To medianowe opóźnienie round-trip (p50), zmierzone podczas 40 połączeń przy użyciu modelu gpt-realtime-2 z włączonym semantic_vad. To nie magia. OpenAI Realtime API realizuje komunikację mowa-do-mowy w ramach jednego gniazda, dzięki czemu pomijamy przekaźnik STT → LLM → TTS, który dodaje około 600 ms narzutu. Jednak domyślne ustawienia nie zapewnią Ci czasu reakcji poniżej sekundy. Oto 7-etapowy proces wdrożenia, który zastosowaliśmy, wraz z kodem i tabelą opóźnień.
To jest poradnik techniczny („build tutorial”), a nie wyjaśnienie koncepcyjne. Jeśli najpierw chcesz zrozumieć warstwową strukturę, przeczytaj artykuł czym właściwie jest agent głosowy AI, a następnie wróć tutaj. Wszystko poniżej zakłada, że masz klucz API OpenAI oraz środowisko Node.js.
Kluczowe wnioski:
- gpt-realtime-2 realizuje komunikację mowa-do-mowy w jednym gnieździe — brak przekaźnika STT/LLM/TTS, oszczędność ~600 ms.
- Generuj klucze efemeryczne po stronie serwera; nigdy nie wysyłaj swojego standardowego klucza API do przeglądarki.
- Strumień mediów Twilio działa w formacie 8 kHz μ-law; należy go przesample'ować do 24 kHz PCM16 dla Realtime API.
- Zmierzyliśmy opóźnienie round-trip na poziomie p50 1,1 s / p95 1,9 s. Przerywanie (barge-in) jest obsługiwane przez
response.cancel.
Co zbudujesz w 7 krokach
W tym poradniku stworzymy agenta głosowego OpenAI Realtime API, który odbiera połączenia telefoniczne, odpowiada w czasie krótszym niż 1,5 sekundy, wywołuje rzeczywiste funkcje w trakcie rozmowy i pozwala rozmówcy na przerywanie. Przepływ jest krótki: rozmówca dzwoni na numer telefonu, audio jest streamowane na Twój serwer, Twój serwer przekazuje je do gpt-realtime-2 przez pojedyncze gniazdo, model odpowiada i może wywoływać narzędzia, a audio wraca do rozmówcy.
Oto ścieżka realizacji – możesz zatrzymać się na dowolnym etapie, który pasuje do Twojego przypadku użycia:
- Wygenerowanie klucza efemerycznego (trasa serwerowa)
- Otwarcie i konfiguracja sesji
- Dodanie wywoływania funkcji
- Połączenie z numerem telefonu przez Twilio
- Obsługa przerywania (barge-in) i interwencji
- Dostrojenie opóźnień do poziomu poniżej sekundy
- Wdrożenie i zabezpieczenie
Audio przenoszone jest przez trzy rodzaje transportu, a wybór zależy od źródła dźwięku. Przeglądarka przechwytuje go bezpośrednio (WebRTC), Twój serwer posiada już surowy strumień (WebSocket) lub sieć telefoniczna dostarcza go (SIP). W przypadku mostka Twilio użyjemy WebSocketu, wspominaliśmy o pozostałych tam, gdzie ma to sens.
Krok 1: Wygenerowanie klucza efemerycznego (krok, którego nie można pominąć)
Nigdy nie udostępniaj swojego standardowego klucza API OpenAI przeglądarce ani urządzeniu klienckiemu. Realtime API wydaje w tym celu krótkotrwałe klucze efemeryczne. Twój serwer wywołuje POST /v1/realtime/client_secrets, podając swój prawdziwy klucz, przekazuje klientowi token, który wygasa za około minutę, a klient łączy się przy jego użyciu.
Oto minimalna trasa Express, która generuje taki klucz:
// server.js
import express from "express";
const app = express();
app.get("/session", async (req, res) => {
const r = await fetch("https://api.openai.com/v1/realtime/client_secrets", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPENAI_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
session: { type: "realtime", model: "gpt-realtime-2" },
}),
});
const data = await r.json();
res.json({ client_secret: data.value, expires_at: data.expires_at });
});
app.listen(3000);Przeglądarka pobiera dane z /session, odczytuje krótkotrwały sekret i otwiera połączenie Realtime, używając go. Jeśli Twój agent działa wyłącznie po stronie serwera (przypadek Twilio z Kroku 4), możesz pominąć przekazanie do klienta i otworzyć gniazdo bezpośrednio z backendu, używając standardowego klucza. Przepływ z kluczami efemerycznymi istnieje w celu ochrony niezaufanych klientów.
Krok 2: Otwarcie sesji i konfiguracja gpt-realtime-2
Otwórz połączenie, a następnie wyślij session.update, ustawiając model, format audio, głos oraz wykrywanie zmiany mówcy. Dokumentacja OpenAI zaleca rozpoczęcie od ustawienia reasoning.effort na low i zwiększania go tylko wtedy, gdy logika narzędzi wymaga większej precyzji, ponieważ wyższy wysiłek kosztuje Cię dodatkowe opóźnienia. Audio przesyłane jest w formacie 24 kHz PCM16 w obu kierunkach.
ws.send(JSON.stringify({
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2",
output_modalities: ["audio"],
audio: {
input: { format: "pcm16", sample_rate: 24000 },
output: { format: "pcm16", sample_rate: 24000, voice: "marin" },
},
instructions: "You are a reservations agent for a restaurant. Be brief.",
reasoning: { effort: "low" },
turn_detection: { type: "semantic_vad" },
},
}));Rodzaj transportu, w który opakujesz to gniazdo, zależy od źródła audio:
| Transport | Kiedy używać | Źródło audio |
|---|---|---|
| WebRTC | Przeglądarka lub aplikacja mobilna przechwytują mikrofon bezpośrednio | Urządzenie klienta |
| WebSocket | Twój serwer posiada już surowy strumień audio | Potok serwerowy |
| SIP | Chcesz, aby OpenAI obsługiwało część telefoniczną | PSTN / telefonia |
Pełna lista pól sesji oraz zestaw funkcji GA znajdują się w dokumentacji OpenAI Realtime API. Użyjemy WebSocketu, ponieważ w Kroku 4 Twilio przekaże nam surowe audio.
Krok 3: Dodanie wywoływania funkcji (aby agent mógł naprawdę działać)
Agent głosowy, który nie potrafi działać, to tylko lektor. Wywoływanie funkcji pozwala modelowi gpt-realtime-2 wstrzymać rozmowę, poprosić Twój kod o wykonanie zadania i kontynuować mówienie z uwzględnieniem wyniku. Deklarujesz narzędzie w sesji, model emituje zdarzenie function_call_arguments.done, gdy chce je użyć, Ty wykonujesz pracę i odsyłasz wynik.
Zadeklaruj narzędzie, a następnie obsłuż zdarzenie:
// in session.update -> session.tools:
tools: [{
type: "function",
name: "book_reservation",
description: "Book a table for a given party size and time.",
parameters: {
type: "object",
properties: {
party_size: { type: "integer" },
time: { type: "string", description: "ISO 8601 datetime" },
},
required: ["party_size", "time"],
},
}]
// handling the call:
if (event.type === "response.function_call_arguments.done") {
const args = JSON.parse(event.arguments);
const result = await bookTable(args); // your real logic
ws.send(JSON.stringify({
type: "conversation.item.create",
item: {
type: "function_call_output",
call_id: event.call_id,
output: JSON.stringify(result),
},
}));
ws.send(JSON.stringify({ type: "response.create" })); // let it speak the result
}Najczęstszą przyczyną cichego niewywoływania narzędzi jest: nieobsługiwanie function_call_arguments.done oraz nie wysyłanie afterward response.create. Model wygenerował wywołanie, Ty je zignorowałeś, a rozmówca słyszy ciszę.
Jeśli Twój agent zarządza wieloma narzędziami, OpenAI Agents SDK zmieniło tutaj zasady gry. Jego aktualizacja z 15 kwietnia 2026 roku uczyniła Model Context Protocol (MCP) elementem pierwszoklasowym i zamieniła przekazywanie zadań między pod-agentami w prymityw środowiska wykonawczego. Zamiast upychać każde narzędzie w jednym prompcie, agent routujący może przekazać rezerwację do pod-agenta rezerwacyjnego, a pytanie dotyczące fakturacji do innego. Szybki start Agents SDK dla głosu opakowuje tę samą sesję Realtime w RealtimeAgent i zapewnia mechanizm przekazywania zadań bez pisania własnej pętli orkiestracji.
Krok 4: Mostek do numeru telefonu (Twilio)
Aby odbierać rzeczywiste połączenia, musisz połączyć dostawcę telekomunikacyjnego z gniazdem. W przypadku Twilio kierujesz przychodzące połączenie na TwiML <Connect><Stream>, które otwiera WebSocket do Twojego serwera, a Ty przekażesz ramki audio między Twilio a Realtime API. Alternatywą jest SIP – OpenAI Realtime akceptuje SIP bezpośrednio, co całkowicie eliminuje potrzebę przekaźnika mediów, jeśli nie musisz ingerować w audio.
TwiML inicjujące strumień:
<Response>
<Connect>
<Stream url="wss://your-server.com/twilio-stream" />
</Connect>
</Response>Oto haczyk, który może zabrać Ci cały dzień, jeśli go przeoczysz: strumień mediów Twilio działa w formacie 8 kHz μ-law, a Realtime API wymaga 24 kHz PCM16. Musisz przesample'ować dane w obu kierunkach, w przeciwnym razie otrzymasz zniekształcony, „chipmunkowy” dźwięk.
// inbound: Twilio (8kHz μ-law base64) -> Realtime (24kHz PCM16)
const pcm16 = upsample(muLawDecode(Buffer.from(msg.media.payload, "base64")), 8000, 24000);
realtime.send(JSON.stringify({
type: "input_audio_buffer.append",
audio: pcm16.toString("base64"),
}));
// outbound: Realtime (24kHz PCM16) -> Twilio (8kHz μ-law)
const ulaw = muLawEncode(downsample(modelPcm16, 24000, 8000));
twilioWs.send(JSON.stringify({
event: "media",
media: { payload: ulaw.toString("base64") },
}));Pełny format ramek znajdziesz w dokumentacji Twilio Media Streams. Niech resampling będzie tani, ponieważ ciężka biblioteka w tym miejscu doda opóźnienia, które odczujesz przy każdej ramce.
Krok 5: Obsługa przerywania (Barge-in) i interwencji
Produkcyjny agent pozwala rozmówcy mówić поверх niego. Barge-in oznacza wykrycie, że rozmówca zaczął mówić, gdy agent jest w połowie zdania, a następnie czyste przerwanie agenta. Realtime API obsługuje to za pomocą response.cancel: gdy wykrywanie zmiany mówcy zgłosi rozpoczęcie mowy podczas odtwarzania, anulujesz aktywną odpowiedź i czyścisz wszelkie zbuforowane audio skierowane do rozmówcy.
if (event.type === "input_audio_buffer.speech_started") {
realtime.send(JSON.stringify({ type: "response.cancel" }));
twilioWs.send(JSON.stringify({ event: "clear" })); // drop queued playback
}Wykrywanie zmiany mówcy ma dwa tryby, a wybór ma znaczenie. server_vad uruchamia się na podstawie progów ciszy i tenduje do przerywania rozmówcy przy naturalnych pauzach. semantic_vad czeka, aż model uzna, że rozmówca rzeczywiście zakończył myśl, co powoduje znacznie mniej fałszywych przerwań podczas chwil zastanowienia. Dla połączeń telefonicznych semantic VAD brzmi bardziej ludzko.
Krok 6: Dostrojenie opóźnień do poziomu poniżej sekundy
To moment, w którym demo staje się produktem, dlatego oto liczby z naszej własnej implementacji, a nie teoretyczny budżet. Przeprowadziliśmy testy tego samego agenta restauracyjnego podczas 40 połączeń testowych w maju 2026 roku, na jednym małym serwerze umieszczonym blisko regionu OpenAI, zmieniając jedynie ustawienia wykrywania zmiany mówcy i wnioskowania.
| Konfiguracja | Round-trip p50 | p95 | Uwagi |
|---|---|---|---|
| server_vad, reasoning low | ~1,4 s | ~2,3 s | więcej fałszywych barge-in przy pauzach |
| semantic_vad, reasoning low | ~1,1 s | ~1,9 s | nasz domyślny setting produkcyjny |
| semantic_vad, reasoning medium | ~1,8 s | ~3,1 s | lepsza dokładność narzędzi, wolniej |
Dźwignie, które faktycznie przyniosły efekt, w kolejności wpływu:
- Utrzymuj
reasoning.effortna poziomie low, chyba że konkretne narzędzie naprawdę wymaga precyzji. Poziom medium niemal podwoił nasze p50. - Nie wypychaj audio szybciej niż w czasie rzeczywistym. Przepełnianie
input_audio_buffer.appendpowoduje dryf; dostosuj tempo ramek do czasu zegarowego. - Utrzymuj gniazdo w gotowości. Zimne otwieranie połączenia przy każdym wywołaniu dodaje handshake do opóźnienia pierwszego słowa. Pooluj połączenia tam, gdzie volumen rozmów na to pozwala.
- Efektywny resampling. Naiwny resampler w krytycznej ścieżce dodał nam ~80 ms na każdą turę rozmowy.
Ile kosztuje uruchomienie tego rozwiązania na minutę po wdrożeniu? Oddzielnie przeanalizowaliśmy matematykę bring-your-own-key – zobacz ile kosztuje minuta działania agenta głosowego BYOK, zamiast powtarzać to tutaj.
Krok 7: Wdrożenie i zabezpieczenie do produkcji
Różnica między „działało na moim laptopie” a „przetrwa 500 połączeń dziennie” to kilka dobrze znanych błędów. Oto lista kontrolna hartowania systemu, oparta na błędach, które faktycznie łamią agentów Realtime:
| Pułapka | Objaw | Rozwiązanie |
|---|---|---|
| Zła częstotliwość próbkowania | zniekształcony / chipmunkowy dźwięk | 24 kHz PCM16 w obu kierunkach |
Ignorowanie function_call_arguments.done | narzędzia nigdy nie działają | nasłuchuj i wysyłaj response.create |
| Wypychanie audio szybciej niż realtime | przepełnienie bufora, dryf | dostosuj tempo ramek do realtime |
| Brak logiki ponownego łączenia | rozłączanie przy chwianiu gniazda | auto-reconnect + wznowienie sesji |
Brak obsługi response.done | nakładające się tury rozmowy | blokuj następną turę na response.done |
Dwie dodatkowe kwestie dla rzeczywistego ruchu. Przy długich połączeniach rotuj lub resetuj sesję co kilka tur, aby kontekst nie dryfował, ponieważ 20-minutowa rozmowa gromadzi stan, nad którym model zaczyna się potykać. Loguj każde wywołanie narzędzia wraz z argumentami i wynikiem; gdy rozmówca powie „agent zarezerwował zły termin”, sam transkrypt nie powie Ci, czy błąd leżał po stronie modelu, czy Twojego kodu.
Jeśli wybierzesz ścieżkę Agents SDK z Kroku 3, jej nowa piaskownica kontenerowa uruchamia kod narzędzi w izolacji, co ma znaczenie, gdy Twoje narzędzia dotykają systemu plików lub powłoki systemowej, a nie tylko API.
Kiedy warto kupić zarządzaną platformę zamiast budować samodzielnie
Budowanie bezpośrednio na Realtime API daje największą kontrolę i najniższy koszt za minutę, ale przejmujesz odpowiedzialność za logikę ponownego łączenia, mostek telefoniczny, zgodność z przepisami i obserwowalność – wszystkie nieefektowne części kroków od 4 do 7. Jeśli potrzebujesz agenta telefonicznego działającego w tym tygodniu i nie chcesz utrzymywać przekaźnika mediów, zarządzana platforma jest szybszym wyborem.
Zbudowaliśmy tego samego agenta na trzech dużych platformach i szczerze je porównaliśmy: Retell, Vapi lub Bland. Jeśli nadal wahasz się, po której stronie stanąć, przeanalizuj pełną ramę decyzyjną build-vs-buy, zanim zaangażujesz czas inżynierski.
Gdy zespoły chcą kontroli niestandardowej budowy Realtime, ale nie chcą zatrudniać osób do jej utrzymania, tym się zajmujemy: produkcja agentów głosowych, od mostka telefonicznego po powyższe dostrojenie opóźnień. Chętnie przyjrzymy się Twojemu przypadkowi, jeśli go rozważasz.
O autorze — Mert Batur Gurbuz jest współzałożycielem Techsy.io, gdzie zespół dostarcza agentów AI, systemy automatyzacji oraz pipeline'y voice/SDR dla klientów B2B. Studiuje na University of Birmingham i pisze o stosie narzędzi LLM, z którego zespół Techsy faktycznie korzysta w produkcji. LinkedIn
Często zadawane pytania
Jakie są opóźnienia agenta głosowego OpenAI Realtime API?
W naszej implementacji na gpt-realtime-2 z semantic_vad i niskim wysiłkiem wnioskowania, opóźnienie round-trip wyniosło p50 1,1 s i p95 1,9 s w 40 połączeniach testowych. Komunikacja mowa-do-mowy w jednym gnieździe omija przekaźnik STT/LLM/TTS, co w ogóle umożliwia odpowiedzi w czasie poniżej sekundy.
Czy potrzebuję WebRTC, WebSocketu czy SIP dla mojego agenta głosowego?
Użyj WebRTC, gdy przeglądarka lub aplikacja mobilna przechwytują mikrofon bezpośrednio, WebSocketu, gdy Twój serwer posiada już surowy strumień audio (przypadek mostka Twilio), oraz SIP, gdy chcesz, aby OpenAI obsługiwało część telefoniczną bez Twojego własnego przekaźnika mediów. Większość agentów telefonicznych używa WebSocketu lub SIP.
Jak połączyć OpenAI Realtime API z Twilio?
Skieruj przychodzące połączenie Twilio na TwiML <Connect><Stream>, które otwiera WebSocket do Twojego serwera, a następnie przekaż audio między Twilio a gniazdem Realtime. Przesample'uj 8 kHz μ-law z Twilio do 24 kHz PCM16 wymaganych przez API w obu kierunkach, w przeciwnym razie audio będzie zniekształcone.
Jak działa wywoływanie funkcji w Realtime API?
Deklarujesz narzędzia w konfiguracji sesji. Gdy model chce jedno z nich użyć, emituje zdarzenie function_call_arguments.done. Wykonujesz pracę, odsyłasz wynik jako element konwersacji function_call_output, a następnie wysyłasz response.create, aby agent wypowiedział wynik. Zapomnienie o tym ostatnim kroku jest powodem, dla którego narzędzia często „cichą” awarią.
Jak obsługiwać przerywanie (barge-in) w Realtime API?
Gdy wykrywanie zmiany mówcy zgłosi input_audio_buffer.speech_started podczas odtwarzania, wyślij response.cancel, aby zatrzymać aktywną odpowiedź i wyczyścić wszelkie zakolejkowane audio wyjściowe skierowane do rozmówcy. Połącz to z semantic_vad, aby naturalne pauzy nie wyzwalały fałszywych przerwań w połowie zdania.
Jaką częstotliwość próbkowania audio używa OpenAI Realtime API?
Realtime API używa audio 24 kHz PCM16 w obu kierunkach. Dostawcy telekomunikacyjni, tacy jak Twilio, dostarczają 8 kHz μ-law, więc mostek telefoniczny musi przesample'ować dane w górę przy wejściu i w dół przy wyjściu. Niedopasowane częstotliwości próbkowania są najczęstszą przyczyną zniekształceń audio.
Ile kosztuje uruchomienie agenta głosowego na Realtime API?
Koszt zależy od minut audio wejściowego i wyjściowego na gpt-realtime-2, a ekonomia bring-your-own-key różni się znacznie od zarządzanej platformy płatnej za minutę. Przeanalizowaliśmy pełną matematykę w naszym rozbiorze cenowym agentów głosowych, zamiast szacować ją tutaj.
Czy powinienem budować na Realtime API, czy używać Retell, Vapi lub Bland?
Buduj bezpośrednio, gdy chcesz maksymalnej kontroli i najniższego kosztu za minutę oraz możesz wziąć na siebie odpowiedzialność za ponowne łączenie, telefonię i zgodność z przepisami. Kup zarządzaną platformę, gdy szybkość wprowadzenia na rynek jest ważniejsza. Nasze porównanie Retell vs Vapi vs Bland oraz rama decyzyjna build-vs-buy omawiają kompromisy.
Co zmieniła aktualizacja OpenAI Agents SDK z kwietnia 2026 dla agentów głosowych?
Aktualizacja z 15 kwietnia 2026 roku uczyniła Model Context Protocol elementem pierwszoklasowym, dodała piaskownicę kontenerową dla kodu narzędzi i zamieniła przekazywanie zadań między pod-agentami w prymityw środowiska wykonawczego. Dla agentów głosowych oznacza to, że agent routujący może przekazywać zadania wyspecjalizowanym pod-agentom, zamiast upychać każde narzędzie w jednym prompcie.