Приложение секретаря организовано в виде композиции docker-контейнеров, взаимодействующих через HTTP/HTTPS и WebSocket-соединения, которые устанавливаются на отдельном хосте (рисунок 1).

Рисунок 1
| Контейнер | Описание | Порт(ы) | Технологии |
|---|---|---|---|
| clerk | HTTP-сервер для приёма аудио, поиска контактов и управления телефонной книгой | 8000/tcp | Go |
| asr-server | Распознавание речи и компиляция моделей | 8003/tcp | Python |
| vosk-server | WebSocket-сервер для распознавания аудио | 2700/tcp | Python, Vosk |
| mongo | Хранилище истории вызовов и статистики | 27017/tcp | MongoDB |
| keycloak | Сервис авторизации и управления учётными записями | 8085/tcp | Java, Keycloak |
| keycloak-db | База данных для Keycloak | 5432/tcp | PostgreSQL |
| nginx | Прокси-сервер для веб-интерфейса | 8080/tcp | Nginx |
| webbackend | Бэкенд для веб-интерфейса | 8091/tcp | — |
| Сервис | Описание |
|---|---|
| RestFS | HTTP интерфейс с помощью которого взаимодействует MSR и IVR script взаимодействуют с clerk |
| MSR | Медиа сервер. Отправляет аудио поток от абонента через restfs в clerk |
| IVR Script | Скрипт реализующий основную логику работы с clerk |
Проект построен на модульном принципе, что позволяет гибко комбинировать сервисы:
| Модуль | Файл | Назначение |
|---|---|---|
| Базовое ядро | compose.yaml | Основные сервисы: Clerk, ASR-Server, Vosk-Server, Mongo, Nginx, WebBackend. Обязателен для запуска |
| Модуль авторизации | compose.keycloak.yaml | Интеграция Keycloak + Keycloak-DB + Keycloak-Importer в общую сеть |
| Модуль мониторинга | compose.peeper.yaml | Стек для сбора метрик и логов (опционально) |
| Конфигурация | Состав | Использование |
|---|---|---|
| Встроенный Keycloak | Ядро + Keycloak | Полноценная автономная система с авторизацией |
| Внешний Keycloak | Только ядро | Интеграция с существующим Keycloak-сервером. Параметры задаются через .env или переменные окружения |
| Встроенный Keycloak + Мониторинг | Ядро + Keycloak + Peeper | Полный стек с мониторингом |
| Внешний Keycloak + Мониторинг | Ядро + Peeper | Мониторинг с внешней авторизацией |
Назначение: Основной сервис, реализующий логику голосового помощника.
Функции:
/speech-recognition/{filename};Проксирование аудио в ASR-Server по WebSocket;
Получение гипотез распознавания от ASR;
Диалоговый поиск контакта в телефонной книге с учётом контекста;
Формирование ответов для MSR (номер, уточняющий вопрос, ошибка);
Ведение истории в MongoDB;
Обновление телефонной книги (по расписанию и вручную через /pb_update);
Запуск перекомпиляции ASR-модели;
Публикация статусов через WebSocket /status.
Назначение: Обработка аудио, распознавание речи и управление голосовой моделью.
Функции:
WebSocket /ws/asr_vad — приём аудио, VAD-сегментация и распознавание через Vosk;
Отправка partial и final результатов в Clerk;
Получение телефонной книги от Clerk для подготовки словаря;
Компиляция модели с новым словарём (фонетика, граф распознавания);
Перезапуск Vosk-Server с новой моделью;
Предоставление статуса компиляции через /status/compile.
Назначение: Непосредственное распознавание речи на основе модели Vosk.
Функции:
Приём аудио по WebSocket;
Распознавание с возвратом промежуточных (partial) и финальных (final) результатов;
Перезагрузка модели по команде от ASR-Server.
Назначение: Хранение истории и статистики.
Коллекции:
history — записи вызовов с полями:
call_ref — уникальный идентификатор вызова;
source — номер звонящего;
clerk_num — номер автосекретаря;
number — найденный номер;
status — unknown/good/bad/call_ended/no_number;
date — дата и время;
files — сопоставление аудиофайлов и распознанных текстов;
additional — дополнительные параметры (частота дискретизации, источник).
stats — агрегированная статистика по источникам:
Source — источник вызовов;
RequestCount — общее количество;
Correct — успешные;
Failed — неуспешные.
Назначение: Бэкенд для веб-интерфейса.
Функции:
Аутентификация через Keycloak;
Предоставление WebSocket-каналов для UI;
Работа с MongoDB для отображения истории и статистики.
Назначение: Веб-интерфейс и проксирование.
Функции:
Раздача статического фронтенда;
Проксирование запросов UI к:
Clerk (история, статистика, поиск, телефонная книга, статусы);
WebBackend (WebSocket-каналы).
Отдача логов и PCM-файлов для анализа.
Назначение: Авторизация и управление пользователями.
Компоненты:
Keycloak — сервер авторизации;
Keycloak-DB — PostgreSQL для хранения realm'ов, клиентов, пользователей;
Keycloak-Importer — однократный импорт настроек (clerk-realm.json) при старте.
Система поддерживает два источника, настраиваемых через переменную PB_SOURCES:
Корпоративный источник с иерархической структурой (отделы, группы, подгруппы);
Авторизация через Keycloak (JWT) или API-ключ;
Параметры:
ADDRESS_BOOK_URL — URL GraphQL-API;
AB_AUTH_TYPE — BY_KEYCLOAK_JWT или API_KEY;
AB_AUTH_HOST, AB_AUTH_PORT, AB_AUTH_REALM, AB_CLIENT_ID, AB_CLIENT_SECRET — для JWT;
AB_API_KEY — для API-ключа.
Статический файл формата vCard;
Доступен по HTTP/HTTPS;
Параметр: PB_URL — URL файла.
PB_SOURCES=ADDRESSBOOK,VCF — Address Book имеет приоритет, VCF игнорируется;
PB_SOURCES=ADDRESSBOOK — только Address Book;
PB_SOURCES=VCF — только VCF.
Инициализация (при старте):
Clerk загружает телефонную книгу из настроенного источника;
Выполняется маппинг данных во внутренний формат (contact.Contact);
Контакты валидируются (обязательные поля, корректность номеров, ФИО).
Периодическое обновление (каждые 12 часов или по запросу /pb_update):
Повторная загрузка из источника;
Применение изменений через ApplyDiff (добавление, обновление, удаление);
Сохранение пользовательских настроек (множители);
Отправка подготовленного словаря в ASR-Server.
Подготовка словаря:
Контакты транслитерируются и форматируются;
Учитывается поле «Множитель» для увеличения веса контакта;
Словарь отправляется в ASR-Server через SendPrepare.
Запуск компиляции:
ASR-Server получает запрос SendRecompile;
Запускается фоновая задача компиляции;
Создаются фонетические данные и пересобирается поисковый граф.
Ожидание завершения:
Clerk периодически опрашивает статус (GetStatusCompile);
Статусы: wait → compile → ready / fail.
Перезапуск Vosk-Server:
Новая модель копируется в Vosk-Server;
Через WebSocket подаётся команда на загрузку модели.
Публикация статусов:
Статус компиляции транслируется всем подписчикам через WebSocket /status.
В веб-интерфейсе для контакта можно задать значение «Множитель». Значение сохраняется в оперативной памяти и в backup.pb.
При обновлении телефонной книги:
Множитель меняет относительный вес контакта, повышая вероятность его выбора при распознавании.
Запрос: POST /speech-recognition/domain/{domain}/{filename};
Формат аудио: PCM/WAV, 48 kHz, моно, 16 бит (s16E);
Заголовки:
x-call-ref — уникальный идентификатор вызова;
x-asr-service — целевой ASR (для балансировки);
Expect: 100-continue — ожидание подтверждения.
Процесс:
Clerk извлекает домен, имя файла, call-ref;
По имени файла определяет номера звонящего и автосекретаря;
Создаёт контекст обработки;
Запускает потоковую отправку аудио в ASR-Server по WebSocket.
Для каждого вызова создаётся запись в MongoDB (история);
Аудиопоток сохраняется на диск для последующего анализа;
Итоговый результат обновляет запись истории.
Clerk устанавливает WebSocket-соединение с ASR-Server;
Аудио отправляется бинарными сообщениями;
В конце передаётся сигнал EOF;
ASR-Server возвращает текстовые сообщения:
partial — промежуточный результат;
result — финальный результат.
Клиент шлёт аудио-чанки │ ▼ [Буфер VAD] │ ▼ Анализ (200 мс) │ ├─ Тишина → ожидание ├─ Начало речи → старт сегмента └─ Конец речи → завершение сегмента │ ▼ Накопление секундных порций │ ▼ Отправка в Vosk (порциями по 1 секунде) │ ▼ Получение partial-результатов → отправка в Clerk │ ▼ Завершение сегмента → отправка целого отрезка в Vosk │ ▼ Получение final-результата → отправка в Clerk
Алгоритм для каждого текстового сегмента:
Добавить сегмент к общему накопленному тексту;
Нормализовать текст (регистр, лишние символы);
Выполнить поиск в телефонной книге:
По полному имени, фамилии, отчеству;
По подразделению, группе (при наличии).
Возможные исходы:
Однозначный номер → немедленный ответ 200 OK с номером;
Несколько контактов → уточняющий вопрос;
Нет контакта → сообщение об ошибке;
У контакта нет номера → сообщение об ошибке;
Пустой текст → ожидание следующих сегментов.
Контекст диалога:
Хранится по call_ref;
Содержит предыдущий запрос и «следующий атрибут» для уточнения (фамилия, имя, подразделение).
HTTP/1.1 206 Partial Content
{
"done": false,
"recognized": "",
"negative_url": "negative/<callRef>"
} |
HTTP/1.1 206 Partial Content
{
"done": true,
"recognized": "<текст>",
"answer": "<фраза для TTS>",
"negative_url": "negative/<callRef>"
} |
HTTP/1.1 200 OK
{
"done": true,
"recognized": "<текст>",
"number": "<найденный номер>",
"positive_url": "positive/<callRef>",
"negative_url": "negative/<callRef>"
} |
unknown — автоматически при создании записи или при неоднозначном результате;
good — при успешном поиске или по запросу /positive/{callRef};
bad — по запросу /negative/{callRef};
call_ended — при обрыве вызова или таймауте ASR;
no_number — контакт найден, но без номера.
Эндпоинты Clerk:
GET /history — список всех записей;
GET /history/{callRef} — конкретная запись;
DELETE /history — удаление истории.
Структура записи:
{
"_id": "ObjectId(...)",
"call_ref": "3095-6666",
"req_source": "ivr",
"detection_attr": "fio",
"status": "unknown",
"date": "2026-03-20T11:17:57.709Z",
"files": {
"2026-03-20_11-17-54-519208_asr_3095-6666.wav": "ковалев"
},
"additional": {
"source": "msr",
"sample_rate": 48000
},
"source": "3095",
"clerk_num": "6666",
"number": null
} |
Периодический пересчёт (раз в 10 минут);
Хранится в коллекции stats;
Поля: Source, RequestCount, Correct, Failed;
Эндпоинт: GET /stats.
Модуль мониторинга (compose.peeper.yaml) включает стек для сбора метрик и логов:
Сбор метрик с Docker-контейнеров;
Визуализация состояния системы;
Анализ логов.
| Сервис | Порт | Протокол | Назначение |
|---|---|---|---|
| Clerk | 8000/tcp | HTTP/WS | Основное API, WebSocket статусов |
| ASR-Server | 8003/tcp | HTTP/WS | Распознавание, компиляция |
| Vosk-Server | 2700/tcp | WebSocket | Распознавание аудио |
| Mongo | 27017/tcp | TCP | База данных |
| Keycloak | 8085/tcp | HTTP | Авторизация |
| Keycloak-DB | 5432/tcp | TCP | PostgreSQL |
| Nginx | 8080/tcp | HTTP | Frontend, прокси |
| WebBackend | 8091/tcp | HTTP/WS | Backend для UI |