본문으로 건너뛰기
김신건의 로그

[Voice AI] STT 스트리밍: partial vs final, endpointing, gRPC/WS

· 수정 · 📖 약 1분 · 440자/단어 #stt #streaming #asr #partial #final #endpointing
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-300msendpointing 후 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-streambinary 효율적, 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
Stabilitypartial 의 변경 빈도< 5%

자세한 latency 분석은 latency-percentiles.

흔한 함정

WARNING

  1. partial 로 LLM 호출 = 낭비. final 까지 기다리거나 시맨틱 endpointing.
  2. endpointing 너무 김 (1.5초+) = 대화 어색. 사용자가 답답함.
  3. endpointing 너무 짧음 (200ms) = 사용자 말끊김. 시맨틱 권장.
  4. WebSocket idle timeout = 침묵 시 connection close. ping/pong 주기적.
  5. 언어 자동 감지 = 짧은 발화 / 다국어 혼용 시 깨짐. 명시.

관련 위키

이 글의 용어 (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 비음성 (침묵, 노이즈) 구분. 음성 에이전트의 발화 시작 / 종료 감지에 필수. 종…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기