배치 파이프라인(Batch pipeline)은 파일을 처리합니다. 실시간 파이프라인(Real-time pipeline)은 다음 20밀리초가 도착하기 전에 현재 20밀리초를 처리합니다. 대화형 인공지능(Conversational AI), 방송 스튜디오(broadcast studio), 텔레포니 봇(telephony bot)은 모두 이 지연 시간 예산(latency budget) 위에서 생사가 갈립니다.
유형: Build
언어: Python, Rust
선수 강의: Phase 6 · 02 (Spectrograms), Phase 6 · 04 (ASR), Phase 6 · 07 (TTS)
예상 시간: 약 75분
학습 목표
대화형 인공지능(Conversational AI)의 종단 간(end-to-end) 지연 시간 예산을 단계별로 나누어 봅니다.
링 버퍼(Ring buffer), 음성 활동 감지(VAD), 스트리밍 자동 음성 인식(streaming ASR), 인터럽션(interruption), 지터 버퍼(jitter buffer)를 설명할 수 있습니다.
Python 오디오 콜백(audio callback)에서 전역 인터프리터 락(GIL)과 메모리 할당(allocation)을 조심해야 하는 이유를 이해합니다.
실시간 음성 앱(real-time voice app)에 필요한 전송(transport)과 관측 가능성(observability) 도구를 고를 수 있습니다.
문제
살아 있는 것처럼 느껴지는 음성 비서(voice assistant)를 만들고 싶다고 가정합니다. 사람의 대화 차례 전환(turn-taking) 지연 시간은 침묵에서 응답까지(silence-to-response) 기준 약 230밀리초입니다. 500밀리초를 넘어가면 로봇처럼 어색하게 느껴지고, 1500밀리초를 넘어가면 망가진 시스템처럼 느껴집니다. 2026년 기준 전체 듣기 → 이해 → 응답 → 말하기(hear → understand → respond → speak) 루프의 예산은 다음과 같습니다.
단계(Stage)
예산(Budget)
Mic → buffer
20 ms
VAD
10 ms
ASR (streaming)
150 ms
LLM (first token)
100 ms
TTS (first chunk)
100 ms
Render → speaker
20 ms
**Total
약 400 ms**
Moshi(Kyutai, 2024)는 전이중(full-duplex) 200밀리초를 기록했습니다. GPT-4o-realtime(2024)은 약 320밀리초입니다. 2022년의 캐스케이드 파이프라인(cascaded pipeline)은 2500밀리초로 배포되곤 했습니다. 10배 개선은 세 가지 기법에서 비롯되었습니다. (1) 모든 단계에서의 스트리밍(streaming), (2) 부분 결과(partial result) 기반의 비동기 파이프라이닝(asynchronous pipelining), (3) 중단 가능한 생성(interruptible generation)입니다.
사전 테스트
2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요
1.대화형 인공지능(Conversational AI) 음성 비서에서, 사용자가 말을 끝낸 시점부터 첫 오디오 응답 바이트까지의 총 지연 시간 예산은 약 400 ms입니다. 이 예산을 지키는 것이 왜 중요한가요?
2.실시간 오디오 처리 파이프라인에서 링 버퍼(ring buffer)의 목적은 무엇인가요?
0/2 답변 완료
개념
프레임 / 청크 / 윈도우(Frame / chunk / window). 실시간 오디오는 고정 크기 블록 단위로 흐릅니다. 흔한 선택은 20밀리초입니다. 16 kHz에서는 한 프레임이 320 샘플입니다. 다운스트림(downstream)의 모든 단계는 이 박자(cadence)를 따라잡아야 합니다.
링 버퍼(Ring buffer). 고정 크기의 원형 버퍼(circular buffer)입니다. 생산자 스레드(producer thread)가 새 프레임을 쓰고, 소비자 스레드(consumer thread)가 읽어 갑니다. 핫 패스(hot path)에서 메모리 할당이 일어나지 않도록 막아 줍니다. 크기는 대략 최대 지연 시간 × 샘플레이트(sample-rate)로 잡습니다. 2초 분량의 16 kHz 링 버퍼는 32,000 샘플입니다.
음성 활동 감지(Voice Activity Detection; VAD). 아무도 말하지 않을 때 다운스트림 처리를 차단(gate)합니다. Silero VAD 4.0(2024)은 CPU에서 30밀리초 프레임당 1밀리초 미만으로 실행됩니다. webrtcvad는 더 오래된 대안입니다.
스트리밍 자동 음성 인식(Streaming ASR). 오디오가 도착하는 동안 부분 전사(partial transcript)를 내보내는 모델입니다. 스트리밍 모드의 Parakeet-CTC-0.6B(NeMo, 2024)는 320밀리초 지연에서 2~5% 단어 오류율(WER)을 냅니다. Whisper-Streaming(Macháček et al., 2023)은 Whisper를 청크 단위로 잘라 약 2초 지연의 준스트리밍(near-streaming)을 만듭니다.
인터럽션(Interruption). 비서가 말하는 도중에 사용자가 말을 시작하면 (a) 끼어들기(barge-in)를 감지하고, (b) 음성 합성(TTS)을 멈추고, (c) 남은 대규모 언어 모델(LLM) 출력을 버려야 합니다. 이 모든 일이 100밀리초 안에 일어나야 합니다. 그렇지 않으면 사용자는 비서가 자신의 말을 듣지 못한다고 느낍니다.
지터 버퍼(Jitter buffer). 네트워크 패킷은 순서가 뒤바뀌거나 늦게 도착할 수 있습니다. 지터 버퍼는 이를 재정렬하고 부드럽게 만들어 줍니다. 너무 작으면 들리는 공백(audible gap)이 생기고, 너무 크면 지연이 커집니다. 일반적으로 60~80밀리초를 둡니다.
흔한 함정(gotcha)
스레드 경합(Thread contention). Python의 전역 인터프리터 락(GIL)에 무거운 모델까지 더해지면 오디오 스레드가 굶주릴 수 있습니다. C 콜백(C-callback) 기반 오디오 라이브러리(sounddevice, PortAudio)를 사용하고, 핫 패스에서는 Python을 최소화합니다.
샘플레이트 변환 지연(Sample-rate conversion latency). 파이프라인 안에서 리샘플링(resampling)을 하면 5~20밀리초가 추가됩니다. 미리(upfront) 리샘플링을 하거나, 무지연 리샘플러(zero-latency resampler, 예: PolyPhase, soxr_hq)를 사용합니다.
TTS 프라이밍(TTS priming). Kokoro처럼 빠른 TTS도 첫 요청에는 100~200밀리초의 워밍업(warm-up)이 발생합니다. 실제 첫 차례가 시작되기 전에 더미 호출(dummy run)로 모델을 데우고 캐시(cache)에 올려 둡니다.
에코 캔슬링(Echo cancellation). 음향 반향 제거(AEC)가 없으면 TTS 출력이 마이크로 다시 들어가서 봇 자기 목소리로 음성 인식(ASR)이 트리거됩니다. WebRTC AEC3가 오픈 소스 표준입니다.
# Parakeet-CTC-0.6B streaming via NeMofrom nemo.collections.asr.models import EncDecCTCModelBPE
asr = EncDecCTCModelBPE.from_pretrained("nvidia/parakeet-ctc-0.6b")
# chunk_ms=320 ms, look_ahead_ms=80 msfor chunk in audio_stream():
partial_text = asr.transcribe_streaming(chunk)
print(partial_text, end="\r")
Step 4: 인터럽션 핸들러
classDialog:
def__init__(self):
self.tts_task = Nonedefon_user_speech(self, frame):
ifself.tts_task andnotself.tts_task.done():
self.tts_task.cancel() # barge-in# then feed to streaming ASRdefon_final_user_utterance(self, text):
self.tts_task = asyncio.create_task(self.reply(text))
asyncdefreply(self, text):
asyncfor tts_chunk in llm_then_tts(text):
speaker.write(tts_chunk)
핵심은 비동기 입출력(async I/O)과 취소 가능한(cancellable) TTS 스트리밍입니다. 오디오 트랙에 대해 WebRTC peerconnection.stop()을 호출하는 방식이 정석(canonical)입니다.
사용하기
2026년 기준 스택은 다음처럼 고릅니다.
계층(Layer)
선택(Pick)
Transport
LiveKit (WebRTC) 또는 Pion (Go)
VAD
Silero VAD 4.0
Streaming ASR
Parakeet-CTC-0.6B 또는 Whisper-Streaming
LLM first-token
Groq, Cerebras, vLLM-streaming
Streaming TTS
Kokoro 또는 ElevenLabs Turbo v2.5
Echo cancel
WebRTC AEC3
End-to-end native
OpenAI Realtime API 또는 Moshi
흔한 함정
안전을 이유로 500밀리초 버퍼링. 버퍼 자체가 지연의 하한(latency floor)이 됩니다. 가능한 한 줄여야 합니다.
스레드를 고정(pinning)하지 않음. 오디오 콜백이 UI 스레드보다 우선순위가 낮은 스레드에서 돌면, 부하 상황에서 끊김(glitch)이 발생합니다.
TTS 청크가 너무 작음. 200밀리초 미만의 청크는 보코더 아티팩트(vocoder artifact)가 귀에 들리게 만듭니다. 320밀리초 청크가 스위트 스폿(sweet spot)입니다.
지터 버퍼 없음. 실제 네트워크는 지터(jitter)가 심합니다. 부드럽게 다듬는 단계가 없으면 팝 노이즈(pop noise)가 납니다.
단발성(single-shot) 오류 처리. 오디오 파이프라인은 크래시에 강해야(crash-proof) 합니다. 예외 하나가 세션 전체를 죽이면 안 됩니다.
산출물 만들기
원문은 outputs/skill-realtime-designer.md로 저장하라고 안내합니다. 현재 이 강의의 실제 산출물 파일은 outputs/skill-realtime-pipeline.md입니다. 종단 간 지연 목표에 맞춰 각 단계별 지연 예산을 가진 실시간 오디오 파이프라인을 설계하는 스킬(skill)입니다.
연습문제
쉬움.code/main.py를 실행해 봅니다. 링 버퍼와 에너지 기반 VAD를 시뮬레이션하고, 가짜(fake) 10초 스트림에 대한 각 단계의 지연 시간을 출력합니다.
중간.sounddevice를 사용해 마이크 입력을 20밀리초 프레임 단위로 처리하는 패스스루(passthrough) 루프를 만들고, 각 프레임의 VAD 상태를 출력합니다.
어려움.aiortc로 전이중(full duplex) 에코 테스트를 만듭니다. browser → WebRTC → Python → WebRTC → browser 경로를 연결하고, 1 kHz 펄스(pulse)로 화면-대-화면(glass-to-glass) 지연 시간을 측정합니다.
핵심 용어
용어
흔한 설명
실제 의미
링 버퍼(Ring buffer)
원형 큐(circular queue)
오디오 프레임용 고정 크기, 락 프리(lock-free) 또는 단일 생산자-단일 소비자(SPSC-locked) 선입선출(FIFO) 큐.
음성 활동 감지(VAD)
침묵 게이트(silence gate)
음성(speech)과 비음성(non-speech)을 표시하는 모델 또는 휴리스틱(heuristic).
스트리밍 ASR
실시간 음성-텍스트 변환(STT)
오디오가 도착하는 동안 부분 텍스트를 내보내며 제한된 룩어헤드(lookahead)를 가짐.