Реализация потокового распознавания речи в реальном времени (Streaming STT)
Streaming STT передаёт аудио чанками и возвращает частичные результаты с задержкой 100–500 мс — в отличие от batch-подхода, где ждёт конца записи. Это ключевое требование для голосовых ботов, живых субтитров и агент-ассист систем.
Архитектура streaming pipeline
Microphone/WebRTC → WebSocket Server → STT Engine → NLP Processing → Response
16kHz PCM <200ms RTT partial+final intent/NER UI/TTS
Ключевые параметры:
- Chunk size: 100–250 мс (оптимальный баланс задержки и качества)
- VAD: обязателен для определения паузы в речи
- Endpointing: детекция конца высказывания для отправки финального результата
WebSocket сервер на FastAPI
from fastapi import FastAPI, WebSocket
from faster_whisper import WhisperModel
import numpy as np
import asyncio
app = FastAPI()
model = WhisperModel("medium", device="cuda", compute_type="float16")
@app.websocket("/stream")
async def stream_stt(websocket: WebSocket):
await websocket.accept()
audio_buffer = bytearray()
try:
while True:
chunk = await websocket.receive_bytes()
audio_buffer.extend(chunk)
# Транскрибируем каждые 2 секунды накопленного аудио
if len(audio_buffer) >= 32000 * 2: # 2 sec @ 16kHz 16-bit
audio_array = np.frombuffer(audio_buffer, dtype=np.int16).astype(np.float32) / 32768.0
segments, _ = model.transcribe(audio_array, language="ru")
partial_text = " ".join([s.text for s in segments])
await websocket.send_json({
"type": "partial",
"text": partial_text
})
audio_buffer = bytearray()
except Exception:
await websocket.close()
Сравнение движков для streaming
| Движок | Задержка | Языки | Стоимость |
|---|---|---|---|
| Deepgram Nova-2 | 100–200 мс | 30+ | $0.0043/мин |
| Google STT Streaming | 150–300 мс | 125+ | $0.006/мин |
| Azure Speech | 150–300 мс | 100+ | $0.01/мин |
| faster-whisper (self) | 200–500 мс | 99 | ~$0.001/мин |
| Vosk (self, CPU) | 300–700 мс | 20+ | ~$0/мин |
VAD для endpointing
import webrtcvad
vad = webrtcvad.Vad(2) # aggressiveness 0-3
def is_speech(audio_chunk: bytes, sample_rate: int = 16000) -> bool:
return vad.is_speech(audio_chunk, sample_rate)
WebRTC VAD обрабатывает чанки строго 10, 20 или 30 мс. Для определения конца фразы: 500–800 мс тишины после последнего речевого фрейма.
Клиентская часть (браузер)
const socket = new WebSocket('wss://api.example.com/stream');
const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
const recorder = new MediaRecorder(mediaStream, {
mimeType: 'audio/webm;codecs=opus'
});
recorder.ondataavailable = (event) => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(event.data);
}
};
recorder.start(250); // chunks each 250ms
Сроки реализации
- Базовый WebSocket стример с облачным STT: 3–4 дня
- Self-hosted с VAD и endpointing: 1 неделя
- Полный pipeline с фронтендом и обработкой ошибок: 2 недели







