Задача по созданию AI-ассистента очень интересная и востребованная на рынке.

Для чего она?

Структуризация и контроль процессов. Самый большой импакт, конечно же, всё это даёт на повторяющихся встречах (дейлики, двухнедельные срезы и т.д.). Большинство сотрудников бегают на десятки встреч в неделю, и «забыть», что было на предыдущей встрече, очень легко - в этот момент можно просто обратиться к RAG или к протоколам напрямую. Также есть сотрудники, участие которых необходимо, но невозможно, - таким сотрудникам можно прислать протокол на апрув. Некоторые уже даже автоматически ставят задачи в Jira через function call или MCP на основе протоколов! Но это не auto-accept - как правило, человек смотрит в протокол и нажимает кнопку.

Вроде вникли в плюсы от сервиса - давайте разберём, из каких частей состоит задача по написанию такого сервиса.


1. Транскрибатор

Две опции применения:

a) Офлайн-транскрибация

Очевидный кейс: запись заканчивается, и мы хотим извлечь из неё текст.

Требования: качество транскрибации, время играет небольшую роль, поэтому можем позволить себе SOTA-решения.

Опенсорс-решений немало хороших, но мы выбираем WhisperX, т.к. качество хорошее и можно добавлять кастомные слова.

У бизнесов часто есть специфичные термины, которые плохо распознаёт любой транскрибатор. Варианта два:

  1. Добавить в словарь STT-модели, если такое возможно. Лечит, но, как правило, не всегда.

  2. LLM few-shot пост-процессинг - пишем промпт вроде: «Ты работаешь с транскрибацией. Вот список терминов, в которых STT-модель могла допустить ошибку. Вот в таких контекстах эти термины используются, вот что они значат \примеры». Я бы рекомендовал отдельным вызовом, но можно и внутри саммари-вызова эту задачу подкинуть.

    • Минусы - больше нагрузки.

    • Плюсы - как правило, качественнее варианта 1.

Также под WhisperX есть хорошая диаризация для русского языка через русскую align-модель - а такое мы уважаем.

Как выбирать транскрибатор (от менее трудозатратного к более):

  1. Фидбек в интернете (WhisperX - наш выбор).

  2. Берём пачку записей и берём дорогое проприетарное API (Deepgram, ElevenLabs, Qwen ASR, Google Speech Kit и т.д.). Транскрибируем топ-моделями, смотрим, устраивает ли нас качество. Если качество этих моделей заказчика устраивает, то измеряем количество ошибок нашей модели на этой транскрибации. Скажу сразу - это смелая гипотеза: в такой оценке мы не столько стремимся к эталонному ответу, сколько утверждаем себе, что ответ топ-модели нас устраивает и мы стремимся к нему. Не самый надёжный способ, но хоть что-то.

  3. Транскрибировать руками пачку встреч, проверить нашу модель и посчитать WER/CER + пробежаться глазами, где модель ошибается, поискать закономерности. Посмотреть, какие ошибки наиболее частые:

    - такое слово есть, но модель написала его неправильно

    - написала лишнее слово

    - не дописала слово и т.д.

Также учитывайте наличие хорошей диаризации! Это очень важно, особенно в дальнейшем под идентификацию.

В результате данному сервису мы присылаем запись и получаем ответ вида:

speaker_1 00:00-00:13 привет ребят, собрал вас на часовую встречу, чтобы вы мне 
помогли найти кнопку, которая чётко прописана в документации, но я клал и на вас,
и на документацию

speaker_2 00:13-00:18 как ты задолбал, ей-богу, открывай документацию

b) Real-time режим (Wake Word Detection)

Есть заказчики, которые хотят, чтобы ассистента можно было вызвать в ходе встречи и как-то с ним провзаимодействовать. На помощь приходит задачка wake word detection - тут нам уже необходима быстрая real-time модель или API с целью уловить конкретное слово. Когда мы это слово улавливаем - делаем какой-то action. Такая тула позволяет дать ассистенту навыки, а примеры навыков:

Здесь должна быть ссылка на GitHub

  • Писать что-то напрямую без риска того, что это будет отсутствовать в протоколе: «Барбос (ассистент триггерится), запиши в список дел: убрать .env из публичного репозитория компании, срок выполнения - пару месяцев».

  • Задавать вопросы по старым встречам через RAG или tools: «Барбос, что мы на прошлой встрече обсуждали?» Барбос должен знать название текущей встречи, дату и календарь. Он заходит в транскрибацию и отвечает на вопрос - либо сообщением в чат ВКС, либо голосом через TTS. Для этого нам нужно взять ответ Барбоса, озвучить его моделью, включить микрофон в ВКС и отдать на этот микрофон запись. И помните, что всё это должно работать быстро - короче, я бы поделал такое, это весело :)

Итог по транскрибатору: мы должны получить асинхронное API, которое принимает на вход аудио и флаг diarize=True/False, а на выходе отдаёт task_id, по которому мы будем проверять статус транскрибации. Когда статус будет completed, отдельным эндпоинтом вытащим транскрибацию.


2. Коннектор

Тааакс, из аудио текст мы научились получать, а вот как извлечь запись и подключить к ней бота? Сейчас расскажем.

У нас должны быть готовы коннекторы к разным ВКС. В зависимости от ссылки мы должны понять, к какой ВКС она ведёт (Google Meet, Телемост, Zoom, Teams и т.д.).

Далее с помощью Playwright (Python-библиотека) пишем код, который будет поднимать контейнер и подключать к нему бота (следим за нагрузкой, чтобы рассчитать инфру - CPU/RAM). В Playwright есть handle-мод, в котором вы можете для каждой страницы изолированно написать код, а потом объединить это в пайплайн. Логируем информацию на странице + скриншоты, мутим звук и видео, указываем имя, прописываем критерии завершения сессии, записываем звук и видео в минимальном разрешении (800×600, например - видео потом расскажу зачем) - и опаааа!

У нас появился сервис, в который мы передаём ссылку, он подключает к этой ссылке бота, записывает видео, из которого мы потом можем извлечь аудиодорожку и передать в транскрибатор - и вуаля!

Если у вас большой трафик, обратите внимание, что у разных площадок разные анти-флуд лимиты на подключения анонимных пользователей с одного IP. Эти лимиты довольно большие, но тем не менее.


3. Интерфейсы для ссылок

Для ссылок мы будем использовать три интерфейса: Telegram-бот, панель нашего сайта (где пользователь может что-нибудь потыкать и посохранять) и календарь рабочей почты.

Если с первыми двумя всё понятно, то с последним нужно будет потрудиться. Все встречи, которые вы планируете через рабочий воркфлоу (Яндекс Календарь, Google Calendar или чем вы там пользуетесь), приходят на почту - либо с ICS-вложением, либо просто текстом. Там находится абсолютно вся нужная для планирования встречи информация.

Пишем SMTP/IMAP клиенты на отправку и чтение и добавляем бота во все встречи, в которые хотите, чтобы он пришёл. На периодичные встречи он будет приходить по той же ссылке - главное, БД продумать нормально и парсить правильно. Но это всё очень легко дебажится через нормальное логирование.

Далее пишем шедулер, который в конкретное время будет отправлять ссылки в наше API (коннектор). А остальные интерфейсы (Telegram и UI) можем подключить к API напрямую.


Промежуточный итог

На данный момент у нас готов сервис, который по получению ссылки напрямую или по событию в календаре подключает бота, извлекает видео и аудио из встречи. Затем аудио мы передаём в транскрибатор и получаем текст. Далее текст - в PostgreSQL, а аудио и видео временно в S3.


4. Идентификация спикеров

Есть два стула…

Наши пользователи уже подключают бота и получают транскрибацию с диаризацией, но наши скромняжки хотят идентификацию…

На помощь приходит то самое видео. Из видео по таймкодам мы вырезаем кадры с определёнными спикерами - по 20 штук, например, каждого спикера со случайных отрезков его речи - и берём то имя, которое чаще попадается. ВКС выводят на мейн-фрейм говорящего: берём кадр, вырезаем имя, отправляем в наше OCR API (см. далее) - получаем ответ, приравниваем к спикеру.

Проблем нет, работает хорошо, НО: наши любимые коллеги любят сидеть в переговорках, а тут засада - таких не победить. Одно решение - копить voice bank и идентифицировать через голосовые эмбеддинги, но это хранение биометрии, и задумайтесь, насколько вы готовы к этому.

А так - просто идентифицируем этих пользователей как «спикер из переговорки», и тоже пойдёт.

Обратите внимание: при извлечении кадров учитывайте, что при демонстрации экрана фрейм активного спикера меняет размер и расположение.


5. OCR API

Нужно только если хотим идентификацию. Surya OCR - лёгкая и справится замечательно. Пишем API, которое принимает изображение и возвращает буквы.


6. LLM (протоколы)

Поднимаем vLLM-раннер с нашей любимой 7B-70B моделью, пишем промпт на structured output, получаем JSON, парсим его в HTML или что угодно. А можно парсить его в ивенты на нашей платформе и, например (то, о чём я говорил в начале), раздать роли - и руководители смогут сразу в Jira из протокола назначать задачи или изменять их по апруву. Ну, короче, прикольно получается.


7. RAG

Про RAG будет отдельная статья, но если вкратце - это даст вам возможность задавать вопросы по предыдущим встречам без указания конкретной встречи (обязательно дайте пользователям пермишены!). Плюс это можно классно интегрировать с wake word detection, когда LLM даёт ответ в режиме real-time в чат вашей ВКС.

Только осторожнее с доступами тут!


Заключение

Ну вот наша песня подошла к концу. Надеюсь, что этот пост помог вам получить какое-то понимание того, как написать свой сервис AI-ассистента для ВКС.

Terra tg

На гитхабе вас уже ждет простенькая реализация от наших младшеньких, которая позволяет подключать бота к телемосту и зуму, а еще выложим в него whisper api репозиторий с хорошей диаризацией на русском!)

Terra github

Если вам интересен какой-либо AI-сервис - напишите об этом в комментариях, и мы его разберём!