Перейти к основному содержанию

Голосовой ввод (storm-media)

Около 3 мин

Голосовой ввод в AI-чат

С версии 6.6.6668 в AI-чате можно диктовать сообщения голосом: кнопка микрофона в поле ввода (или горячая клавиша Ctrl+Space) записывает голосовую заметку до 60 секунд, распознаёт её и вставляет текст в поле ввода.

Распознавание речи выполняет отдельный сервис storm-media (модель Whisper). Основное приложение остаётся тонким прокси: браузер отправляет запись на бэкенд Storm, тот передаёт её в storm-media и возвращает распознанный текст. Эта статья описывает, как развернуть storm-media и включить голосовой ввод.

Как это работает

Браузер (запись микрофоном, ≤60 сек)
   │  POST /api/v1/ai/chat/transcribe
   ▼
Бэкенд Storm  ──── Authorization: Bearer <stormMediaToken> ────►  storm-media (Whisper)
   ▲                                                                 │
   └──────────────── распознанный текст ◄────────────────────────────┘
  • Запись ведёт браузер (MediaRecorder), поэтому страница Storm должна открываться по HTTPS — иначе браузер не даст доступ к микрофону.
  • Лимиты: до 60 секунд и до 4 МБ на заметку (минута голоса в webm/opus — около 1 МБ).
  • Распознавание минутной заметки на GPU занимает единицы секунд.

Шаг 1. Требования

УсловиеГдеКомментарий
MCP_ENABLED=trueENV основного приложенияГолосовой ввод — часть AI-модуля, как и чат (см. Включение чата)
AI-модуль в лицензииЛицензия (enableAiFeatures)Без него API чата закрыто
HTTPS у StormВаш прокси/балансировщикТребование браузера для доступа к микрофону
Сервис storm-mediaОтдельный хост/контейнерСм. Шаг 2. Для локального распознавания нужен GPU NVIDIA

Нужен ли GPU?

storm-media поддерживает два режима распознавания (ASR_BACKEND):

  • local (по умолчанию) — модель faster-whisper large-v3 работает внутри контейнера. Данные не покидают ваш контур, но нужен GPU NVIDIA (~5 ГБ видеопамяти). Возможен запуск на CPU (ASR_DEVICE=cpu), но распознавание будет заметно медленнее.
  • remote — storm-media проксирует аудио на внешний OpenAI-совместимый эндпоинт /v1/audio/transcriptions (свой Whisper-сервер или облачный сервис). GPU на хосте storm-media не нужен.

Шаг 2. Развернуть storm-media

Образ собирается в том же реестре, что и enterprise-приложение:

docker pull cr.selcloud.ru/stormbpmn-enterprise/storm-media:latest

Пример docker-compose.yml для локального распознавания на GPU:

services:
  storm-media:
    image: cr.selcloud.ru/stormbpmn-enterprise/storm-media:latest
    restart: unless-stopped
    environment:
      INTERNAL_TOKEN: "<секрет — придумайте длинную случайную строку>"
      ASR_BACKEND: "local"
    ports:
      - "8085:8080"                        # порт наружу — на ваше усмотрение
    volumes:
      - whisper_models:/models/whisper     # кэш весов модели (~3 ГБ), переживает обновления
      - media_tmp:/tmp/storm-media
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    healthcheck:
      test: ["CMD", "curl", "-fsS", "http://localhost:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 5

volumes:
  whisper_models:
  media_tmp:

Для режима remote уберите блок deploy: (GPU не нужен) и добавьте переменные:

    environment:
      INTERNAL_TOKEN: "<секрет>"
      ASR_BACKEND: "remote"
      ASR_BASE_URL: "http://whisper-host:8000"   # OpenAI-совместимый эндпоинт, БЕЗ /v1
      ASR_API_KEY: "<ключ, если требуется>"

Ключевые переменные окружения storm-media:

ПеременнаяОписаниеПо умолчанию
INTERNAL_TOKENShared-secret: запросы без заголовка X-Internal-Token отклоняются. Пусто = проверка отключена (только для доверенной сети)пусто
ASR_BACKENDlocal (Whisper внутри) или remote (внешний эндпоинт)local
ASR_MODELМодель faster-whisperlarge-v3
ASR_DEVICEcuda / cpu / autocuda
ASR_CACHE_DIRКаталог кэша весов (монтируйте volume)/models/whisper
ASR_BASE_URLДля remote: адрес эндпоинта без /v1пусто
ASR_API_KEYДля remote: ключ внешнего сервисапусто

Проверка работоспособности: GET /health (живость), GET /ready (модель загружена и готова), GET /metrics (Prometheus).

Первый запуск в режиме local

Веса Whisper (~3 ГБ) не вшиты в образ — при первом старте сервис скачивает их с Hugging Face в volume whisper_models. Хосту нужен разовый доступ в интернет. Для полностью изолированного контура заполните volume заранее (перенесите каталог кэша с машины с интернетом) — дальше сервис работает офлайн. Пока модель грузится, GET /ready отвечает ошибкой — это нормально.

Шаг 3. Настроить доступ бэкенда к storm-media

Бэкенд Storm ходит в storm-media с заголовком Authorization: Bearer <stormMediaToken>, а сам storm-media проверяет заголовок X-Internal-Token. Есть два способа связать их:

Вариант А — напрямую (доверенная приватная сеть). Запустите storm-media с пустым INTERNAL_TOKEN (проверка отключена) и укажите в настройках Storm адрес сервиса напрямую. Убедитесь, что порт storm-media недоступен снаружи периметра.

Вариант Б — через reverse-proxy (рекомендуется). Поставьте перед storm-media nginx, который проверяет Bearer-токен и подставляет X-Internal-Token:

location /media/ {
    if ($http_authorization != "Bearer <ваш-внешний-токен>") { return 401; }
    proxy_set_header X-Internal-Token "<значение INTERNAL_TOKEN storm-media>";
    proxy_pass http://127.0.0.1:8085/;   # порт storm-media
    client_max_body_size 10m;
}

Шаг 4. Включить голосовой ввод в админ-панели

Все настройки — в Административной панели, раздел «🤖 AI-ассистент», подгруппа «Голосовой ввод». Применяются на лету, без перезапуска.

Настройка (ключ)ТипОписание
Голосовой ввод в чате (voiceInputEnabled)тумблерПоказывает кнопку микрофона в чате. По умолчанию выключен
storm-media: Base URL (stormMediaBaseUrl)строкаАдрес storm-media, без /v1 — путь дописывается автоматически. Для варианта А: http://media-host:8085; для варианта Б: https://host/media
storm-media: токен (Bearer) (stormMediaToken)строкаТокен, который бэкенд отправляет в Authorization: Bearer. Для варианта А укажите любую непустую строку (сервис её не проверяет), для варианта Б — внешний токен из конфига nginx. Поле обязательно: пусто = «не настроено»
Язык распознавания (голос) (voiceInputLanguage)выборauto / ru / en / de. Пусто = auto (на коротких клипах авто-определение изредка ошибается — при однородной аудитории лучше зафиксировать язык)

Итоговый чек-лист:

  1. storm-media запущен, GET /ready отвечает успешно;
  2. MCP_ENABLED=true, лицензия с AI-модулем;
  3. В админ-панели заполнены stormMediaBaseUrl и stormMediaToken, включён тумблер «Голосовой ввод в чате»;
  4. Storm открыт по HTTPS — в поле ввода чата появилась кнопка микрофона.

Устранение неполадок

СимптомПричинаРешение
Кнопки микрофона нетТумблер voiceInputEnabled выключен, чат недоступен (MCP_ENABLED/лицензия)Проверьте условия из чек-листа; сам чат должен работать
Браузер не даёт доступ к микрофонуStorm открыт по HTTPНастройте HTTPS (требование браузера, а не Storm)
«Голосовой ввод не настроен»Пустой stormMediaBaseUrl или stormMediaTokenЗаполните обе настройки в админ-панели
«Голосовой ввод отключён»Тумблер voiceInputEnabled выключенВключите тумблер
«Не удалось распознать запись»storm-media недоступен, неверный токен/URL, ошибка распознаванияПроверьте GET /health и GET /ready storm-media, логи бэкенда (строки storm-media) и логи контейнера storm-media
«Аудиозапись длиннее 60 секунд» / «слишком большая»Превышены лимиты заметки (60 сек / 4 МБ)Диктуйте короче — лимит защищает GPU от длинных файлов
Первые запросы падают после рестарта storm-mediaМодель ещё загружаетсяДождитесь успешного GET /ready