[Voice AI] STT 스트리밍: partial vs final, endpointing, gRPC/WS
STT streaming, partial transcript, final transcript, endpointing, time-to-final, interim result, gRPC streaming STT, WebSocket STT
정의
스트리밍 STT = 오디오 청크 가 들어올 때마다 부분 결과 (partial) 를 즉시 반환 + 발화 종료 시 최종 결과 (final) 확정.
IMPORTANT
대화형 음성 AI 의 첫 단계. 종단 지연 1초 미만 의 시작점.
partial vs final
sequenceDiagram
autonumber
participant Mic
participant STT
participant App
Mic->>STT: chunk 1 (100ms)
STT-->>App: partial: "안녕"
Mic->>STT: chunk 2
STT-->>App: partial: "안녕하세"
Mic->>STT: chunk 3
STT-->>App: partial: "안녕하세요"
Mic->>STT: chunk 4 (침묵)
Note over STT: endpoint 감지
STT-->>App: FINAL: "안녕하세요"
App->>App: LLM 호출
| partial (interim) | final | |
|---|---|---|
| 의미 | 현재 추정 (변경 가능) | 확정 |
| 빈도 | 매 100-300ms | endpointing 후 1회 |
| 정확도 | 낮음 (변경됨) | 높음 |
| 사용 | UI 라이브 자막 | LLM 호출, action |
CAUTION
partial 로 LLM 호출 금지. 반복 호출 + 변경된 텍스트 → 낭비 + 일관성 깨짐.
Endpointing (발화 종료 감지)
사용자 침묵 → 얼마나 기다린 후 final?
| 전략 | 의미 |
|---|---|
| 에너지 기반 VAD | 침묵 N ms 후 종료 |
| 신경망 VAD (Silero) | 음성/비음성 확률 기반 |
| 시맨틱 endpointing | 문장 의미 + VAD (LiveKit 0.4) |
| 고정 timeout | 무조건 N초 후 |
일반: 침묵 500-800ms 후 final
short: 200-400ms (빠른 대화)
long: 1000-1500ms (사용자가 멈춤 많음)
시맨틱 endpointing 의 가치
❌ 순수 VAD: "내 번호는 010-..." [500ms 침묵] → final "내 번호는 010" → 사용자 끊김!
✓ 시맨틱: "내 번호는 010-1234-5678" 까지 *기다림* (전화번호 패턴 인식)
LiveKit, Pipecat 의 자체 모델 이 오디오 + 텍스트 보고 ~300ms 까지 단축.
프로토콜: WebSocket vs gRPC
flowchart LR
Client[Client] -->|WebSocket binary frames| Server1["STT (WebSocket)"]
Client -->|gRPC stream| Server2["STT (gRPC)"]
Client -->|HTTP/2 + chunks| Server3["STT (HTTP/2)"]
| 프로토콜 | 장점 | 단점 |
|---|---|---|
| WebSocket | 브라우저 native, 단순 | 텍스트/binary 혼합 어색 |
| gRPC bidi-stream | binary 효율적, schema | 브라우저는 grpc-web 필요 |
| HTTP/2 chunked | 표준 | session 관리 직접 |
2026 시점 서버↔서버 = gRPC (CLOVA, Google), 브라우저↔서버 = WebSocket (Deepgram, AssemblyAI) 분기.
WebSocket 예시 (Deepgram)
const ws = new WebSocket('wss://api.deepgram.com/v1/listen?model=nova-3&language=ko&interim_results=true&endpointing=500');
ws.binaryType = 'arraybuffer';
ws.onmessage = (e) => {
const data = JSON.parse(e.data);
if (data.channel?.alternatives[0]) {
const transcript = data.channel.alternatives[0].transcript;
const isFinal = data.is_final;
if (isFinal) {
onFinalTranscript(transcript);
} else {
onPartial(transcript);
}
}
};
// 마이크 → WebSocket 오디오 청크
const audioContext = new AudioContext({ sampleRate: 16000 });
const processor = audioContext.createScriptProcessor(2048, 1, 1);
processor.onaudioprocess = (e) => {
const pcm = float32To16BitPCM(e.inputBuffer.getChannelData(0));
ws.send(pcm);
};
gRPC 예시 (CLOVA, Python)
import grpc
import nest_pb2, nest_pb2_grpc
def request_generator(audio_stream):
yield nest_pb2.NestRequest(
type=nest_pb2.RequestType.CONFIG,
config=nest_pb2.NestConfig(
language='ko-KR',
sample_rate=16000,
encoding=nest_pb2.AudioEncoding.LINEAR16,
)
)
for chunk in audio_stream:
yield nest_pb2.NestRequest(
type=nest_pb2.RequestType.AUDIO,
audio=chunk,
)
with grpc.secure_channel('clovaspeech-gw.ncloud.com:443', creds) as channel:
stub = nest_pb2_grpc.NestServiceStub(channel)
metadata = [('authorization', f'Bearer {token}')]
for response in stub.Recognize(request_generator(audio_iter), metadata=metadata):
if response.is_final:
print('FINAL:', response.transcript)
else:
print('partial:', response.transcript)
지표
| 지표 | 의미 | 목표 |
|---|---|---|
| TTP (Time-to-Partial) | 첫 partial 까지 | < 200ms |
| TTF (Time-to-Final) | 발화 종료 → final | < 500ms |
| TTL (Time-to-Last partial) | 마지막 partial 까지 | < 100ms |
| Stability | partial 의 변경 빈도 | < 5% |
자세한 latency 분석은 latency-percentiles.
흔한 함정
WARNING
- partial 로 LLM 호출 = 낭비. final 까지 기다리거나 시맨틱 endpointing.
- endpointing 너무 김 (1.5초+) = 대화 어색. 사용자가 답답함.
- endpointing 너무 짧음 (200ms) = 사용자 말끊김. 시맨틱 권장.
- WebSocket idle timeout = 침묵 시 connection close. ping/pong 주기적.
- 언어 자동 감지 = 짧은 발화 / 다국어 혼용 시 깨짐. 명시.
관련 위키
이 글의 용어 (7개)
- [Django] Channels: WebSocket, Consumer, Groupdjango
- 정의 Django Channels는 Django에 WebSocket, 비동기 프로토콜, 백그라운드 작업을 추가하는 공식 확장. 기본 Django는 HTTP만, Channels는 …
- [Network] gRPC: HTTP/2 + Protobuf, 4가지 streaming 패턴network
- 정의 gRPC 는 HTTP/2 위에서 Protobuf 직렬화 로 동작하는 고성능 RPC 프레임워크. Google 내부 Stubby 의 오픈소스 후계. 핵심 4가지: 1. Prot…
- [Voice AI] 음성 에이전트 아키텍처: Cascading / Streaming / S2S + Latency Budgetai
- 정의 실시간 음성 AI 의 3가지 패턴. 각자 지연 vs 유연성 vs 비용 트레이드오프. 3 패턴 비교 | | Cascading | Streaming | S2S | |---|--…
- [Voice AI] P50/P95/P99: latency 분포와 SLOdevops
- 정의 P50 / P95 / P99 = latency 분포의 분위수. 평균은 outlier 에 가려져 무의미. 음성 에이전트의 사용자 경험 은 p95/p99 가 결정. [!IMPO…
- [Voice AI] STT 모델: Whisper, Deepgram, AssemblyAI, CLOVAvoice
- 정의 STT (Speech-to-Text) / ASR (Automatic Speech Recognition) = 음성 → 텍스트 변환. 2026 시점 전사 정확도 인간 수준, 실…
- [Voice AI] Turn Detection + Barge-in + AEC: 자연스러운 대화voice
- 정의 자연스러운 음성 대화의 3 요소: 1. Turn Detection - 사용자가 말끝났는지 정확히 판단 2. Barge-in - 에이전트가 말하는 중에 사용자가 끼어들 수 있…
- [Voice AI] VAD: Silero, 에너지 기반, endpointing 임계값voice
- 정의 VAD (Voice Activity Detection) = 오디오 스트림에서 음성 vs 비음성 (침묵, 노이즈) 구분. 음성 에이전트의 발화 시작 / 종료 감지에 필수. 종…
이 개념을 다룬 위키 페이지 (6)
- wiki[Voice AI] 음성 에이전트 아키텍처: Cascading / Streaming / S2S + Latency Budget
- wiki[Voice AI] WebSocket 미디어 스트림: 양방향 풀듀플렉스, 프레임 버퍼링
- wiki[Voice] Whisper 내부: encoder-decoder, log-mel, tokenizer
- wiki[Voice AI] STT 모델: Whisper, Deepgram, AssemblyAI, CLOVA
- wiki[Voice AI] Turn Detection + Barge-in + AEC: 자연스러운 대화
- wiki[Voice AI] VAD: Silero, 에너지 기반, endpointing 임계값
💬 댓글