До основного вмісту

Потокова транскрипція Whisper у реальному часі на сервері

AI-агенти на VDS · 29.09.2026

Звичайний Whisper приймає готовий файл і віддає текст за кілька секунд чи хвилин — цього достатньо для розшифровки запису дзвінка, але не для субтитрів на льоту чи голосового асистента. Потокова транскрипція обробляє звук шматками у міру надходження і показує текст із затримкою в частки секунди. Розберемо, як зібрати такий конвеєр на власному VDS.

Чим потокова транскрипція відрізняється від пакетної

Базова установка Whisper, описана у статті про транскрипцію аудіо та відео на VPS, працює з цілим файлом: модель бачить весь контекст одразу і видає найточніший результат. Потік же приходить без кінця — мікрофон, дзвінок, ефір — і чекати повний запис не можна.

Рішення — різати потік на короткі сегменти по 2-5 секунд, розпізнавати кожен майже незалежно і склеювати результат. Плата за швидкість — модель бачить менше контексту, тому точність на межах сегментів трохи нижча, ніж при пакетній обробці.

Нарізка аудіо і визначення мовлення (VAD)

Різати потік суворо за таймером погано: слово може виявитися розрізаним навпіл між двома чанками. Voice Activity Detection (VAD) знаходить паузи в мовленні і ріже саме по них. Бібліотека silero-vad визначає межі фраз за мілісекунди і не потребує GPU.

pip install silero-vad faster-whisper sounddevice numpy
python3 -c "import torch; model, utils = torch.hub.load('snakers4/silero-vad', 'silero_vad')"

Типова схема: буфер накопичує аудіо, VAD позначає кінець фрази, накопичений шматок одразу йде у модель розпізнавання, буфер очищується.

faster-whisper у потоковому режимі

Для потокової обробки краще підходить faster-whisper — реалізація на CTranslate2, яка розпізнає короткі сегменти у 2-4 рази швидше за оригінальний PyTorch-код Whisper. На CPU-сервері з 8 ядрами модель розміру small обробляє 3-секундний чанк приблизно за 0.5-0.8 секунди — цього достатньо для субтитрів без помітної затримки.

from faster_whisper import WhisperModel

model = WhisperModel("small", device="cpu", compute_type="int8")
segments, info = model.transcribe("chunk.wav", language="uk", beam_size=1)
for seg in segments:
    print(seg.start, seg.end, seg.text)

beam_size=1 і модель розміру small чи base — обов'язковий компроміс для потоку: повнорозмірний large-v3 із великим beam search дає затримку в кілька секунд на чанк, що руйнує ідею реального часу.

Сервер на WebSocket для прийому звуку

Клієнт — браузер чи застосунок — надсилає аудіо шматками через WebSocket, сервер накопичує буфер, ріже за VAD і віддає текст назад тим самим з'єднанням. Мінімальний сервер на websockets і asyncio:

import asyncio, websockets

async def handle(ws):
    buffer = bytearray()
    async for chunk in ws:
        buffer.extend(chunk)
        if len(buffer) > 48000 * 2 * 3:  # ~3 секунди звуку 16-біт
            text = transcribe_chunk(bytes(buffer))
            await ws.send(text)
            buffer.clear()

asyncio.run(websockets.serve(handle, "0.0.0.0", 8765).__aenter__())

У проді замість ручного накопичення за розміром буфера використовуйте VAD із попереднього розділу — він ріже за паузами мовлення, а не за фіксованою кількістю байтів.

Затримка проти точності: що обрати

Розмір чанкаЗатримкаТочністьКоли використовувати
1-2 секмінімальнанижча, рвуться словаживі субтитри, асистент
3-5 секпомірнадобрадзвінки, наради
8-10 секпомітнависокалекції, подкасти
весь файлпісля записумаксимальнаархівна розшифровка

Для більшості завдань субтитрів у реальному часі розумний компроміс — чанк 3 секунди з моделлю small на CPU чи medium на GPU.

Куди вбудувати готовий транскрипт

Текст у реальному часі знадобиться у трьох сценаріях: субтитри на трансляції, голосове введення в чат-бота і пошук по дзвінках. Якщо транскрипт потрібен як вхід для LLM-агента — наприклад, щоб бот відповідав голосом на питання з мовлення — зручно віддавати результат у OpenAI-сумісний ендпоінт через LocalAI, об'єднавши розпізнавання мовлення і генерацію відповіді в одному API. Для контролю затримки у проді корисно підключити моніторинг AI-сервісів і відстежувати час обробки кожного чанка окремою метрикою.

  • Живі субтитри — WebSocket-сервер плюс VAD плюс модель small/base.
  • Голосове введення для бота — той самий конвеєр, але з накопиченням до кінця фрази перед відправкою у LLM.
  • Пошук по дзвінках — можна повернутися до пакетної обробки: затримка не критична, важлива точність.

Чек-лист запуску потокової транскрипції

Перш ніж викочувати потік у прод, перевірте чотири моменти: яку затримку реально дозволяє сценарій, чи вистачає CPU чи потрібен GPU, чи обрізає VAD слова на межах і що відбувається при розриві з'єднання — буфер не має губитися мовчки.

  • Визначте прийнятну затримку сценарію: 1-2 сек для асистента, 5-10 сек для субтитрів лекції.
  • Оберіть розмір моделі під ресурс: base/small на CPU, medium+ на GPU.
  • Налаштуйте VAD, а не різку за таймером — так менше рваних слів на стиках.
  • Додайте перепідключення і відновлення буфера при розриві WebSocket.
← Назад до бази знань Поставити питання підтримці