РелизКод / SDK · В работуHabr AI→ оригинал

Бот на анонимном Chrome в Docker записал 1170 встреч и выдаёт саммари за 4,6 минуты

Автор Habr собрал внутреннего бота для встреч вместо Copilot: за четыре месяца он записал около 1170 встреч, 920 часов речи у 93 организаторов. Медиана от конца записи до саммари с задачами — 4,6 минуты. Распознавание и диаризация идут на своих серверах, аудио наружу не уходит. Потоков два: бот в команде для канальных встреч Teams и анонимный Chrome в контейнере для всех остальных.

AI-обработка оригинала Habr AI; редакция Hamidun News
Бот на анонимном Chrome в Docker записал 1170 встреч и выдаёт саммари за 4,6 минуты
Источник: Habr AI. Коллаж: Hamidun News.
Слушать статью
Паспорт материала и цифры
ПараметрДанные
ПервоисточникHabr AI ↗
Главный редактор
Ключевой факт #1Штатный Teams на русском теряет две трети текста: 26–37 слов в минуту против 99–108
Ключевой факт #2Имена из VTT Teams и диаризация дают верное имя у 94–99% сегментов
Ключевой факт #3Graph API не видит on-prem Exchange, пришлось читать календарь через EWS

Договорённости со встреч живут ровно до следующей встречи. Протокол никто не ведёт, через неделю участники помнят около 10% сказанного. Вторая проблема свежее: в компании работают ИИ-агенты, но они знают только то, что записано и проиндексировано. Всё, что решили голосом, для них не существует. Автор материала на Habr решил закрыть обе дыры собственным ассистентом встреч и показал цифры из продакшена.

Почему не Copilot и не штатная расшифровка

Copilot работает только у тех, у кого есть подписка, а для автоматики «у всех команд без ручных действий» это не подходит. Он также не покрывает чужие тенанты, Телемост, МТС Линк и Труконф. Плюс аудио клиентских встреч хотелось обрабатывать на своих серверах.

Штатная русская расшифровка Teams автора тоже не устроила. На одних и тех же встречах она давала 26–37 слов в минуту, тогда как нормальный темп деловой речи около 100, а конвейер автора выдавал 99–108. Технические термины Teams пишет кириллицей: «эд баттон» вместо add button. Выборка небольшая, и автор честно называет сравнение ориентиром, а не бенчмарком.

Два потока вместо одного

Универсальное решение не получилось, потому что встречи Teams бывают двух видов.

Канальные встречи (дейли, планирования, ретро) записываются по политике сами и ложатся на SharePoint. У них нет обычного группового чата: переписка живёт в ветке под корневым постом канала, и анонимного гостя туда не пускают. Поэтому в команду ставится бот, который читает канал и ловит служебное событие CallRecording со статусом Success. Он забирает MP4, превращает его в WAV 16 кГц, отправляет на распознавание и публикует саммари в тред встречи. Для этого нужно RSC-разрешение ChannelMessage.Read.Group, которое выдаёт владелец команды, без админского гранта на весь тенант.

Все остальные встречи обслуживает «Ассистент Встреч». Его добавляют в приглашение как обычного участника, и к началу встречи в неё заходит анонимный Chrome в Docker. Запись идёт только с явного согласия: позвали и впустили — пишем, иначе нет. Потоки разделены физически: свои регистрации, приложения и права, поэтому утечка ключа одного не задевает другой.

Грабли, которые стоит запомнить

Гонка при записи. Состояние сначала хранилось в JSON-файлах, и на первой неделе listener и страховочный крон отправили одну запись дважды. Решение: Postgres с INSERT … ON CONFLICT DO NOTHING и SELECT … FOR UPDATE, а claim возвращает один из режимов new, retake, attach или skip.

Тупики с транскриптами. RSC ChannelMeetingTranscript.Read.Group отдавал 403, SharePoint не принимал app-only токен без сертификата, а Graph onlineMeetings/transcripts требует application access policy на организатора. Поэтому основа конвейера — MP4 и собственное распознавание, а VTT Teams служит вспомогательным источником.

Имена говорящих. Из VTT берутся число спикеров (подсказка num_speakers) и имена. Затем сегменты сопоставляются с кластерами диаризации по временному лагу. На трёх проверенных вручную встречах 94–99% сегментов получили верное имя. Любой сбой уходит в «Аноним»: неверно приписанная реплика хуже анонимной.

Кроме того, календарь в on-prem Exchange Graph API не видит, поэтому пришлось идти через EWS. Сессия браузерного постера тихо умирала из-за session-куки, а детектор говорящего ловил четверть речи, пока не перешли от событий к опросу состояния.

Что это значит на практике

Если строите похожее, то вывод такой. Не пытайтесь закрыть все платформы одним решением: канальные встречи Teams дешевле обслуживать ботом в команде, остальное — присутствием в разговоре. Храните состояние в базе с блокировками, а не в файлах. Промпты саммари делайте под тип встречи (дейли, ретро, планирование) и читайте их при каждой отправке, чтобы править без рестарта. И главное: транскрипты, которые индексирует платформа, дают агентам память о решениях, принятых голосом. Это основа для проактивных агентов.

Ограничения тоже есть. Приватные каналы поток 1 не поддерживает, на внешних встречах бота нужно впустить из лобби, а у самого Teams бывают баги, когда бот перестаёт получать апдейты канала (issue msteams-docs #11106). Цифры при этом из одной компании, без независимой проверки, но четыре месяца продакшена с 93 организаторами — убедительный довод.

Сюжет

Вопросы и ответы по материалу

Что известно по теме: почему не Copilot и не штатная расшифровка?

Copilot работает только у тех, у кого есть подписка, а для автоматики «у всех команд без ручных действий» это не подходит. Он также не покрывает чужие тенанты, Телемост, МТС Линк и Труконф. Плюс аудио клиентских встреч хотелось обрабатывать на своих серверах. Штатная русская расшифровка Teams автора тоже не устроила. На одних и тех же встречах она давала 26–37 слов в минуту, тогда как нормальный темп деловой речи около 100, а конвейер автора выдавал 99–108.

Что известно по теме: два потока вместо одного?

Универсальное решение не получилось, потому что встречи Teams бывают двух видов. Канальные встречи (дейли, планирования, ретро) записываются по политике сами и ложатся на SharePoint. У них нет обычного группового чата: переписка живёт в ветке под корневым постом канала, и анонимного гостя туда не пускают. Поэтому в команду ставится бот, который читает канал и ловит служебное событие CallRecording со статусом Success.

Что известно по теме: грабли, которые стоит запомнить?

Гонка при записи. Состояние сначала хранилось в JSON-файлах, и на первой неделе listener и страховочный крон отправили одну запись дважды. Решение: Postgres с INSERT … ON CONFLICT DO NOTHING и SELECT … FOR UPDATE, а claim возвращает один из режимов new, retake, attach или skip. Тупики с транскриптами. RSC ChannelMeetingTranscript.Read.Group отдавал 403, SharePoint не принимал app-only токен без сертификата, а Graph onlineMeetings/transcripts требует application access policy на организатора.

ЖХ
Жемал Хамидун — CPO Alpina Digital, продакт-менеджер AlpinaGPT. Делает AI-продукты с 2019 года. Hamidun News — pet project, который вырос в production-агрегатор с 6000+ читателей.
✦ Практика · Hamidun Academy

Соберите рабочего ИИ-агента или сервис своими руками без ручного кода

В Академии Жемала Хамидуна (academy.hamidun.com) открыт бесплатный 72-часовой тест-драйв: интерактивные скетч-уроки по Claude Code и Cursor, живой Tech Radar и готовые шаблоны агентов.

Открыть 72ч бесплатный доступ →Интенсив по Вайб-кодингу
Что вы думаете?
Загружаем комментарии…