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

Хранилище и модели

Страница для тех, кто настраивает сервер: где лежат записи звонков, как подключить S3, как устроен забор файлов из бакета, какая модель распознаёт речь и сколько памяти и процессора ей нужно.

Где хранится звук

За хранение отвечает одна переменная STORAGE_KIND. Тексты, оценки и настройки лежат в PostgreSQL независимо от неё.

local (по умолчанию) s3
Где лежат файлы том Docker audio-data, внутри контейнера tgt-api каталог /data/audio бакет S3-совместимого хранилища
Ключ файла ГГГГ/ММ/ДД/<uuid>.<расширение> <S3_UPLOAD_PREFIX>ГГГГ/ММ/ДД/<uuid>.<расширение>
Что в резервной копии нужна копия тома audio-data звук уже вне сервера, включите версионирование бакета, если провайдер даёт
Занимает диск сервера да, растёт с числом записей нет, только временные файлы загрузки

Дата в ключе указана по UTC, имя файла остаётся только в базе. Какой вид хранилища работает сейчас, видно в «Настройки → Система → Технические сведения → Хранилище аудио».

Внимание

Смена STORAGE_KIND не переносит файлы. Записи, оставшиеся на локальном диске, потеряют звук и сохранят расшифровки. Переключайте вид хранилища до появления настоящих данных или когда текущие записи уже расшифрованы.

Загружаемый файл сначала попадает во временную папку контейнера tgt-api и затем переносится в хранилище; файлы, которые так и не были перенесены, удаляются при запуске API, если старше суток. Файлы, скачиваемые по ссылке, пишутся в хранилище потоком.

Подключение S3

  1. Создайте бакет и ключ доступа у провайдера (или добавьте MinIO сервисом Coolify). Ключу нужны права s3:ListBucket на бакет и s3:GetObject, s3:PutObject, s3:AbortMultipartUpload и s3:DeleteObject на объекты. Для префикса, из которого файлы только забираются, достаточно чтения: забранные файлы платформа никогда не меняет и не удаляет.
  2. В переменных окружения задайте STORAGE_KIND=s3, S3_BUCKET, S3_ACCESS_KEY_ID и S3_SECRET_ACCESS_KEY. Провайдеру, отличному от AWS, нужны ещё S3_ENDPOINT_URL и S3_REGION (например, для Yandex Object Storage это https://storage.yandexcloud.net и ru-central1). Для AWS адрес не нужен, нужен настоящий регион и S3_FORCE_PATH_STYLE=false; MinIO и большинство других провайдеров работают со значением по умолчанию true.
  3. Чтобы платформа забирала файлы, которые кладёт в бакет кто-то другой, задайте ещё INGESTION_SOURCE=s3 и S3_INGEST_PREFIX (папка вида calls/; пусто означает весь бакет, кроме S3_UPLOAD_PREFIX). Префиксы не должны пересекаться: S3_INGEST_PREFIX не может лежать внутри S3_UPLOAD_PREFIX, иначе сервер не запустится.
  4. Перезапустите приложение. Проверьте, что «Хранилище аудио» показывает «S3» и что загруженный файл появился в бакете.

Без обязательной переменной tgt-api не запускается и называет её в сообщении. Сочетание INGESTION_SOURCE=s3 требует STORAGE_KIND=s3, а local-inbox требует local.

Откуда приходят записи

Путь Что запускает Что ограничивает
Автоматический режим сам берёт новые звонки менеджеров продаж из Битрикс24 по расписанию включается владельцем; берёт не больше, чем помещается в очередь, и не глубже семи суток
Вручную кнопка «Загрузить файл» в «Звонки», ссылка или POST /v1/recordings вход владельца или ключ API
Загрузка истории «Загрузить историю» в «Настройки → Автоматика → Дополнительно» период, оценка числа звонков и подтверждение; не больше свободного места в очереди
Событие Битрикс24 исходящий вебхук «звонок завершён» переключатель «Автозабор записей»
Старый забор из хранилища файлы, которые появились в папке inbox или в бакете переключатель «Автозабор записей» и четыре ограничения

Старый забор из хранилища

Он нужен, когда записи кладёт в папку или бакет внешняя система. Он выключен по умолчанию и ограничен: у сервера немного ресурсов, а в бакете может лежать архив за годы. Поэтому забор не берёт ничего старше выбранной даты, работает партиями и только пока в очереди есть место, а включение никогда не поставит в очередь весь бакет сразу. Раздел виден только владельцу, в «Настройки → Система → Старый забор записей из хранилища», и показывается, только пока источник настроен или переключатель включён.

Ограничение Поле в интерфейсе По умолчанию Диапазон
Самая ранняя дата «Забирать записи не раньше» момент включения дата
Размер очереди «Максимум в очереди» 50 1-10 000
Размер партии «Записей за один запуск» 20 1-500
Пауза между запусками «Интервал, секунд» 60 10-86 400

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

  • Берутся только файлы, изменённые позже выбранной даты. Если включить без даты, дата ставится равной текущему моменту. Смена даты начинает просмотр с неё.
  • Пока в очереди столько записей или больше, сколько указано в «Максимум в очереди», ничего не берётся (результат «Очередь заполнена»).
  • За один запуск регистрируется не больше «Записей за один запуск» (и не больше свободного места в очереди).
  • Файлы регистрируются на месте: платформа их не копирует, не переименовывает и не удаляет, а уже известные пропускает. Подходят файлы с расширениями mp3, wav, ogg, oga, opus, m4a, mp4, aac, flac, webm, amr. Пустые объекты пропускаются; в локальной папке inbox ещё пропускаются скрытые файлы (имя с точки) и файлы, изменённые меньше 5 секунд назад.
  • Каждый запуск просматривает весь префикс: один запрос к S3 на каждую тысячу объектов. Для большого бакета увеличьте «Интервал, секунд».
  • Лимит «Максимум в очереди» общий: по нему же ждут автоматическая проверка новых звонков и загрузка истории из Битрикс24.

Результат последнего запуска виден в карточке «Состояние забора»: «Записи забраны», «Новых записей нет», «Забор выключен», «Очередь заполнена», «Источник не настроен» или «Ошибка забора» с сообщением.

Модель распознавания

Речь распознаёт модель T-one: потоковая CTC-модель, обученная на телефонных разговорах. Она работает на процессоре, без видеокарты и без обращений к внешним сервисам. Русский язык, звук приводится к 8 кГц моно. Принимаются распространённые форматы: wav, mp3, ogg/opus, m4a, flac, webm и другие контейнеры из закреплённого списка; побитые файлы разбираются до места обрыва. Самый длинный принимаемый звонок: 10 800 секунд (три часа); более длинный получает ошибку «Запись длиннее, чем принимает сервис, повтор не поможет».

Распознаватель tgt-asr это внутренний сервис: у него нет публичного адреса, страница /docs отключена, он не хранит бизнес-данных. API обращается к нему по секрету ASR_API_KEY.

greedy beam_search (по умолчанию в docker-compose.yml)
Что использует только акустическую модель акустическую модель и языковую модель KenLM (kenlm.bin)
Файлы моделей model.onnx и model.fp32.onnx те же и kenlm.bin
Размер скачиваемого около 0,4 ГБ около 5,9 ГБ
Что делает быстрый, без поправок исправляет слова, которых не бывает в русском языке
  • Декодер сервиса задаёт DECODER. Читается при старте: переключение это перезапуск без пересборки, нужные файлы докачиваются сами.
  • Запись может попросить декодер при загрузке по API (поле decoder). Сервис, запущенный с greedy, отвечает своим декодером, а в расшифровке всегда указано, каким она получена.
  • По умолчанию вычисления идут в fp32: результат не зависит от процессора (на странице «Технические сведения» поле «Вычисления» показывает fp32). Переменная FP32_COMPUTE=false отключает это и нужна только для отладки; в docker-compose.yml её нет.

Память и параллельность

Настройка Где Что делает По умолчанию
ASR_MAX_CONCURRENCY (в сервисе MAX_CONCURRENCY) tgt-asr сколько файлов обрабатывается одновременно, остальные ждут 2
TRANSCRIPTION_CONCURRENCY tgt-api сколько записей API передаёт распознавателю одновременно (1-8). Держите не больше ASR_MAX_CONCURRENCY 2
ASR_MEMORY_LIMIT контейнер tgt-asr лимит памяти 3g
WEB_CONCURRENCY tgt-asr число процессов распознавателя; у каждого своя копия модели и свои слоты 1, задано в файле
ORT_THREADS tgt-asr потоков вычислений на один файл 1 (в файле не задана)

Что нужно знать:

  • По умолчанию на один файл идёт один поток вычислений (ORT_THREADS), поэтому одна расшифровка занимает примерно одно ядро. Число одновременных расшифровок выбирайте по числу свободных ядер сервера и измеряйте скорость на своём сервере: в карточке «Сейчас расшифровывается» на странице очереди показана скорость.
  • По измерениям разработчиков на Linux файл языковой модели отображается в память, поэтому почти вся память под него это кэш файла, который ядро вытесняет при нехватке памяти. Процесс держит около 0,6 ГБ обычной памяти; при маленьком кэше расшифровка идёт медленнее, но результат тот же. Сервис работал и при лимите 1,5 ГБ.
  • WEB_CONCURRENCY увеличивает потребление памяти кратно числу процессов, поэтому для ускорения увеличивайте ASR_MAX_CONCURRENCY, а не число процессов.
  • Скачивание моделей идёт потоком прямо в файл на диске и не требует памяти под весь файл.

Почему образ небольшой, а модели лежат в томе

Модели не входят в образ tgt-asr. Они скачиваются при первом запуске в том asr-models (/models/t-one) и переиспользуются при каждом следующем. Так образ остаётся небольшим, каждое развёртывание не копирует почти 6 ГБ, а очистка кэша сборки Docker не заставляет скачивать модели заново.

Что делает распознаватель при запуске:

  1. Проверяет, каких файлов не хватает для выбранного декодера, и берёт блокировку, чтобы несколько процессов не скачивали одно и то же.
  2. Проверяет свободное место (нужно примерно в 1,2 раза больше размера недостающих файлов) и, если места мало, останавливается с понятным сообщением.
  3. Скачивает файлы с Hugging Face прямо на место назначения: файл появляется под своим именем, только когда он полный, а после обрыва скачивание продолжается.
  4. Один раз строит из model.onnx копию model.fp32.onnx для вычислений в fp32.

Том asr-models не нужно копировать в резервную копию: он скачивается заново. Том audio-data нужен в копии, если звук хранится локально.

Распознаватель можно развернуть и отдельно от платформы, например как общий сервис: тогда ему нужны API_KEYS (секрет), DECODER, MAX_CONCURRENCY, постоянный том в /models и порт 8000. Для работы платформы это не требуется.

См. также