본문 바로가기
AI System/OpenAI API와 바이브 코딩으로 배우는 AI 서비스 개발

d02 - 06. Audio - 04. voice agent - README.md

by Toddler_AD 2026. 6. 2.

Realtime Voice Agent 실습 가이드

  • 이 문서는 06.audio/04.voice_agent 폴더의 Step 1~4 예제를 기준으로 작성한 교육용 실습 교재입니다.
    마이크 입력이 포함된 Step 3, Step 4는 실습 환경에 따라 전사 문장이 달라질 수 있습니다.

0. 강의 안내

0.1 강의 목표

이번 강의에서 배우는 내용

  • OpenAI Realtime API를 WebSocket으로 연결하는 방법
  • 텍스트 응답을 음성 응답으로 확장하는 방법
  • 마이크 입력을 서버로 실시간 전송하는 방법
  • Server VAD (Voice Activity Detection)로 발화 시작과 끝을 감지하는 방법
  • 사용자가 끼어들거나 "그만"이라고 말했을 때 응답을 제어하는 방법

수업 종료 후 할 수 있게 되는 것

  • 텍스트 기반 Realtime 예제를 직접 실행하고 구조를 설명할 수 있습니다.
  • 스피커로 나오는 TTS 응답을 재생하는 코드를 이해할 수 있습니다.
  • 마이크 입력을 받아 사용자 전사 결과를 실시간으로 확인할 수 있습니다.
  • 인터럽트가 가능한 CLI 음성 에이전트를 실행하고 응용 실습으로 확장할 수 있습니다.

0.2 실습 결과 미리보기

완성된 프로그램 화면

======================================================================
  🎤 마이크로 말하세요!
  💡 에이전트가 말하는 중에 끼어들어보세요 (즉시 중단됩니다)
  🎧 스피커 재유입 방지를 위해 이어폰/헤드셋 사용을 권장합니다
  🛑 '멈춰', '그만' 같은 명령으로 응답 차단 가능
  ⏹️  종료: Ctrl+C
======================================================================

실행 데모

아래는 Step 4를 실제로 실행했을 때 확인한 콘솔 출력 예시입니다.

[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료

[💬 사용자] 안녕하세요.

[🤖 에이전트] 안녕하세요! 반갑습니다. 무엇을 도와드릴까요?
[✅ 에이전트] 응답 완료

중요한 관찰 포인트

  • 사용자 전사가 먼저 출력됩니다.
  • 그다음 에이전트 응답이 출력됩니다.
  • 응답이 끝나면 완료 로그가 명확하게 표시됩니다.
  • 실제 전사 문장은 주변 소음, 마이크 위치, 발음에 따라 달라질 수 있습니다.

0.3 학습 흐름

개념 이해
→ 프로그램 실행
→ 코드 분석
→ 기능 확장
→ 응용 실습

이 강의는 한 번에 모든 기능을 설명하지 않습니다. Step 1에서 연결을 먼저 확인하고, Step 2에서 음성 출력을 추가하고, Step 3에서 마이크 입력을 다루고, Step 4에서 완전한 대화와 인터럽트를 합칩니다.


1. 문제 정의 (Why)

1.1 해결하려는 문제

우리가 만들려는 것은 CLI 환경에서 동작하는 실시간 음성 에이전트입니다.

해결하려는 문제는 다음과 같습니다.

  • 사용자가 키보드를 치지 않고도 AI와 대화하고 싶습니다.
  • 사용자가 말하는 동안 서버로 오디오를 계속 보내고 싶습니다.
  • 응답을 텍스트가 아니라 음성으로 바로 듣고 싶습니다.
  • AI가 말하는 중에도 사용자가 끼어들 수 있는 자연스러운 대화 흐름이 필요합니다.

1.2 기존 방식의 한계

기존 음성 처리 방식은 보통 아래 흐름을 따릅니다.

녹음 시작
→ 녹음 종료
→ 파일 저장
→ 서버 업로드
→ STT 처리
→ LLM 응답 생성
→ TTS 변환
→ 오디오 다운로드
→ 재생

이 방식의 한계는 분명합니다.

  • 한 문장을 다 말할 때까지 기다려야 합니다.
  • 처리 단계가 많아서 지연이 큽니다.
  • 응답이 시작된 뒤 사용자가 끼어들기 어렵습니다.
  • 대화가 부드럽지 않고 "명령 실행"처럼 느껴집니다.

1.3 이번 강의에서 만들 것

이번 강의에서는 아래 4단계를 거쳐 최종적으로 CLI 음성 에이전트를 만듭니다.

Step 1: 텍스트로 Realtime 연결 확인
Step 2: 에이전트 음성 출력 추가
Step 3: 사용자 음성 입력과 전사 확인
Step 4: 전사 + 응답 생성 + 인터럽트까지 통합

최종 결과물의 핵심 기능은 다음과 같습니다.

  • 마이크 입력을 서버로 실시간 전송
  • 사용자 발화를 텍스트로 전사해서 출력
  • 에이전트 응답을 음성으로 재생
  • 에이전트가 말하는 도중 끼어들기 지원
  • "멈춰", "그만" 같은 정지 명령 인식

2. 핵심 개념 (Concept)

2.1 반드시 알아야 할 핵심 개념

1. Realtime API

Realtime API는 한 번의 요청과 한 번의 응답으로 끝나는 방식이 아니라, 연결을 유지하면서 오디오와 이벤트를 계속 주고받는 방식입니다.

연결 유지
→ 이벤트 송수신
→ 음성 스트리밍
→ 응답 스트리밍

2. WebSocket

WebSocket은 연결을 계속 유지하면서 클라이언트와 서버가 서로 자유롭게 메시지를 보내는 통신 방식입니다.

이 실습에서는 다음 이벤트 흐름이 중요합니다.

session.created
→ session.update
→ session.updated
→ conversation / audio 이벤트 송수신
→ response.done

3. Streaming

스트리밍은 전체 결과를 다 만든 다음 한 번에 보내는 것이 아니라, 조각 단위로 바로 보내는 방식입니다.

이 실습에서는 두 가지 스트리밍이 나옵니다.

  • 텍스트 스트리밍: response.output_text.delta
  • 오디오 스트리밍: response.output_audio.delta

4. Audio Sampling

현재 예제는 다음 오디오 설정을 사용합니다.

  • SAMPLE_RATE = 24000
  • CHANNELS = 1
  • DTYPE = int16
  • BLOCKSIZE = 960

쉽게 말하면, 마이크 소리를 24kHz 모노 PCM16 형식으로 잘게 쪼개 서버에 보내는 구조입니다.

5. Server VAD (Voice Activity Detection)

VAD는 Voice Activity Detection, 즉 "지금 사람이 말하고 있는가"를 판단하는 기능입니다.

이 예제에서는 서버가 발화 시작과 종료를 판단합니다.

사용자 말 시작
→ speech_started 이벤트
→ 오디오 스트리밍 계속 전송
→ 사용자가 멈춤
→ speech_stopped 이벤트
→ 전사 완료

6. Interrupt

Step 4의 핵심 개념입니다.

  • 사용자가 에이전트 말 중간에 다시 말하면 응답을 멈춥니다.
  • 사용자가 "그만"이라고 말하면 응답 생성을 차단합니다.
  • 오디오 출력 큐와 서버 응답 상태를 함께 관리해야 실제로 잘 멈춥니다.

2.2 핵심 용어 정리

용어 설명
Realtime API 연결을 유지한 상태로 음성과 이벤트를 주고받는 API
WebSocket 양방향 실시간 통신을 위한 연결 방식
PCM16 압축하지 않은 16비트 오디오 형식
Sample Rate 1초 동안 몇 개의 샘플로 소리를 표현하는지 나타내는 값
Frame 잘게 나눈 오디오 데이터 조각
VAD 말의 시작과 끝을 감지하는 기술
Transcript 음성을 문자로 바꾼 결과
Delta Event 스트리밍 중 조금씩 도착하는 응답 조각
Interrupt 진행 중인 에이전트 응답을 중단하는 동작

3. 실습 준비 (Setup)

3.1 개발 환경

권장 환경은 다음과 같습니다.

  • Python 3.10 이상
  • VS Code
  • 마이크와 스피커 또는 헤드셋
  • OpenAI API Key

현재 폴더에서 사용하는 주요 라이브러리

  • websockets
  • sounddevice
  • python-dotenv
  • numpy

3.2 환경 설치

터미널에서 패키지 추가를 위해 아래 명령을 실행합니다.

uv add websockets sounddevice python-dotenv numpy

3.3 API Key 설정

프로젝트 루트인 AS.1.1 폴더에 .env 파일을 만들고 아래처럼 입력합니다.

OPENAI_API_KEY=여기에_본인_API_키

이 값이 없으면 Step 1~4 모두 시작할 수 없습니다.

3.4 오디오 실습 폴더 구조

오디오 실습 폴더 구조는 아래와 같습니다.

06.audio/
└─ 04.voice_agent/
   ├─ README.md
   ├─ step1_basic_connection.py
   ├─ step2_audio_output.py
   ├─ step3_audio_input.py
   └─ step4_full_interrupt.py

각 파일의 역할은 다음과 같습니다.

  • step1_basic_connection.py: 텍스트 기반 최소 연결
  • step2_audio_output.py: 음성 출력 추가
  • step3_audio_input.py: 마이크 입력과 사용자 전사 확인
  • step4_full_interrupt.py: 완전한 음성 대화와 인터럽트 처리

4. 가장 먼저 실행하기 (Quick Start)

교육에서 가장 중요한 단계는 "일단 실행해 보는 것"입니다. 이 폴더에서는 Step 1이 가장 작은 실행 단위입니다.

4.1 최소 실행 코드

Step 1의 핵심 시작점은 아래 한 줄입니다.

if __name__ == "__main__":
    asyncio.run(main())

이 코드는 main() 함수를 실행해 WebSocket 연결, 세션 설정, 사용자 메시지 전송, 응답 출력까지 한 번에 수행합니다.

4.2 프로그램 실행

cd 06.audio\04.voice_agent
uv run python .\step1_basic_connection.py

4.3 실행 결과 확인

실제 확인한 콘솔 출력 예시는 아래와 같습니다.

============================================================
  Step 1: Realtime API 기본 연결
============================================================

[1/4] WebSocket 연결 중...
[✅] WebSocket 연결 완료

[2/4] 세션 생성: session.created

[3/4] 세션 설정 전송...
[✅] 세션 설정 완료: session.updated

[4/4] 대화 시작
------------------------------------------------------------

👤 사용자: 안녕하세요! 오늘 날씨 어때요?

[🤖 에이전트] 안녕하세요! 현재 계신 지역이 어디인지 말씀해 주시면, 그곳의 날씨를 알려드릴게요.

[✅] 응답 완료

이 결과를 보면 최소한 다음 4가지는 확인된 것입니다.

  • API 키가 정상적으로 로드되었습니다.
  • WebSocket 연결이 성공했습니다.
  • 세션 설정이 서버에 반영되었습니다.
  • 텍스트 응답 스트리밍이 정상 동작했습니다.

5. 코드 단계별 구현 (Build Step by Step)

이 폴더의 실습은 실제 파일 기준으로 4단계입니다. 각 단계는 "설명 → 코드 → 실행" 흐름으로 이해하면 좋습니다.

Step 1. 기본 프로그램 만들기: 텍스트로 먼저 연결하기

설명

  • Step 1은 가장 단순한 Realtime 예제입니다.
  • 음성은 아직 사용하지 않습니다.
  • 텍스트를 보내고, 텍스트 응답을 받아 출력합니다.
  • 이 단계가 성공해야 뒤 단계도 안정적으로 이해할 수 있습니다.

코드에서 볼 것

  • open_websocket(): websockets 버전 차이를 흡수하는 연결 함수
  • session.update: 세션 지시사항과 출력 모달리티(modality) 설정
  • conversation.item.create: 사용자 메시지 생성
  • response.create: 응답 생성 요청

실행

uv run python step1_basic_connection.py

실행 결과 예시

[1/4] WebSocket 연결 중...
[✅] WebSocket 연결 완료

[2/4] 세션 생성: session.created

[3/4] 세션 설정 전송...
[✅] 세션 설정 완료: session.updated

[4/4] 대화 시작
------------------------------------------------------------

👤 사용자: 안녕하세요! 오늘 날씨 어때요?

[🤖 에이전트] 안녕하세요! 현재 계신 지역이 어디인지 말씀해 주시면, 그곳의 날씨를 알려드릴게요.

[✅] 응답 완료

이 단계에서 꼭 이해할 점

  • session.created는 연결이 열렸다는 뜻입니다.
  • session.updated는 우리가 보낸 설정이 서버에 적용되었다는 뜻입니다.
  • response.done은 서버 기준으로 한 턴의 응답 스트리밍이 끝났다는 뜻입니다.

Step 2. 응답 받기: 텍스트 응답을 음성 출력으로 확장하기

설명

  • Step 2에서는 에이전트 응답을 스피커로 재생합니다.
  • 텍스트만 보던 Step 1에서 한 단계 발전한 구조입니다.
  • 실제 오디오는 response.output_audio.delta 이벤트를 모아 로컬 재생 큐에 넣은 뒤 스피커로 재생합니다.

코드에서 볼 것

  • AudioPlayer 클래스
  • base64.b64decode()로 오디오 델타 복원
  • 오디오 재생용 큐와 워커 스레드
  • 텍스트 델타(delta)와 오디오 델타(delta)를 동시에 처리하는 루프

실행

uv run python step2_audio_output.py

 

실행 결과 예시

============================================================
  Step 2: 음성 출력 추가
============================================================

[1/6] 오디오 플레이어 시작...

[✅] 오디오 플레이어 시작

[2/6] WebSocket 연결 중...
[✅] WebSocket 연결 완료

[3/6] 세션 생성: session.created

[4/6] 세션 설정 전송 (오디오 모드)...

[✅] 세션 설정 완료: session.updated

[5/6] 대화 시작
------------------------------------------------------------

👤 사용자: 안녕하세요! 간단하게 자기소개 해주세요.

[6/6] 에이전트 응답 중... (음성 재생)

[🤖 에이전트] 안녕하세요! 저는 친절하고 똑똑한 AI 비서예요. 여러분께 도움을 드리고, 궁금한 점에 답변해드리며, 필요한 정보를 찾아드리는 역할을 해요. 언제든 편하게 말씀해 주세요.
[✅] 응답 완료
[✅] 오디오 재생 완료

이 단계에서 꼭 이해할 점

  • 텍스트 출력과 음성 출력은 서로 다른 이벤트를 통해 들어옵니다.
  • 스피커 재생은 네트워크 수신과 분리해서 처리해야 끊김이 줄어듭니다.
  • response.done은 서버가 응답 스트리밍을 끝냈다는 뜻이고, 로컬 스피커 재생이 끝났다는 뜻은 아닙니다.
  • 콘솔에 보이는 오디오 재생 완료 로그는 로컬 재생 큐까지 모두 비워졌다는 뜻입니다.

Step 3. 마이크 입력 받기: 사용자 음성을 전사해 보기

설명

  • Step 3의 역할은 명확합니다. 이 단계는 "사용자 음성을 서버로 보내고, 전사 결과를 확인하는 것"에 집중합니다.
  • 아직 에이전트 응답을 생성하거나 재생하지 않습니다.
  • 교육적으로는 "입력 파이프라인"을 따로 분리해서 이해하는 단계입니다.

코드에서 볼 것

  • MicrophoneCapture 클래스
  • sounddevice.RawInputStream 콜백 구조
  • send_microphone_to_server() 비동기 전송 루프
  • receive_transcription_events() 전사 이벤트 수신 루프
  • Server VAD (Voice Activity Detection)와 create_response=False 설정

실행

uv run python step3_audio_input.py

 

실행 결과 예시

아래는 실제 검증 중 확인한 출력 예시입니다.

============================================================
  Step 3: 오디오 입력(전사)만 다루기
============================================================

[1/3] WebSocket 연결 중...
[✅] 연결 완료

[2/3] 세션 생성 완료

[3/3] 세션 설정 (오디오 입력/전사 전용)...
[✅] 설정 완료

============================================================
  🎤 마이크로 말하세요!
  💡 이 단계에서는 사용자 전사만 출력합니다
  ⏹️  종료: Ctrl+C
============================================================

[✅] 마이크 캡처 시작
    💡 말하면 발화 이벤트/전사가 표시됩니다

[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료
[💬 사용자] 안녕하세요

[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료
[💬 사용자] 반갑습니다

이 단계에서 꼭 이해할 점

  • Step 3에서 에이전트가 말하지 않는 것은 정상입니다.
  • 사용자의 발화 시작과 종료가 별도 이벤트로 보입니다.
  • 전사 결과가 바로 [💬 사용자] 형식으로 출력됩니다.
  • 이 단계가 안정적이어야 Step 4에서 인터럽트 로직을 올릴 수 있습니다.

Step 4. 완성형 음성 에이전트: 응답 생성과 인터럽트까지 통합하기

설명

  • Step 4는 Step 2와 Step 3을 합친 통합 단계입니다.
  • 사용자 전사를 먼저 출력한 뒤, 응답을 수동으로 생성합니다.
  • 이 설계 덕분에 교육생이 "무슨 말을 했는지"를 먼저 확인하고, 그 다음 AI 응답을 보는 흐름이 유지됩니다.
  • 인터럽트와 정지 명령어도 이 단계에서 처리합니다.

코드에서 볼 것

  • VoiceAgent 클래스의 상태 관리
  • request_response() 수동 응답 생성 흐름
  • cancel_response()와 오디오 큐 정리
  • 에코 억제와 중복 취소 방지 로직
  • interrupt_response=True, create_response=False 설정과 수동 응답 생성 흐름

실행

uv run python step4_full_interrupt.py

 

실행 결과 예시

아래는 실제 실행 중 확인한 출력 예시입니다.

======================================================================
  Step 4: 사용자 인터럽트 기능 추가
======================================================================

[1/4] 초기화...
[✅] 완료

[2/4] WebSocket 연결...
[✅] 연결 완료

[3/4] 세션 생성 완료

[4/4] 세션 설정 (인터럽트 응답 활성화)...
[✅] 설정 완료

======================================================================
  🎤 마이크로 말하세요!
  💡 에이전트가 말하는 중에 끼어들어보세요 (즉시 중단됩니다)
  🎧 스피커 재유입 방지를 위해 이어폰/헤드셋 사용을 권장합니다
  🛑 '멈춰', '그만' 같은 명령으로 응답 차단 가능
  ⏹️  종료: Ctrl+C
======================================================================


[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료

[💬 사용자] 안녕

[🤖 에이전트] 안녕하세요! 무엇을 도와드릴까요?
[✅ 에이전트] 응답 완료


[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료

[💬 사용자] 트랜스포머 아키텍처에 대해서 설명해줘

[🤖 에이전트] 좋아요, 트랜스포머 아키텍처에 대해 간단히 설명해 드릴게요. 트랜스포머는 자연어 처리 분야에서 많이 사용되는 모델인데요. 주로 '어텐션 메커니즘'이라는 기술을 활용해서 입력 문장의 각 단어가 서로 어떤 관계가 있는지 학습합니다. 이렇게 해서 번역, 요약, 문장 생성 같은 다양한 작업에서 뛰어난 성능을 보이죠. 궁금한 부분이 있으면 더 물어봐 주세요.
[✅ 에이전트] 응답 완료


[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료

[💬 사용자] 그만

[⏹️  중지 명령] 응답 생성 차단


[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료

[💬 사용자] decoder-only 모델에 대해서 설명해줘

[🤖 에이전트] 알겠습니다. 디코더 온리 모델은 주로 생성형 작업, 예를 들어 번역이나 문장 생성에 사용되는 구조입니다. 입력 데이터를 이해하고 처리하는 인코더 없이, 디코더만을 사용해서 이전에 생성된 단어들을 기반으로 다음 단어를 예측하는 방식이에요. 대표적인 예로 GPT 같은 모델이 이런 디코더 구조를 따르고 있죠.
[✅ 에이전트] 응답 완료


[🎤 사용자] 발화 시작
[🎤 사용자] 발화 종료

[💬 사용자] 멈춰

[⏹️  중지 명령] 응답 생성 차단


[⏹️] 프로그램을 종료했습니다.

실습 중 추가로 확인할 것

  • 에이전트가 길게 말할 때 중간에 다시 말해 봅니다.
  • "멈춰", "그만"을 말해서 응답 생성이 차단되는지 봅니다.
  • 이어폰을 쓰지 않았을 때 사용자 전사가 에이전트 음성을 따라 적는지 관찰해 봅니다.

이 단계에서 꼭 이해할 점

  • 사용자 전사가 먼저 보이도록 응답 생성 시점을 제어합니다.
  • 인터럽트는 서버 취소만으로 끝나지 않고, 로컬 재생 큐도 함께 비워야 체감상 잘 멈춥니다.
  • 실제 음성 앱에서는 상태 관리가 가장 중요합니다.

6. 프로그램 동작 원리 (How It Works)

6.1 전체 흐름

최종 Step 4 기준 전체 흐름은 아래와 같습니다.

마이크 입력
→ 오디오 데이터 캡처
→ Base64 인코딩
→ WebSocket으로 서버 전송
→ Server VAD (Voice Activity Detection)가 발화 구간 판단
→ Whisper 전사 결과 수신
→ response.create 요청
→ 에이전트 응답 텍스트/오디오 스트리밍 수신
→ 스피커 재생

6.2 데이터 흐름

좀 더 세부적으로 보면 다음 순서로 움직입니다.

sounddevice callback
→ asyncio.Queue 적재
→ send_audio 루프에서 서버 전송
→ 서버가 speech_started / speech_stopped 이벤트 전송
→ transcription completed 이벤트 전송
→ VoiceAgent가 전사 출력
→ response.create 호출
→ text/audio delta 수신
→ AudioPlayer 큐 적재
→ 스피커 재생

6.3 API 통신 구조

실습에서 자주 보게 되는 주요 이벤트는 아래와 같습니다.

이벤트 의미
session.created WebSocket 연결 직후 세션이 만들어짐
session.update 클라이언트가 세션 설정을 보냄
session.updated 세션 설정이 반영됨
input_audio_buffer.speech_started 서버가 사용자 발화 시작을 감지함
input_audio_buffer.speech_stopped 서버가 사용자 발화 종료를 감지함
conversation.item.input_audio_transcription.completed 사용자 전사가 완성됨
response.create 에이전트 응답 생성을 요청함
response.output_text.delta 텍스트 응답 일부가 도착함
response.output_audio.delta 오디오 응답 일부가 도착함
response.done 서버 기준으로 한 번의 응답 스트리밍이 종료됨. 로컬 스피커 재생 완료와는 다를 수 있음
response.cancel 진행 중 응답을 취소함

7. 코드 구조 분석 (Code Walkthrough)

7.1 디렉토리 구조

06.audio/
└─ 04.voice_agent/
   ├─ README.md
   ├─ step1_basic_connection.py
   ├─ step2_audio_output.py
   ├─ step3_audio_input.py
   └─ step4_full_interrupt.py

7.2 주요 모듈

파일 역할
step1_basic_connection.py 텍스트 기반 Realtime 연결 학습
step2_audio_output.py 오디오 출력과 재생 큐 학습
step3_audio_input.py 마이크 캡처와 사용자 전사 학습
step4_full_interrupt.py 통합 음성 대화와 인터럽트 로직 학습

7.3 핵심 함수와 클래스

이름 역할
open_websocket() websockets 버전 차이를 흡수하며 연결 생성
AudioPlayer 서버에서 받은 PCM 오디오를 스피커로 재생
MicrophoneCapture 마이크 입력을 캡처해 비동기 큐로 전달
send_microphone_to_server() 캡처한 오디오를 서버로 계속 전송
receive_transcription_events() Step 3에서 사용자 전사 이벤트만 출력
VoiceAgent Step 4에서 응답 생성, 취소, 인터럽트 상태를 관리
cancel_response() 진행 중 응답과 재생 큐를 함께 중단
request_response() 사용자 전사가 끝난 뒤 응답 생성을 요청

 

코드를 읽을 때 추천 순서

main()
→ session.update 구조
→ 이벤트 수신 루프
→ AudioPlayer / MicrophoneCapture
→ VoiceAgent 상태 관리

8. 고급 기능 (Advanced)

8.1 동시성 처리

이 실습은 단순한 순차 실행이 아닙니다.

  • Step 3은 마이크 캡처, 오디오 전송, 전사 수신을 동시에 수행합니다.
  • Step 4는 여기에 응답 생성과 오디오 재생까지 추가됩니다.

핵심 기술은 다음과 같습니다.

  • asyncio.create_task()
  • asyncio.gather()
  • queue.Queue와 asyncio.Queue
  • 오디오 재생용 백그라운드 스레드

8.2 스트리밍 처리

스트리밍의 장점은 사용자가 기다리는 시간을 줄이는 데 있습니다.

  • 텍스트는 조각 단위로 바로 보입니다.
  • 오디오는 조각 단위로 바로 재생 큐에 들어갑니다.
  • 전체 결과가 끝날 때까지 기다리지 않아도 됩니다.

8.3 성능 최적화

현재 예제에 이미 반영된 최적화 포인트는 아래와 같습니다.

  • BLOCKSIZE = 960으로 안정적인 입력 프레임 유지
  • Step 4에서 noise_reduction: far_field 적용
  • 응답 취소 시 오디오 큐 즉시 비우기
  • 중복 취소 방지를 위한 debounce 적용
  • 사용자 전사 이후에만 응답을 생성해 콘솔 순서 안정화

추가로 실습 시 권장하는 방법

  • 스피커 대신 이어폰 또는 헤드셋 사용
  • 조용한 환경에서 마이크 테스트
  • 한 문장을 너무 길게 말하지 않기

9. 문제 해결 (Troubleshooting)

9.1 자주 발생하는 오류

문제 원인 해결 방법
OPENAI_API_KEY 오류 .env 파일이 없거나 키가 비어 있음 프로젝트 루트에 .env를 만들고 키를 다시 입력
WebSocket 연결 실패 인터넷 문제 또는 키 문제 네트워크 상태 확인, API 키 재확인
마이크 인식 안 됨 장치 권한 또는 기본 입력 장치 문제 Windows 마이크 권한 허용, 기본 장치 확인
소리가 안 들림 출력 장치 문제 또는 볼륨 문제 스피커 선택, 볼륨 확인, 다른 출력 장치 테스트
사용자 전사가 이상함 주변 소음 또는 스피커 재유입 이어폰 사용, TV나 스피커 소리 줄이기
Step 3에서 에이전트가 말하지 않음 현재 설계상 정상 Step 3은 전사 전용, 응답은 Step 4에서 확인
Step 4에서 응답이 늦음 마이크 입력이 길거나 소음이 많음 짧고 분명하게 말하고 조용한 환경에서 재시도
종료 후 터미널에 exit code 1 표시 Ctrl+C로 수동 종료한 경우가 있음 프로세스가 남아 있지 않다면 치명적 오류는 아님

 

실습 중 특히 많이 헷갈리는 지점

  1. Step 3은 실패한 것이 아니라 의도적으로 "사용자 전사만" 출력합니다.
  2. Step 4의 전사 문장은 환경에 따라 크게 달라질 수 있습니다.
  3. 스피커 재유입이 있으면 사용자가 말하지 않은 문장도 전사될 수 있습니다.

10. 실습 과제 (Hands-on)

Lab 1. Step 1 프롬프트 바꾸기

  • 사용자 메시지를 다른 주제로 바꿔봅니다.
  • instructions를 바꿔서 에이전트 말투를 바꿔봅니다.
  • 예: 친절한 상담원, 영어 튜터, 여행 가이드

Lab 2. Step 2 음성 스타일 바꾸기

  • voice 값을 바꿔 다른 목소리를 들어봅니다.
  • 텍스트 응답과 음성 응답이 어떻게 같이 출력되는지 관찰합니다.
  • 재생 큐가 왜 필요한지 다시 설명해 봅니다.

Lab 3. Step 3 VAD 감도 실험하기

  • silence_duration_ms 값을 조절해 봅니다.
  • 발화가 너무 빨리 끝나는지, 너무 늦게 끝나는지 비교합니다.
  • 짧은 문장과 긴 문장에서 차이를 기록합니다.

Lab 4. Step 4 인터럽트 시나리오 실험하기

  • 에이전트가 말하는 도중 다시 말해 봅니다.
  • "멈춰", "그만"을 말해 응답이 차단되는지 확인합니다.
  • 어떤 환경에서 인터럽트가 잘 되고, 어떤 환경에서 덜 잘 되는지 정리합니다.

11. 확장 프로젝트 (Mini Project)

프로젝트 1. 음성 챗봇

  • 특정 주제의 FAQ를 답하는 음성 챗봇 만들기
  • 예: 학교 안내, 고객센터, 제품 설명

프로젝트 2. 회의 요약 에이전트

  • Step 3을 확장해 모든 발화를 저장
  • 회의 종료 후 요약 결과를 텍스트 파일로 정리

프로젝트 3. 음성 비서

  • Step 4에 Tool Calling 개념을 추가
  • 일정 조회, 날씨 조회, 메모 저장 같은 기능으로 확장

12. 핵심 요약

이번 강의의 핵심은 아래 5가지입니다.

  • Realtime API는 연결을 유지하면서 이벤트를 주고받는 방식입니다.
  • Step 1은 텍스트 기반 최소 연결 예제입니다.
  • Step 2는 음성 출력과 오디오 재생 구조를 이해하는 단계입니다.
  • Step 3은 마이크 입력과 사용자 전사를 분리해 이해하는 단계입니다.
  • Step 4는 실제 음성 대화에서 중요한 인터럽트와 상태 관리를 다룹니다.

한 줄로 정리하면 아래와 같습니다.

입력을 이해하고
→ 출력을 붙이고
→ 둘을 합친 뒤
→ 인터럽트까지 제어한다

13. 학습 체크리스트

항목 완료
requirements 설치 완료 [ ]
.env 설정 완료 [ ]
Step 1 실행 성공 [ ]
Step 2 실행 성공 [ ]
Step 3에서 사용자 전사 확인 [ ]
Step 4에서 에이전트 응답 확인 [ ]
WebSocket 흐름 설명 가능 [ ]
VAD 역할 설명 가능 [ ]
AudioPlayer 역할 설명 가능 [ ]
MicrophoneCapture 역할 설명 가능 [ ]
인터럽트 원리 설명 가능 [ ]
Hands-on 1개 이상 완료 [ ]
미니 프로젝트 아이디어 정리 [ ]

 

추가 확인 항목

  • [ ] 응답 중 사용자 끼어들기(인터럽트) 시나리오를 직접 검증했다
  • [ ] "멈춰", "그만" 명령으로 응답 차단 동작을 확인했다