우리 테스트 에이전트는 Twilio 전화를 받았고 발신자가 말을 멈춘 후 1.1초 안에 첫 번째 말을 했습니다. 이것은 p50 왕복 레이턴시이며, gpt-realtime-2와 semantic_vad를 사용하여 40개의 전화로 측정했습니다. 마법이 아닙니다. OpenAI Realtime API는 한 개의 소켓 내에서 음성-음성 처리를 수행하므로, 대략 600ms의 지연을 누적시키는 STT → LLM → TTS 릴레이를 건너뜁니다. 하지만 기본 설정만으로는 1초에 도달하지 못합니다. 여기가 우리가 배포한 7단계 구축입니다. 코드와 레이턴시 표도 함께 있습니다.

이것은 구축 튜토리얼이지, 개념 설명이 아닙니다. 먼저 계층화된 분석이 필요하다면 AI 음성 에이전트가 실제로 무엇인지 읽고 돌아오세요. 아래의 모든 내용은 OpenAI API 키와 Node 런타임이 있다고 가정합니다.

핵심 요점:
- gpt-realtime-2는 한 개의 소켓에서 음성-음성 처리를 수행합니다 — STT/LLM/TTS 릴레이 없음, ~600ms 절약.
- 서버 측에서 임시(ephemeral) 키를 발급합니다. 표준 API 키를 브라우저로 절대 전송하지 마세요.
- Twilio의 미디어 스트림은 8kHz μ-law입니다. Realtime API를 위해 24kHz PCM16으로 리샘플링하세요.
- p50 1.1초 / p95 1.9초 왕복 레이턴시를 측정했습니다. Barge-in은 response.cancel로 실행됩니다.

7단계에서 구축할 것

이 튜토리얼은 1.5초 이내에 응답하는 전화 수신 OpenAI Realtime API 음성 에이전트를 구축하고, 대화 중 실제 함수를 호출하며, 발신자가 중단할 수 있도록 합니다. 플로우는 짧습니다: 발신자가 전화번호로 연결하면, 오디오는 서버로 스트리밍되고, 서버는 이를 한 개의 소켓을 통해 gpt-realtime-2로 브릿지합니다. 모델이 음성으로 응답할 수 있고 도구 호출을 실행할 수 있으며, 오디오가 다시 스트리밍됩니다.

여기 경로가 있으며, 사용 사례에 맞는 단계에서 멈출 수 있습니다:

  • 임시 키 발급 (서버 라우트)
  • 세션 열기 및 구성
  • 함수 호출 추가
  • Twilio로 전화번호에 브릿지 연결
  • Barge-in 및 중단 처리
  • 레이턴시를 1초 미만으로 조정
  • 배포 및 강화

세 가지 전송 방식이 오디오를 전달하며, 선택은 오디오가 어디에서 오는지에 따라 달라집니다. 브라우저가 직접 캡처합니다 (WebRTC), 서버가 이미 원본 스트림을 가지고 있습니다 (WebSocket), 또는 전화 네트워크가 이를 전달합니다 (SIP). Twilio 브릿지에 WebSocket을 사용하고 다른 곳에서 적절한 곳을 언급하겠습니다.

1단계: 임시 키 발급 (건너뛸 수 없는 라우트)

표준 OpenAI API 키를 브라우저나 클라이언트 디바이스에 절대 노출하지 마세요. Realtime API는 정확히 이 목적을 위해 단기 임시 키를 발급합니다. 서버는 실제 키로 POST /v1/realtime/client_secrets를 호출하고, 클라이언트에게 약 1분 내에 만료되는 토큰을 전달한 후, 클라이언트는 대신 그것으로 연결합니다.

다음은 하나를 발급하는 최소한의 Express 라우트입니다:

브라우저는 /session을 가져오고, 단기 시크릿을 읽으며, Realtime 연결을 열어 그것으로 연결합니다. 에이전트가 서버 전용인 경우 (4단계의 Twilio 경우), 클라이언트 핸드오프를 건너뛸 수 있고 백엔드에서 표준 키로 직접 소켓을 열 수 있습니다. 임시 플로우는 신뢰할 수 없는 클라이언트를 보호하기 위해 존재합니다.

2단계: 세션 열기 및 gpt-realtime-2 구성

연결을 열고 모델, 오디오 형식, 음성 및 턴 감지를 설정하는 session.update를 보냅니다. OpenAI 문서에서는 reasoning.effort를 낮게 설정하고 도구 로직이 더 많은 정확도를 필요로 하는 경우에만 높이기를 권장합니다. 더 높은 effort는 레이턴시를 증가시키기 때문입니다. 오디오는 양방향으로 24kHz PCM16으로 실행됩니다.

소켓을 래핑하는 전송 방식은 오디오 출처에 따라 달라집니다:

전체 세션 필드 목록과 GA 기능 세트는 OpenAI Realtime API 문서가 신뢰할 수 있는 출처입니다. 4단계에서 Twilio가 원본 오디오를 제공하기 때문에 WebSocket을 사용합니다.

3단계: 함수 호출 추가 (에이전트가 실제로 할 수 있도록)

행동할 수 없는 음성 에이전트는 보이스오버일 뿐입니다. 함수 호출을 통해 gpt-realtime-2는 대화 중 일시 중지하고, 코드에서 무언가를 실행하도록 요청하며, 결과와 함께 계속 말할 수 있습니다. 세션에서 도구를 선언하면, 모델이 원할 때 function_call_arguments.done 이벤트를 생성하고, 작업을 실행한 후, 출력을 다시 보냅니다.

도구를 선언한 후 이벤트를 처리합니다:

도구가 조용히 실행되지 않는 가장 일반적인 이유: function_call_arguments.done을 리스닝하지 않고 그 후 response.create를 보내지 않는 것입니다. 모델이 호출을 생성했고, 당신이 이를 무시했으며, 발신자는 침묵만 듣습니다.

에이전트가 많은 도구를 다루는 경우, OpenAI Agents SDK가 수학을 바꾸었습니다. 2026년 4월 15일 개선으로 Model Context Protocol (MCP)을 최상급으로 만들고 서브-에이전트 핸드오프를 런타임 프리미티브로 변경했습니다. 따라서 모든 도구를 하나의 프롬프트에 쑤셔 넣는 대신, 라우터 에이전트는 예약을 예약 서브-에이전트로 전달하고 청구 질문을 다른 것으로 전달할 수 있습니다. Agents SDK 음성 빠른 시작은 동일한 Realtime 세션을 RealtimeAgent로 래핑하고 당신의 자신의 오케스트레이션 루프를 작성하지 않고 핸드오프를 제공합니다.

4단계: 전화번호에 브릿지 연결 (Twilio)

실제 전화를 받으려면 전화 통신 제공자를 소켓으로 브릿지합니다. Twilio를 사용하면, 수신 전화를 서버로 WebSocket을 여는 TwiML 으로 지정하고, Twilio와 Realtime API 간 오디오 프레임을 릴레이합니다. SIP는 대안입니다 — OpenAI Realtime은 SIP를 직접 수락하므로, 오디오를 건드릴 필요가 없으면 미디어 릴레이를 완전히 제거합니다.

스트림을 시작하는 TwiML:

여기 놓치면 하루를 낭비할 함정이 있습니다: Twilio의 미디어 스트림은 8kHz μ-law이고, Realtime API는 24kHz PCM16을 원합니다. 양방향으로 리샘플링하지 않으면, 음성이 뭉개지고 다람쥐처럼 들립니다.

전체 프레임 형식은 Twilio Media Streams 문서에 있습니다. 리샘플링을 저렴하게 유지하세요. 무거운 라이브러리는 모든 프레임에서 지불할 레이턴시를 추가합니다.

5단계: Barge-in 및 중단 처리

프로덕션 에이전트는 발신자가 이를 말로 막을 수 있게 합니다. Barge-in은 에이전트가 말하는 동안 발신자가 말하기 시작한 것을 감지한 후 에이전트를 깔끔하게 끊는 것을 의미합니다. Realtime API는 response.cancel로 이를 처리합니다: 턴 감지가 재생 중 음성이 시작되었다고 보고하면, 활성 응답을 취소하고 이미 발신자에게 버퍼링한 모든 오디오를 플러시합니다.

턴 감지에는 두 가지 모드가 있으며, 선택이 중요합니다. server_vad는 원본 침묵 임계값에서 트리거하고 자연스러운 일시 중지에서 발신자를 끊는 경향이 있습니다. semantic_vad는 모델이 발신자가 실제로 생각을 마쳤다고 생각할 때까지 기다리므로, 생각하는 일시 중지에서 훨씬 적은 거짓 중단을 생성합니다. 전화 통화의 경우, semantic VAD가 인간답게 느껴지는 것입니다.

6단계: 레이턴시를 1초 미만으로 조정

여기서 데모는 제품이 되므로, 이론적 예산이 아닌 우리 자신의 구축에서의 숫자입니다. 2026년 5월에 동일한 레스토랑 에이전트를 40개의 테스트 전화로 실행했으며, 단일 소형 서버에서 OpenAI 지역 근처에 배치되었고, 턴 감지 및 reasoning 설정만 바꿨습니다.

실제로 바뀐 레버들, 영향 순서:

  • reasoning.effort를 낮게 유지하세요. 특정 도구가 정말로 정확도를 필요로 하지 않는 한. 중간은 우리 p50을 거의 두 배로 만들었습니다.
  • 오디오를 실시간보다 빠르게 푸시하지 마세요. input_audio_buffer.append를 플러딩하면 버퍼가 오버런되고 드리프트가 발생합니다. 월 클록 시간에 맞춰 프레임을 페이싱하세요.
  • 소켓을 따뜻하게 유지하세요. 통화당 콜드-오픈 연결은 첫 번째 단어 레이턴시에 핸드셰이크를 추가합니다. 통화량이 허용하면 연결을 풀링하세요.
  • 효율적으로 리샘플링하세요. 핫 경로의 순진한 리샘플러는 우리에게 턴당 ~80ms를 추가했습니다.

라이브 상태에서 실행하는 데 분당 비용이 어느 정도입니까? 우리는 bring-your-own-key 수학을 별도로 작업했습니다 — 여기 다시 유도하는 대신 BYOK 음성 에이전트의 분당 비용을 참조하세요.

7단계: 프로덕션을 위해 배포 및 강화

"내 노트북에서 작동했다"와 "하루에 500개 전화를 견딘다" 사이의 차이는 몇 가지 잘 알려진 실패입니다. 여기가 강화 체크리스트이며, 실제로 Realtime 에이전트를 깨뜨리는 실수에서 도출되었습니다:

...

출처 바로가기