BrainstormBuddy: как я собрал AI ассистента, который слушает созвон и подсказывает ответ в реальном времени

BrainstormBuddy: как я собрал AI ассистента, который слушает созвон и подсказывает ответ в реальном времени

С обычными AI-ассистентами всё довольно понятно: написал вопрос, подождал несколько секунд, получил ответ.

На созвоне такая схема почти бесполезна.

Когда тебе задают вопрос на собеседовании, встрече с клиентом или переговорах, нельзя сказать: «секунду, сейчас скопирую это в ChatGPT». Ответ нужен здесь и сейчас. Желательно — пока пауза ещё не стала неловкой.

Так появился BrainstormBuddy — Windows-приложение, которое слушает разговор, распознаёт речь и показывает подсказку прямо поверх остальных окон.

Причём вся цепочка может работать локально: от захвата звука до speech-to-text и LLM.

Исходники открыты, проект распространяется под MIT.

GitHub: https://github.com/liveinno/brainstorm-buddy

Скачать:

В этой статье хочу рассказать не столько о кнопках приложения, сколько о том, что оказалось внутри.

Потому что самое интересное началось примерно в тот момент, когда схема «взяли звук → отправили в Whisper → спросили LLM» встретилась с реальным разговором.

Что вообще делает BrainstormBuddy

У приложения два источника звука.

Первый — системный loopback. Это то, что я слышу из Zoom, Teams, браузера или любого другого приложения.

Второй — мой микрофон.

Они идут отдельно, поэтому система понимает, где говорит собеседник, а где пользователь.

Дальше цепочка примерно такая:

BrainstormBuddy: как я собрал AI ассистента, который слушает созвон и подсказывает ответ в реальном времени

На первый взгляд здесь вообще нет ничего необычного.

На практике каждый из этих переходов превращается в отдельную инженерную проблему. Как понять, что человек закончил предложение? Что делать, если он думает и замолкает на полсекунды?

Что делать, если видео играет 40 секунд вообще без тишины?

Что будет, если LLM отвечает пять секунд, а за это время собеседник успел сказать ещё три фразы?

Что произойдёт, когда пользователь посреди звонка подключит Bluetooth-наушники? А если модель вернула HTTP 200, но самого ответа в content нет?

Вот из таких мелочей в итоге и состоит real-time AI.

Почему я не стал делать всё через облако

Самый простой вариант такого продукта — отправлять аудио на внешний STT, затем результат в облачную LLM.

Технически это заметно легче.

Но разговоры — довольно чувствительные данные. Это могут быть интервью, внутренние встречи, коммерческие переговоры.

Поэтому BrainstormBuddy изначально проектировался так, чтобы весь pipeline можно было запустить локально.

Для русского speech-to-text используется GigaAM. Модель работает через ONNX Runtime. Для английского и смешанной речи можно использовать Whisper. LLM тоже необязательно должна находиться в облаке: приложение умеет работать с Ollama и OpenAI-compatible API.

При этом я не стал делать из local-first религию.

На обычном ноутбуке небольшая локальная LLM почти неизбежно проигрывает быстрой облачной модели и по задержке, и по качеству ответа. Поэтому архитектура гибридная: пользователь сам решает, какие части pipeline оставить на машине, а какие вынести наружу.

Можно получить полностью локальную схему:

Audio → GigaAM → локальная LLM → Overlay

а можно оставить распознавание на компьютере, но использовать внешнюю LLM:

Audio → GigaAM → OpenAI-compatible API → Overlay

или подключить собственный STT-сервер.

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

Текущий desktop-стек довольно обычный для Windows: C# 12, .NET 8, WPF, NAudio, ONNX Runtime, Whisper и HTTP API для языковых моделей.

BrainstormBuddy: как я собрал AI ассистента, который слушает созвон и подсказывает ответ в реальном времени

Самое сложное — понять, когда человек закончил говорить

STT нельзя бесконечно кормить непрерывным аудиопотоком. Сначала его нужно разрезать на фразы.

Самый очевидный способ — считать фразу законченной после периода тишины. Но люди говорят неидеально: начинают мысль, делают паузу, продолжают. Если слишком быстро закрывать аудиочанк, LLM начинает отвечать на половину предложения. Если ждать слишком долго — пропадает ощущение real-time.

В BrainstormBuddy поэтому используется несколько ограничений. Обычно фраза закрывается после примерно 1,8 секунды тишины. Но если звук идёт непрерывно и пауза вообще не наступает, срабатывает дополнительный лимит и накопленный фрагмент всё равно отправляется в STT. Есть и абсолютный потолок длительности чанка как защита от аномальных ситуаций.

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

BrainstormBuddy: как я собрал AI ассистента, который слушает созвон и подсказывает ответ в реальном времени

Аудиопауза ещё не означает конец мысли

Есть другая ситуация:

«Мы решили что…»

Пауза.

«…перенесём сервис в Kubernetes».

Для аудиодетектора это могут быть две разные фразы, но по смыслу это одно предложение.

Поэтому поверх STT появился небольшой семантический слой. Если распознанная реплика выглядит незаконченной, запрос к LLM можно ненадолго придержать и объединить со следующим фрагментом.

Важно, что сама транскрипция при этом появляется сразу. Задерживается только AI-ответ. Пользователь видит, что приложение его услышало, но модель не начинает отвечать на каждую половину предложения.

Это довольно простая эвристика, но в живом разговоре она иногда полезнее добавления ещё одной модели в pipeline.

Почему STT и LLM пришлось разделить

Изначально обработка была практически последовательной: получили аудио, распознали, отправили текст в LLM, дождались ответа и пошли дальше.

Проблема обнаружилась быстро. Если LLM отвечает пять секунд, разговор на эти пять секунд не останавливается. Собеседник продолжает говорить, новые аудиофрагменты накапливаются, а транскрипция начинает отставать.

Поэтому STT и генерация теперь работают независимо:

Audio → STT Queue → STT Worker → текст на экран→ LLM Queue → LLM Worker → AI-ответ

Как только STT закончил распознавание, фраза сразу появляется в интерфейсе. Затем она попадает в отдельную очередь LLM, и ответ добавляется позже.

Для меня это стало одним из главных выводов проекта: медленная LLM не должна блокировать транскрипцию.

В real-time приложениях лучше вообще меньше думать о последовательной цепочке функций и больше — о нескольких независимых потоках данных, которые работают с разной скоростью.

Контекст иногда важнее размера модели

Можно подключить мощную LLM, но если она ничего не знает о пользователе, ответы на собеседовании будут слишком общими или начнут содержать придуманные факты.

Поэтому в BrainstormBuddy можно передавать контекст: опыт, проекты, технологии и другую информацию, которую модель должна учитывать при формировании подсказок.

BrainstormBuddy: как я собрал AI ассистента, который слушает созвон и подсказывает ответ в реальном времени

Кроме интервью есть несколько режимов работы: встречи, продажи, brainstorming, перевод. По сути BrainstormBuddy постепенно превратился не столько в «помощника для интервью», сколько в движок для AI-подсказок во время живого разговора.

Overlay и ощущение real-time

Результат показывается в прозрачном WPF-окне поверх остальных приложений. Сначала появляется транскрипция, затем к ней добавляется ответ AI.

Это маленькая деталь, которая сильно влияет на UX. Пользователь не смотрит пять секунд на пустое окно и не думает, работает ли приложение вообще. Он сразу видит: речь распознана, ответ генерируется.

Overlay можно менять по размеру, положению и прозрачности.

А еще есть транскрибация из файлов, саммари встреч, протокол встречи и много еще другого. Буду рад предложениям по улучшению функционала, в статью не получается все фичи приземлить.

66