Разработка live captions на базе ИИ: от идеи до внедрения

Проектируем и внедряем системы искусственного интеллекта: от прототипа до production-ready решения. Наша команда объединяет экспертизу в машинном обучении, дата-инжиниринге и MLOps, чтобы AI работал не в лаборатории, а в реальном бизнесе.
Показано 1 из 1Все 1564 услуг
Разработка live captions на базе ИИ: от идеи до внедрения
Средний
от 1 дня до 3 дней
Часто задаваемые вопросы

Направления AI-разработки

Этапы разработки AI-решения

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1358
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1250
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    956
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1188
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    646
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    929

Система live captions реального времени: архитектура, задержка, интеграция

Субтитры в реальном времени (live captions) — техническое средство реабилитации по ГОСТ Р 52872-2019 и международному стандарту WCAG 2.1 (критерий 1.2.4). Мы разрабатываем системы субтитрирования, которые работают с задержкой менее 2 секунд. Это критично для трансляций, конференций, телевидения и образовательных платформ. Наша команда — 12 инженеров с суммарным опытом более 25 лет в области STT и NLP. Мы реализовали 10+ инсталляций для событий с аудиторией до 5000 человек и обеспечили доступ к информации для тысяч слабослышащих. Внедрение AI-субтитров позволяет сэкономить до 40% бюджета по сравнению с ручным субтитрированием, а окупаемость наступает в течение 2–3 месяцев при регулярных трансляциях. Для оценки вашего проекта свяжитесь с нами — мы предложим решение под ключ.

Проблемы, которые решаем

Стандартные субтитры часто отстают от речи на 5–10 секунд — это неприемлемо для слабослышащих. Типичные сложности:

  • Синхронизация текста и звука страдает при использовании batch-обработки аудио.
  • Облачные STT-сервисы не всегда справляются с доменной лексикой (медицинская, юридическая терминология).
  • Интеграция с платформами вроде Zoom и Teams требует отдельного бота и настройки API.

Мы решаем эти проблемы через выбор streaming-модели, оптимизацию буферизации и кастомизацию словаря. Например, в одном проекте для конференции по телемедицине мы дообучили Whisper на корпусе медицинских терминов — точность распознавания выросла с 82% до 95%.

Как обеспечить задержку менее 2 секунд?

Ключ — выбор streaming-модели и архитектура передачи аудио. Deepgram Nova-2 выдаёт partial results каждые 200 мс, что даёт задержку end-to-end около 1 секунды — в 2–3 раза быстрее, чем faster-whisper large-v3 в batch-режиме. Для локальных сценариев мы используем faster-whisper с VAD-фильтром и буфером 3 секунды, что даёт 2.5–4 секунды. Но если нужна < 2 сек — только облачный streaming.

Real-time STT стек

Для субтитров с задержкой < 2 секунд от момента речи используем локальный faster-whisper или облачный Deepgram Nova-2. Пример ядра на Python:

import asyncio
import websockets
from faster_whisper import WhisperModel
import numpy as np
import sounddevice as sd

class RealTimeCaptioner:
    def __init__(self):
        self.model = WhisperModel(
            "large-v3",
            device="cuda",
            compute_type="float16"
        )
        self.buffer = []
        self.chunk_duration = 3.0  # секунды буферизации
        self.sample_rate = 16000

    async def stream_captions(self, websocket, audio_queue: asyncio.Queue):
        """Стриминг субтитров через WebSocket"""
        while True:
            chunk = await audio_queue.get()
            self.buffer.append(chunk)

            buffer_duration = len(self.buffer) * len(chunk) / self.sample_rate

            if buffer_duration >= self.chunk_duration:
                audio_data = np.concatenate(self.buffer)
                self.buffer = []

                segments, _ = self.model.transcribe(
                    audio_data,
                    language="ru",
                    vad_filter=True,
                    vad_parameters={"min_silence_duration_ms": 500}
                )

                for segment in segments:
                    caption = {
                        "text": segment.text.strip(),
                        "start": segment.start,
                        "end": segment.end,
                        "confidence": segment.avg_logprob
                    }
                    await websocket.send(json.dumps(caption, ensure_ascii=False))

WebRTC интеграция для браузера

Клиентская часть на JavaScript захватывает аудио с микрофона и передаёт на сервер через WebSocket. Сервер возвращает субтитры, которые отображаются с rolling-окном.

// Клиентская часть: захват аудио и стриминг на сервер
class LiveCaptionClient {
    constructor(wsUrl) {
        this.ws = new WebSocket(wsUrl);
        this.captionDiv = document.getElementById('captions');
    }

    async startCapturing() {
        const stream = await navigator.mediaDevices.getUserMedia({
            audio: { sampleRate: 16000, channelCount: 1, echoCancellation: true }
        });

        const audioContext = new AudioContext({ sampleRate: 16000 });
        const processor = audioContext.createScriptProcessor(4096, 1, 1);

        processor.onaudioprocess = (event) => {
            const pcmData = event.inputBuffer.getChannelData(0);
            const int16Array = new Int16Array(pcmData.length);
            for (let i = 0; i < pcmData.length; i++) {
                int16Array[i] = Math.max(-32768, Math.min(32767, pcmData[i] * 32768));
            }
            if (this.ws.readyState === WebSocket.OPEN) {
                this.ws.send(int16Array.buffer);
            }
        };

        this.ws.onmessage = (event) => {
            const caption = JSON.parse(event.data);
            this.displayCaption(caption.text);
        };

        const source = audioContext.createMediaStreamSource(stream);
        source.connect(processor);
        processor.connect(audioContext.destination);
    }

    displayCaption(text) {
        // Отображение с rolling-window (последние 2-3 строки)
        const line = document.createElement('p');
        line.textContent = text;
        line.className = 'caption-line';
        this.captionDiv.appendChild(line);

        // Убираем старые строки
        while (this.captionDiv.children.length > 3) {
            this.captionDiv.removeChild(this.captionDiv.firstChild);
        }

        // Auto-scroll
        this.captionDiv.scrollTop = this.captionDiv.scrollHeight;
    }
}

Как выбрать STT-модель для субтитров?

Выбор между локальным и облачным решением:

Параметр faster-whisper (локально) Deepgram Nova-2 (облако)
Задержка 0.3–0.8 сек (инференс) 0.1–0.3 сек (streaming)
Качество высокое (large-v3) высокое (специализированная)
Конфиденциальность полная данные уходят в облако
Стоимость один GPU (~$0.5/час) $0.004/мин аудио
Поддержка языков 99+ 30+

Для задач, где нужна полная изоляция данных (медицинские, государственные) — локальный faster-whisper с Triton Inference Server. Для типовых трансляций — облачный Deepgram или AssemblyAI. Мы оценим проект и предложим оптимальный вариант. Закажите предварительный аудит — он бесплатен и займёт 30 минут.

Требования к отображению (WCAG 2.1)

/* Субтитры для слабослышащих — WCAG 2.1 критерий 1.4.3 */
.caption-container {
    background-color: rgba(0, 0, 0, 0.85);
    color: #FFFFFF;
    font-size: 1.5rem;           /* минимум 24px */
    line-height: 1.6;
    padding: 12px 20px;
    border-radius: 4px;
    max-width: 80%;
    font-family: Arial, sans-serif;  /* высокая разборчивость */
}

/* Высокий контраст (коэффициент 7:1 для AA+) */
.caption-line {
    color: #FFFFFF;
    text-shadow: 1px 1px 2px #000;
}

Интеграция с Zoom/Teams через Bot

# Zoom использует RTMP для стриминга субтитров
import httpx

async def push_zoom_captions(meeting_id: str, caption_text: str, seq: int):
    """Отправляем субтитры в Zoom через Closed Caption API"""
    async with httpx.AsyncClient() as client:
        await client.post(
            f"https://api.zoom.us/v2/meetings/{meeting_id}/live_streaming/captions",
            json={"text": caption_text, "seq": seq, "lang": "ru-RU"},
            headers={"Authorization": f"Bearer {ZOOM_JWT_TOKEN}"}
        )

Что такое streaming transcription?

Streaming transcription — это технология, при которой модель распознаёт речь по мере поступления аудиофрагментов, выдавая промежуточные результаты (partial results) каждые 100-200 мс. Это позволяет обновлять субтитры плавно, без пауз на ожидание полной фразы. Используем WebSocket для передачи аудиофрагментов и получения текста с временными метками.

Чек-лист для внедрения

  • [ ] Аудит текущих каналов звука и платформы
  • [ ] Выбор STT-модели (локальная/облачная)
  • [ ] Калибровка под доменную лексику
  • [ ] Разработка WebRTC/WebSocket-сервера
  • [ ] Интеграция с Zoom, Teams, YouTube Live, RTMP
  • [ ] Интерфейс управления и ручной коррекции
  • [ ] Документация и обучение операторов
  • [ ] Гарантия 6 месяцев

Ориентировочные сроки

Веб-компонент субтитрирования — 1–2 недели. Полная интеграция с платформами — 2–3 недели. Для оценки вашего проекта свяжитесь с нами — опишите сценарий, и мы рассчитаем решение. Экономия бюджета может достигать 40% по сравнению с ручным субтитрированием, а окупаемость — за 2–3 месяца. Получите консультацию прямо сейчас!

Распознавание и синтез речи: ASR, TTS, клонирование голоса

Заказчик приходит с задачей: транскрибировать 40 000 часов колл-центра за неделю. Штатный облачный ASR (Google Speech-to-Text) выдаёт WER 28% на отраслевой лексике и стоит ощутимо дорого при таких объёмах. Задача — снизить WER ниже 10% и перейти на self-hosted инференс.

Типовые проблемы, с которыми приходят

WER не сходится к нужной метрике. Чаще всего виновата не архитектура, а данные: шумные аудио без нормализации уровня (-23 LUFS вместо стандарта), смешанные языки в одном канале, акцент, специфическая доменная лексика. Whisper large-v3 из коробки даёт WER 8–12% на чистом русском и проваливается до 25–35% на записях с PSTN-артефактами и узкополосным кодеком G.711.

Диаризация ломается при больше двух спикеров. pyannote/speaker-diarization-3.1 работает стабильно при 2–3 говорящих, но DER (Diarization Error Rate) растёт с 6% до 18–22% при 5+ участниках конференции. Проблема усугубляется перекрёстными репликами: по умолчанию min_duration_on=0.1 срезает короткие вставки.

Клонирование голоса — латентность или качество. XTTS v2 (Coqui) даёт натуральный голос, но при потоковой генерации stream_chunk_size=20 первый аудиочанк прилетает через 1.4–2.0 с — неприемлемо для интерактивных сценариев. StyleTTS2 и Kokoro быстрее, но требуют точной подготовки референсного аудио.

Как это решается на практике

Базовый стек для production-пайплайна:

  • ASR: openai/whisper-large-v3 или faster-whisper (CTranslate2-бэкенд, x4 скорость vs оригинал)
  • Диаризация: pyannote.audio 3.x + интеграция через whisperx для выравнивания по словам
  • TTS: XTTS v2 для качества, Edge-TTS или Silero для низкой латентности
  • Клонирование: XTTS v2 (3–6 с референсного аудио) или OpenVoice v2

Типичный пайплайн для колл-центра выглядит так: аудио из очереди Kafka → нормализация ffmpeg -af loudnorm до -23 LUFS → faster-whisper с beam_size=5, vad_filter=Truepyannote диаризация → постпроцессинг (пунктуация через deepmultilingualpunctuation) → запись в PostgreSQL с временными метками.

Кейс из практики. Финтех-компания с 12 000 звонков/день. Исходный WER на русском с банковской лексикой — 22% (Google STT). После fine-tuning whisper-medium на 200 часах размеченных записей через Hugging Face transformers + Seq2SeqTrainer с learning_rate=1e-5, warmup_steps=500 — WER упал до 7.3%. Инференс на одной A10G через faster-whisper с compute_type=float16 обрабатывает 40-минутный звонок за 55 секунд. Итоговая стоимость инференса — $0.0008/мин против $0.016/мин у облачного провайдера.

Дообучение Whisper на доменных данных

Когда общая модель не справляется, fine-tuning — первый инструмент. Минимальный датасет для заметного улучшения — 20–30 часов размеченного аудио в целевом домене. Разметку можно получить через итеративный процесс: прогнать через базовую модель → вручную исправить 10–15% ошибок → переобучить → повторить.

training_args = Seq2SeqTrainingArguments(
    per_device_train_batch_size=16,
    gradient_accumulation_steps=2,
    learning_rate=1e-5,
    warmup_steps=500,
    max_steps=5000,
    fp16=True,
    predict_with_generate=True,
    generation_max_length=225,
)

Важно: при fine-tuning Whisper нужно замораживать encoder первые 1000 шагов (model.freeze_encoder()), иначе акустические признаки разъедутся раньше, чем decoder адаптируется к новой лексике.

Синтез речи: выбор под задачу

Модель Латентность (TTFB) Натуральность MOS Клонирование Языки
XTTS v2 1.2–2.0 с 4.1–4.3 Да, 3 с референса 17
StyleTTS2 0.3–0.6 с 4.0–4.2 Да, требует адаптации en, + fine-tune
Kokoro-82M 0.08–0.15 с 3.7–3.9 Нет en, ja
Silero TTS 0.05–0.1 с 3.4–3.6 Нет ru, en, de, и др.
Edge-TTS ~0.4 с (cloud) 4.0 Нет 100+

Для интерактивных ботов с требованием TTFB < 300 мс — Silero или Kokoro. Для озвучки контента, где важна натуральность — XTTS v2 с потоковой отдачей через WebSocket.

Процесс работы

Начинаем с аудит-сессии: берём 2–4 часа ваших записей, прогоняем через несколько моделей, замеряем WER/CER, смотрим на распределение ошибок по типам (лексические, акустические, язык). Это занимает 1–2 дня и сразу показывает, нужен ли fine-tuning или достаточно пост-обработки.

Далее — выбор архитектуры под ваш throughput: один GPU для 1000 мин/день или кластер с балансировщиком для 100 000+ мин/день. Деплой через Docker-контейнер с FastAPI или Triton Inference Server для батчированного инференса.

Сроки зависят от сложности: базовая интеграция готовой модели — 1–2 недели. Fine-tuning с подготовкой данных и валидацией — 4–8 недель. Полная разработка голосового пайплайна (ASR + диаризация + TTS + мониторинг) — 2–4 месяца.