Cascaded 음성봇 지연 줄이기: 스트리밍과 청크 처리로 TTFT 단축하기
사용자가 말을 마치고 마이크 표시가 꺼졌는데, 스피커에서는 한동안 아무 소리도 나오지 않습니다. 음성 AI 에이전트를 만들 때 가장 먼저 부딪히는 벽이 바로 이 어색한 침묵, 즉 "응답이 느리다"는 문제입니다. 음성 인식(STT), 언어 모델(LLM), 음성 합성(TTS)을 직렬로 잇는 Cascaded 구조에서는 각 단계의 지연이 차곡차곡 쌓여 첫 소리가 나오기까지의 공백을 만들어 냅니다. 어느 단계가 병목인지, 왜 누적되는지에 대한 진단은 01편에서 자세히 다루며, 이번 글은 그 진단을 전제로 구조를 바꾸지 않은 채 이 지연을 깎아내는 처방에 곧바로 들어갑니다. 즉 Cascaded 구조를 그대로 둔 채 동원하는 최적화 전략, 일종의 정교한 임시방편(workaround)을 해부합니다.
[시리즈] S2S·LLM 음성 에이전트 엔지니어링 63편 · 현재 1-3편
← 이전: 1-2 1초의 벽 — 지연이 UX를 결정하는 이유 / 다음 → 1-4 RAG 병목과 오디오 버퍼링 제어
- 1-1 AI 보이스봇이 답답한 이유 — Cascaded 레이턴시
- 1-2 1초의 벽 — 지연이 UX를 결정하는 이유
- 1-3 지연 줄이기 — 스트리밍·청크로 TTFT 단축 (현재 글)
- 1-4 RAG 병목과 오디오 버퍼링 제어
- 2-1 End-to-End S2S 작동 원리
전체 목차 펼치기 (63편)
Part 1 — 음성 AI가 느린 이유: 레이턴시 진단 (4편)
- 1-1 AI 보이스봇이 답답한 이유 — Cascaded 레이턴시
- 1-2 1초의 벽 — 지연이 UX를 결정하는 이유
- 1-3 지연 줄이기 — 스트리밍·청크로 TTFT 단축 (현재 글)
- 1-4 RAG 병목과 오디오 버퍼링 제어
Part 2 — 텍스트를 넘어: End-to-End S2S의 원리 (4편)
- 2-1 End-to-End S2S 작동 원리
- 2-2 OpenAI Realtime API vs 오픈소스 S2S
- 2-3 음성 토큰(Audio Token)이란
- 2-4 감정·톤 조절이 CS를 바꾸는 이유
Part 3 — 실시간 대화와 에이전트화: 인터럽트·MCP·도구 (5편)
- 3-1 실시간 인터럽트(끼어들기) 감지
- 3-2 실시간 음성에 MCP가 필요한 이유
- 3-3 스트리밍 중 MCP 초고속 데이터 룩업
- 3-4 통화 중 외부 API 도구 호출
- 3-5 실시간 세션 관리 — 통화 맥락·고객 데이터
Part 4 — 인프라·세션·보안: 프로덕션 백엔드 (5편)
- 4-1 동적 컨텍스트 윈도우 관리
- 4-2 WebRTC·WebSocket 초저지연 음성
- 4-3 비동기 이벤트 루프와 동시 콜 처리
- 4-4 트래픽 스파이크를 견디는 확장성 설계
- 4-5 실시간 음성 보안 — 암호화·PII 마스킹
Part 5 — 운영·품질·평가: 에이전틱 워크플로우와 신뢰성 (7편)
- 5-1 에이전틱 워크플로우 — 응대를 넘어 완결로
- 5-2 상용화의 한계와 트러블슈팅
- 5-3 보이스 에이전트 평가(Eval) 설계
- 5-4 관측성(Observability) — 어디서 끊기나
- 5-5 가드레일과 환각 제어
- 5-6 상담사 전환(Handoff) 설계
- 5-7 프롬프트 A/B 테스트와 튜닝
Part 6 — 음성 경험 고도화·실전 통합: 한국어·UX·전화망·엣지 (5편)
- 6-1 한국어 음성 AI의 난관 — 존댓말·코드스위칭
- 6-2 턴테이킹과 시맨틱 VAD
- 6-3 보이스 페르소나 디자인
- 6-4 SIP·PSTN 전화망 연동
- 6-5 온디바이스·엣지 S2S와 프라이버시
Part 7 — S2S 도입 전략·구매 의사결정 (4편)
Part 8 — LLM 콜봇 PoC 현장 (6편)
- 8-1 LLM 콜봇 PoC — 검증의 갭
- 8-2 STT 정확도 93% 목표가 부족한 이유
- 8-3 금융 콜봇에서 소수점이 중요한 이유
- 8-4 TTFT 1.5초, 콜봇 스트리밍 파이프라인 설계
- 8-5 콜봇 SLM: 30B vs 70B 트레이드오프
- 8-6 PoC와 본사업 사이의 성능 괴리
Part 9 — 상담어시스트: 상담사 화면을 바꾸는 실시간 AI (7편)
- 9-1 상담어시스트란 무엇인가 — 실시간 STT가 상담사 화면을 바꾸는 구조
- 9-2 상담어시스트의 뼈대 — STT·인텐트·KMS 파이프라인
- 9-3 추천의 근거 — KMS·RAG 지식 그라운딩
- 9-4 통화 중 놓친 고지를 잡는 실시간 필수 안내 체크
- 9-5 AI가 상담 품질을 자동 채점하는 법 — AutoQA
- 9-6 신입 상담사에게 어시스트가 더 필요한 이유
- 9-7 온프레미스 상담어시스트 — GPU 제약과 모델 선택
Part 10 — 직접 만든 롤플레이 챗봇: RAG 에이전트 구축기 (8편)
- 10-1 왜 롤플레이 챗봇을 직접 만들었나
- 10-2 전체 아키텍처 한 장
- 10-3 RAG 파이프라인과 로컬 임베딩
- 10-4 멀티 LLM 백엔드 스위칭
- 10-5 AI 고객 페르소나 프롬프트 엔지니어링
- 10-6 퀴즈 풀 사전생성
- 10-7 대화가 끝나면 채점한다
- 10-8 혼자 쓰는 챗봇의 현실
Part 11 — KMS 실무 — 지식관리시스템의 설계와 운영 현실 (8편)
절대 지연이 아니라 "체감 지연"을 줄인다
최적화를 시작하기 전에 개념부터 정리해야 합니다. 우리가 줄여야 하는 것은 두 가지로 나뉩니다. 하나는 절대 지연(end-to-end latency)으로, 입력 음성이 들어와 출력 음성이 모두 나오기까지의 총 시간입니다. 다른 하나는 체감 지연으로, 사용자가 말을 끝낸 순간부터 봇의 첫 소리가 들리기까지의 시간입니다.
절대 지연과 체감 지연을 나란히 놓고 보면, 대화 품질을 좌우하는 쪽은 거의 전적으로 후자입니다. 사람은 응답 전체가 빨리 끝나기를 바라는 게 아니라, "내 말이 닿았다"는 신호를 빨리 받고 싶어 합니다. 그래서 최적화의 본질은 처리 시간을 0으로 만드는 것이 아니라, 기다림을 잘게 쪼개 사용자가 눈치채지 못하게 숨기는 것에 가깝습니다. 아래에서 다룰 세 가지 기법은 모두 이 "숨기기"의 변주입니다.
캐싱과 중간 멘트, 이 두 장치를 나란히 놓고 보면 공통점이 하나 보입니다. 파이프라인 자체는 건드리지 않으면서 체감 대기만 낮춘다는 것인데, 음성봇을 실제로 돌려 보면 이 두 가지가 자주 함께 쓰입니다. 하나는 캐싱입니다. 자주 반복되는 질의는 매번 전체 파이프라인을 다시 돌리는 대신, 앞서 합성해 둔 결과를 재사용해 거의 즉답으로 내보냅니다. 다른 하나는 중간 멘트입니다. 봇이 "요약하면 ~ 맞으실까요? 확인 중입니다" 같은 짧은 응답을 먼저 내보내 침묵을 메우는 방식입니다. 두 장치 모두 실제 처리 시간을 줄이지는 못하지만, 사용자가 느끼는 대기는 크게 낮춥니다. 본질이 "숨기기"라는 점이 여기서 분명하게 드러납니다.
첫 번째 무기: 오디오 스트리밍 (배치를 버려라)
가장 큰 효과를 내는 것은 단계 사이를 배치(batch)가 아니라 스트림(stream)으로 잇는 것입니다. 배치 방식은 STT가 음성을 전부 받아 텍스트 한 덩어리를 내놓고, 그것을 LLM이 통째로 처리해 응답 전체를 만든 뒤, TTS가 다시 전체 문장을 음성으로 바꿉니다. 각 단계가 앞 단계의 "완성"을 기다리므로 지연이 그대로 누적됩니다.
스트리밍은 파이프에 물을 흘리듯 단계를 동시에 흐르게 만듭니다.
- 스트리밍 STT: 발화가 끝나기를 기다리지 않고, 인식 중간 결과(partial transcript)를 계속 흘려보냅니다. 사용자가 말하는 동안 이미 텍스트가 쌓이므로, 발화가 끝나는 순간 LLM에 던질 프롬프트가 거의 준비되어 있습니다.
- LLM 토큰 스트리밍: 응답 전체가 생성되기를 기다리지 않고, 첫 토큰부터 받아 즉시 다음 단계로 넘깁니다.
- 스트리밍 TTS: 텍스트가 일부만 들어와도 그 부분을 먼저 음성으로 합성해 재생합니다.
세 단계를 모두 스트리밍으로 연결하면, 직렬로 더해지던 시간이 상당 부분 겹쳐(overlap) 사라집니다. 사용자가 말을 마치기도 전에 STT는 끝나가고, LLM은 곧바로 토큰을 뱉기 시작하는 구조가 됩니다.
두 번째 무기: 문장 단위 청크 분할 (chunking)
스트리밍을 켜도 한 곳에서 병목이 생깁니다. 바로 LLM과 TTS의 경계입니다. LLM은 토큰을 흘려보내지만, TTS는 보통 의미 있는 단위가 있어야 자연스럽게 음성을 만듭니다. 토큰 하나하나를 그대로 합성하면 발음이 뚝뚝 끊기고 운율이 무너집니다.
토큰 하나마다 합성하는 방식과 응답 전체를 기다렸다 합성하는 방식을 나란히 놓고 보면, 그 중간 지점에서 실무가 흔히 택하는 절충안이 문장 단위 청크 분할입니다. LLM이 토큰을 흘려보내는 동안, 출력 버퍼를 지켜보다가 문장이 하나 완성되면 그 청크만 떼어 즉시 TTS로 넘기는 방식입니다. 응답 전체가 아니라 "첫 문장"만 완성되면 봇은 이미 말을 시작할 수 있습니다. 사용자가 듣는 첫 소리까지의 시간이 응답 길이와 무관해지는 것이 핵심입니다.
분할 기준은 보통 구두점(마침표, 물음표, 느낌표)과 한국어 종결·연결 어미, 그리고 최소·최대 길이를 함께 봅니다. 너무 짧으면 운율이 어색하고, 너무 길면 첫 소리가 늦어지므로 균형이 필요합니다.
buffer = ""
for token in llm_stream(): # LLM 토큰 스트림
buffer += token
# 토큰이 경계 토큰(마침표 등)에 도달했을 때만 청크 완성 여부를 검사
if is_sentence_boundary(buffer) or len(buffer) >= MAX_CHUNK:
tts_stream.send(buffer) # 완성된 청크를 즉시 TTS로
buffer = ""
if buffer:
tts_stream.send(buffer) # 남은 꼬리 청크 처리
이때 첫 청크는 짧게, 이후 청크는 다소 길게 잡는 전략도 자주 씁니다. 시작은 빨리 떼고, 흐름이 붙은 뒤에는 자연스러움을 챙기는 방식입니다.
세 번째 무기: TTFT 단축과 엔드포인팅
체감 지연의 출발선은 LLM이 첫 토큰을 내놓는 시점, 즉 TTFT(Time To First Token)입니다. 청크 분할이 아무리 빨라도 첫 토큰이 늦게 나오면 소용이 없으므로, 이 출발선 자체를 당기는 작업이 함께 가야 합니다.
TTFT는 주로 프롬프트 길이, 모델 크기, 그리고 캐시 활용 여부에 좌우됩니다. 실무에서 자주 쓰는 방법은 다음과 같습니다.
프롬프트 다이어트와 캐시
시스템 프롬프트와 대화 이력이 길수록 첫 토큰까지 시간이 늘어납니다. 매 턴 통째로 다시 넣기보다, 고정된 지시문은 캐시를 활용하고 가변 부분만 갱신하는 식으로 입력을 가볍게 유지합니다.
모델 선택의 분리
대화의 반사적인 응답(맞장구, 확인, 짧은 안내)에는 가볍고 빠른 모델을, 복잡한 추론이 필요한 구간에만 큰 모델을 쓰는 분리 전략도 효과적입니다. 모든 발화에 가장 큰 모델을 쓰는 것은 TTFT 관점에서 비효율적입니다.
엔드포인팅(endpointing)
사용자가 말을 끝낸 시점을 빨리 감지하는 것도 출발선을 당깁니다. 보통 음성 활동 감지(VAD)로 침묵을 측정해 발화 종료를 판단하는데, 이 침묵 임계값이 곧 트레이드오프입니다. 임계값을 짧게 잡으면 봇이 빨리 반응하지만, 사용자가 잠깐 숨을 고른 것을 "끝났다"고 오판해 말을 끊을 위험이 커집니다. 반대로 길게 잡으면 안전하지만 매 턴 그만큼 늦어집니다. 짧게 잡은 경우와 길게 잡은 경우를 나란히 놓고 보면 정답이랄 게 없어서, 결국 도메인 특성에 맞춰 이 값을 세밀하게 조정하는 쪽으로 수렴합니다.
STT 후처리와 연산 자원 배치
STT 뒤에는 보통 후처리 단계가 붙습니다. 인식된 텍스트의 숫자나 고유명사를 표준 형태로 정규화해 LLM 입력의 품질을 맞추는 작업입니다. CPU로 돌릴 때와 GPU로 돌릴 때를 나란히 놓고 비교해 보면, 이 후처리 단계는 CPU만으로도 실시간 성능을 충족하는 경우가 대부분이라 처음에는 CPU를 선택하는 쪽이 합리적입니다. 다만 전체 체감 지연을 한 단계 더 깎아야 하는 극단적 저지연 상황에서는, 정확도는 이미 충분한데도 같은 모델을 GPU로 올려 처리 시간을 더 줄이는 선택이 반복됩니다. "정확도 때문이 아니라 레이턴시를 더 낮추려고 GPU로 전환"하는, 자원 배치 자체가 하나의 최적화 레버가 되는 셈입니다.
정리: 최적화 스택을 합치면
각 기법은 지연의 서로 다른 구간을 공략합니다. 한두 개만 적용해도 효과가 있지만, 함께 쌓을 때 비로소 체감 지연이 크게 줄어듭니다.
| 기법 | 주로 줄이는 구간 | 핵심 트레이드오프 |
|---|---|---|
| 오디오 스트리밍 | 단계 간 대기(직렬 누적) | 구현 복잡도 증가, 부분 결과 처리 필요 |
| 문장 청크 분할 | LLM→TTS 경계의 대기 | 청크가 짧으면 운율 저하 |
| TTFT 단축 | LLM 첫 토큰까지의 시간 | 경량 모델 사용 시 품질 저하 가능 |
| 엔드포인팅 튜닝 | 발화 종료 감지 지연 | 너무 짧으면 사용자 말 끊김 |
이 스택을 잘 조합하면, 구조를 바꾸지 않고도 체감 지연을 자연스러운 대화에 근접한 1초 안팎까지도 끌어내릴 수 있습니다(다만 실제 도달 가능한 수치는 워크로드, 모델, 네트워크 환경에 따라 달라집니다). 많은 상용 음성봇이 바로 이 최적화의 산물입니다.
결론: 그래도 못 넘는 Cascaded의 태생적 한계
그러나 이 모든 기법은 한계를 없애는 것이 아니라 미루는 기술입니다. Cascaded 구조에는 최적화로 넘을 수 없는 벽이 셋 있습니다.
첫째, 직렬 의존성입니다. STT, LLM, TTS는 본질적으로 순서가 있고, 각 단계가 가진 최소 처리 시간의 합은 아무리 겹쳐도 0으로 수렴하지 않습니다. 둘째, 텍스트 병목입니다. 음성을 텍스트로 바꾸는 순간 말투, 머뭇거림, 감정, 운율 같은 정보가 사라집니다. TTS는 그 잃어버린 뉘앙스를 텍스트만 보고 복원할 수 없습니다. 셋째, 끼어들기의 벽입니다. 사용자가 봇의 말을 끊는 끼어들기(barge-in)는 재생 중인 TTS를 중단하고 파이프라인을 재시작하는 방식으로 Cascaded 구조에서도 흔히 구현됩니다. 다만 사람처럼 듣는 동시에 말하는 진성 전이중(full-duplex) 대화, 즉 봇이 말하는 와중에 사용자 발화를 실시간으로 받아 반응하는 형태는 단계가 직렬로 묶인 구조에서 매끄럽게 구현하기 어렵습니다.
결국 최적화는 "충분히 빠른 척"을 정교하게 만드는 작업입니다. 더 근본적인 해결, 즉 음성에서 음성으로 직접 가는 End-to-End Speech-to-Speech 구조는 다음 단계의 이야기입니다.
자주 묻는 질문 (FAQ)
Q. 스트리밍만 켜면 지연이 사라지나요?
아니요. 스트리밍은 단계 간 대기를 겹치게 할 뿐, 각 단계의 최소 처리 시간은 남습니다. 특히 LLM의 첫 토큰 지연과 네트워크 왕복은 스트리밍만으로 해결되지 않으므로, TTFT 단축과 함께 가야 효과가 납니다.
Q. 청크를 너무 잘게 쪼개면 어떻게 되나요?
첫 소리는 빨라지지만 음성의 운율이 끊기고 부자연스러워집니다. 한 단어, 한 음절 단위로 합성하면 억양이 뭉개지므로, 첫 청크만 짧게 떼고 이후는 문장 단위로 묶는 균형이 일반적입니다.
Q. 스트리밍 STT는 인식 정확도가 떨어지지 않나요?
중간 결과(partial)는 확정 전이라 흔들릴 수 있습니다. 그래서 보통 부분 결과로는 미리 준비만 해 두고, 발화 종료 시점의 최종 확정 텍스트로 LLM 입력을 갱신해 정확도와 속도를 모두 챙깁니다. 덧붙이면 스트리밍 STT의 세부 동작은 벤더마다 다르고 업데이트도 잦아서, 2026년 6월 기준인 이 정리와 함께 각 벤더 문서를 확인하는 편이 안전합니다.
최적화는 Cascaded의 한계를 미루는 기술이지, 없애는 기술이 아닙니다.
댓글
댓글 쓰기