음성 비서 파이프라인 구축 — Phase 6 Capstone(Build a Voice Assistant Pipeline — The Phase 6 Capstone)
Lesson 01-11에서 다룬 모든 내용을 하나로 이어 붙입니다. 듣고, 추론하고, 다시 말하는 음성 비서(voice assistant)를 만듭니다. 2026년 기준으로 이것은 더 이상 연구 문제가 아니라 풀린 엔지니어링 문제이지만, 통합 단계의 세부 설계가 실제 서비스 배포 여부를 가릅니다.
유형: Build
언어: Python
선수 강의: Phase 6 · 04, 05, 06, 07, 11; Phase 11 · 09 (Function Calling); Phase 14 · 01 (Agent Loop)
예상 시간: 약 120분
학습 목표
음성 비서 파이프라인을 구성하는 일곱 가지 구성요소(component)를 end-to-end로 설명할 수 있습니다.
음성 활동 감지(VAD), 스트리밍 음성 인식(streaming STT), 도구 호출(tool calling)이 가능한 거대 언어 모델(LLM), 스트리밍 음성 합성(streaming TTS), 끼어들기 처리기(interruption handler)를 하나의 흐름으로 연결할 수 있습니다.
지연 시간(latency) 목표와 품질(quality) 목표를 단계별로 나누어 설정할 수 있습니다.
개인정보(PII) 로그 보존, 끼어들기(barge-in), 환각(hallucination), 호출어(wake word) 처리 등 운영 환경에서 발생하는 위험을 식별할 수 있습니다.
문제
다음과 같은 end-to-end 비서를 만듭니다.
마이크 입력(16 kHz, mono)을 캡처합니다.
사용자 발화의 시작과 끝을 감지합니다.
스트리밍 방식으로 텍스트로 변환(transcribe)합니다.
변환된 텍스트(transcript)를 타이머·날씨·캘린더 같은 도구(tool)를 호출할 수 있는 거대 언어 모델(LLM)에 전달합니다.
LLM이 생성하는 텍스트를 음성 합성(TTS)으로 스트리밍합니다.
합성된 오디오를 사용자에게 재생합니다.
사용자가 응답 도중 끼어들면(interrupt) 즉시 멈춥니다.
지연 시간 목표는 노트북 CPU 환경에서 사용자의 발화가 끝난 시점부터 첫 TTS 오디오 바이트가 나오기까지 800 ms 이내입니다. 품질 목표는 단어 누락이 없고, 무음 구간에서 환각된 자막이 없으며, 음성 복제(voice cloning) 누출이 없고, 프롬프트 인젝션(prompt injection)이 성공하지 않는 것입니다.
사전 테스트
2문제 · 이 강의를 시작하기 전에 얼마나 알고 있는지 확인해보세요
1.음성 비서 파이프라인(voice assistant pipeline)은 일곱 가지 구성요소를 종단간(end-to-end)으로 연결합니다. 다음 중 핵심 흐름을 올바르게 식별한 것은 무엇인가요?
2.음성 비서가 VAD 트리거 전 200-400 ms의 오디오를 저장하는 프리롤(pre-roll) 버퍼가 필요한 이유는 무엇인가요?
0/2 답변 완료
개념
일곱 가지 구성요소
오디오 캡처(Audio capture). 마이크에서 16 kHz mono로, 20 ms 단위 청크(chunk)로 받습니다. Python에서는 보통 sounddevice를 쓰고, 운영 환경에서는 플랫폼 네이티브 API인 AudioUnit/ALSA/WASAPI를 사용합니다.
음성 활동 감지(VAD; Lesson 11). Silero VAD를 임곗값(threshold) 0.5, 최소 발화 길이 250 ms, 무음 유지 시간(silence hang-over) 500 ms로 운용합니다. 발화의 시작과 끝 신호를 전달합니다.
스트리밍 음성 인식(Streaming STT; Lesson 4-5). Whisper-streaming, Parakeet-TDT, 또는 Deepgram Nova-3(API)을 사용합니다. 부분 변환 결과(partial)와 최종 변환 결과(final)를 함께 내보냅니다.
도구 호출이 가능한 LLM(LLM with tool calling). GPT-4o, Claude 3.5, Gemini 2.5 Flash 등이 후보입니다. 도구는 JSON 스키마로 정의하고, 토큰을 스트리밍 방식으로 받습니다.
스트리밍 음성 합성(Streaming TTS; Lesson 7). Kokoro-82M(공개 모델 중 가장 빠름)이나 Cartesia Sonic(상용)을 사용합니다. LLM 토큰이 20개 정도 누적되면 TTS를 시작합니다.
재생(Playback). 스피커로 출력합니다. 대역폭이 좁은 네트워크에서는 Opus 코덱으로 인코딩합니다.
끼어들기 처리기(Interruption handler). TTS 재생 도중 VAD가 발화 시작을 감지하면 재생을 멈추고, LLM 생성을 취소하고, STT를 다시 시작합니다.
반드시 만나게 되는 세 가지 실패 양상
첫 단어 잘림(First-word clip). VAD가 한 박자 늦게 시작해 사용자의 "hey"가 잘립니다. 시작 임곗값을 0.5가 아니라 0.3에서 출발하세요.
응답 중 끼어들기 혼란(Mid-response interrupt confusion). 사용자가 끼어든 뒤에도 LLM이 계속 토큰을 생성해서, 비서가 사용자 위에 겹쳐 말합니다. VAD 신호를 LLM 취소 경로에 직접 연결해야 합니다.
무음 환각(Silence hallucination). Whisper가 워밍업 단계의 무음 프레임에 대해 "Thanks for watching" 같은 자막을 환각하여 내놓습니다. 항상 VAD로 입력을 게이팅(gating)해야 합니다.
2026년 운영 환경 참조 스택(2026 production reference stacks)
스택(Stack)
지연 시간(Latency)
라이선스(License)
비고(Notes)
LiveKit + Deepgram + GPT-4o + Cartesia
350-500 ms
상용 API
2026년 산업 표준 기본 구성
Pipecat + Whisper-streaming + GPT-4o + Kokoro
500-800 ms
대부분 오픈소스
직접 구축에 적합
Moshi(full-duplex)
200-300 ms
CC-BY 4.0
단일 모델, 다른 아키텍처(architecture). Lesson 15에서 다룸
Vapi / Retell(managed)
300-500 ms
상용
가장 빠르게 출시 가능, 커스터마이징(customization) 제약
Whisper.cpp + llama.cpp + Kokoro-ONNX
offline
오픈소스
프라이버시(privacy) / 엣지(edge) 환경
직접 만들기
Step 1: 청크 단위 마이크 캡처(의사 코드)
import sounddevice as sd
defmic_stream(chunk_ms=20, sr=16000):
q = queue.Queue()
defcb(indata, frames, time, status):
q.put(indata.copy().flatten())
with sd.InputStream(channels=1, samplerate=sr, blocksize=int(sr * chunk_ms/1000), callback=cb):
whileTrue:
yield q.get()
code/main.py는 실제 하드웨어 없이도 파이프라인의 전체 모양을 볼 수 있도록, 일곱 가지 구성요소를 stub 모델로 연결한 실행 가능한 시뮬레이션(runnable simulation)입니다. 실제 구현에서는 stub을 다음 구성요소로 교체합니다.
silero-vad (pip install silero-vad)
deepgram-sdk 또는 openai-whisper
openai(gpt-4o) 또는 anthropic
kokoro 또는 cartesia
입출력용 sounddevice
흔한 함정
개인정보(PII)를 영구 로깅(logging). 전체 턴의 오디오는 대부분의 관할권(jurisdiction)에서 개인정보입니다. 30일 보존 정책과 저장 시 암호화(encrypted at rest)를 적용하세요.
끼어들기(barge-in) 미지원. 사용자는 반드시 중간에 끼어듭니다. 비서는 즉시 발화를 멈춰야 합니다.
블로킹 TTS. 동기식(synchronous) TTS는 이벤트 루프(event loop)를 막습니다. 비동기(async) 또는 별도 스레드(thread)를 사용하세요.
도구 호출 오류 처리 부재. 도구는 실패하기 마련입니다. LLM에 오류를 돌려주고 한 번 재시도(retry)한 뒤, 그래도 실패하면 부드럽게 기능을 축소(graceful degrade)해야 합니다.
과도한 환각 필터. 너무 강하게 거르면 비서가 "I can't help with that."만 반복합니다. 너무 느슨하면 아무 말이나 합니다. 별도 평가 세트(held-out set)로 보정(calibrate)하세요.
호출어(wake-word) 옵션 부재. 항상 듣기(always-listening)는 프라이버시 책임 문제입니다. Porcupine이나 openWakeWord 같은 호출어 게이트를 추가하세요.
산출물 만들기
outputs/skill-voice-assistant-architect.md로 저장합니다. 예산, 규모, 언어, 컴플라이언스(compliance) 제약을 받아서 풀스택(full-stack) 설계 스펙(spec)을 만들어 주는 스킬(skill)입니다.
연습문제
쉬움.code/main.py를 실행합니다. stub 모듈로 하나의 턴(turn)을 end-to-end로 시뮬레이션하고 단계별 지연 시간을 출력합니다.
중간. STT stub을 미리 녹음된 .wav 파일에 대해 동작하는 실제 Whisper 모델로 교체합니다. 단어 오류율(WER; Word Error Rate)과 end-to-end 지연 시간을 측정합니다.
어려움. 도구 호출을 추가합니다. get_weather(아무 API나)와 set_timer를 구현하고, LLM이 도구를 거치도록 라우팅(routing)한 다음, 사용자가 "set a 5 minute timer"라고 말했을 때 올바른 함수가 호출되고 합성된 응답이 이를 확인해주는지 검증합니다.
핵심 용어
용어
흔한 설명
실제 의미
턴(Turn)
사용자 + 비서의 한 번의 왕복
VAD로 경계가 잡힌 사용자 발화 하나 + 그에 대응되는 LLM-TTS 응답 하나입니다.
끼어들기(Barge-in)
인터럽션(interruption)
사용자가 비서가 말하는 도중 발화를 시작하고, 비서가 즉시 멈추는 상황입니다.
호출어(Wake word)
"Hey assistant" 같은 짧은 단어
짧은 키워드 검출기(detector)입니다. Porcupine, Snowboy, openWakeWord 등이 있습니다.
종료점 결정(End-pointing)
턴이 끝나는 지점
VAD와 최소 무음 시간으로 사용자가 발화를 끝냈는지 판단합니다.
프리롤(Pre-roll)
발화 직전 버퍼(pre-speech buffer)
VAD가 트리거되기 전 200-400 ms 분량 오디오를 보관해 첫 단어 잘림을 방지합니다.
도구 호출(Tool call)
함수 호출(function invocation)
LLM이 JSON을 내보내면 런타임이 해당 함수에 디스패치(dispatch)하고, 결과를 다시 루프 안으로 돌려보냅니다.