결론 먼저 — 음성 파이프라인 latency는 4구간으로 쪼개야 줄일 수 있다
| 구간 | 이름 | 대표 단축 전략 |
|---|---|---|
| 1 | VAD → STT 종료 (사용자 발화 끝 인식) | endpointing 임계 튜닝, VAD 별도 분리 |
| 2 | STT 결과 → LLM 입력 | partial result 활용, prompt 캐싱 |
| 3 | LLM 첫 토큰까지 | Flash 계열 모델, 짧은 system prompt |
| 4 | TTS 첫 오디오 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초”라는 데이터 한 줄만 손에 쥐면 어디를 만질지가 자명해진다. 측정 분해를 늘리는 게 모델을 바꾸는 것보다 효과가 컸던 적이 그때 처음이었다.
답글 남기기