,

[음성 파이프라인] STT → LLM → TTS 전체 latency 분해 — 4구간 절단법

결론 먼저 — 음성 파이프라인 latency는 4구간으로 쪼개야 줄일 수 있다

구간이름대표 단축 전략
1VAD → STT 종료 (사용자 발화 끝 인식)endpointing 임계 튜닝, VAD 별도 분리
2STT 결과 → LLM 입력partial result 활용, prompt 캐싱
3LLM 첫 토큰까지Flash 계열 모델, 짧은 system prompt
4TTS 첫 오디오 chunk → 재생 시작스트리밍 합성, 짧은 첫 문장

“전체 1.6초”라는 한 숫자만 보면 어디부터 줄일지 알 수 없다. 4구간으로 절단하면 단축 전략이 곧바로 보인다.

왜 분해해야 하는가

음성 대화의 turn-taking 자연성 임계는 약 1초 안팎이다 (텍스트 대비 매우 빡빡하다). 사용자 발화가 끝난 시점부터 응답 음성이 시작되기까지의 총 latency가 그 임계를 넘으면 시스템은 “느리다”는 인상을 준다.

그런데 단일 숫자만 측정하면 “어디서 줄여야 하나”가 보이지 않는다. 4구간 절단법을 쓰면 각 구간이 전체 latency에 기여하는 비율이 드러나고, 가장 비싼 구간부터 손대는 우선순위가 자연스럽게 잡힌다.

구간 1 — VAD/Endpointing 지연

사용자가 말을 끝낸 시점과 STT 시스템이 “끝났다”고 판단하는 시점 사이의 지연이다. 이 구간이 의외로 가장 크다. 일반적으로 STT 엔진은 안전하게 잡으려고 침묵 약 0.5–1초가 지속되어야 발화 종료로 본다.

  • 단축 전략: endpointing 임계를 도메인에 맞게 짧게 (예: 600 → 350ms)
  • 주의: 너무 짧게 하면 사용자가 말 중간에 짧은 호흡을 해도 “끝났다”고 잘못 판단
  • 대안: 별도 VAD(Voice Activity Detection)를 두고, 발화 종료 신호를 LLM 호출 직전에만 활용

구간 2 — STT 결과 → LLM 입력

STT의 최종 결과(final transcript)를 받아 LLM에 prompt를 구성해 보내는 사이의 지연이다. 보통 수십 ms 이내지만, prompt가 길거나 외부 함수 호출(retrieval 등)이 들어가면 빠르게 백 ms 단위로 늘어난다.

  • 단축 전략: STT의 partial result를 받아 LLM prompt 구성을 미리 시작 (단, final 직전에 갱신)
  • system prompt와 도구 정의는 캐싱 가능한 부분에 분리 (Anthropic prompt caching, Vertex prompt caching 등)
  • retrieval이 필요하면 비동기로 미리 시작

구간 3 — LLM 첫 토큰까지

LLM 호출 시점부터 첫 토큰이 스트리밍으로 돌아오는 시점까지의 지연(time-to-first-token, TTFT). 이 구간이 음성 latency의 핵심이다.

  • 단축 전략: Flash 계열 / 작은 모델로 우선 응답, 필요시 Pro로 라우팅 (이전 글 참고)
  • system prompt와 few-shot 예시를 짧게 — TTFT는 입력 토큰 수에 비례하기 쉽다
  • prompt caching이 가능하면 동일한 system prompt에 대한 TTFT가 큰 폭으로 줄어든다

구간 4 — TTS 첫 오디오 chunk까지

LLM 첫 토큰이 도착해 TTS에 보내고, TTS가 첫 오디오 chunk를 돌려주기까지의 지연. TTS API가 스트리밍 합성을 지원하지 않으면 전체 응답 문장이 완성될 때까지 기다려야 하므로 이 구간이 폭발한다.

  • 단축 전략: 스트리밍 TTS 사용 (Google Cloud TTS의 streaming synthesize 등)
  • LLM이 만드는 첫 한 문장이 짧도록 prompt에 가이드 — “첫 응답은 한 문장으로”
  • 특정 정형 응답(“네”, “잠시만요” 등)은 TTS 결과를 캐싱해 즉시 재생

측정 — 4구간 latency를 한 줄에 찍는 방법

각 구간 경계에 서버 시간 timestamp를 찍고, 한 turn이 끝날 때 4개 차이값을 로그로 남기면 그날부터 분포가 쌓인다. 1주일치만 모이면 어느 구간이 평균 latency의 50% 이상을 잡고 있는지 거의 결정적으로 드러난다.

주의: p50만 보면 안 된다. 음성 대화 UX는 p95에 의해 결정된다. 가끔 한 번 2초 침묵이 발생하면 사용자는 그 한 번을 기억한다. p95 측정 대상으로 삼고, p95가 1초 안쪽으로 들어오는 구간 분포를 만드는 게 목표다.

개인 메모 — “어디서 줄일까”라는 질문이 가능해진 순간

처음 음성 파이프라인 데모를 짰을 때, 전체 latency만 보고 “느리다”는 결론을 내렸다. 그래서 모델만 바꿔보고, prompt만 줄여보고, TTS만 다른 걸로 갈아끼워 보면서 한 번에 한 변수만 건드렸다. 그래도 차이가 잘 보이지 않았던 이유가, 사실은 가장 긴 구간이 endpointing이었기 때문이라는 걸 시간이 한참 지나서야 알았다.

4구간 절단을 시작한 뒤로 의사결정이 빨라졌다. “p95 1.6초 중 endpointing이 0.7초”라는 데이터 한 줄만 손에 쥐면 어디를 만질지가 자명해진다. 측정 분해를 늘리는 게 모델을 바꾸는 것보다 효과가 컸던 적이 그때 처음이었다.

참고

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다