- BrainTools - https://www.braintools.ru -
Полгода назад я прикинул, сколько рабочих созвонов ежедневно улетает на чужие сервера через модные ИИ-ноутейкеры. Стало как-то неуютно. Otter, Granola, Fireflies — все льют разговоры в облако. Granola в этом году вообще отличилась: The Verge раскопали, что заметки по дефолту доступны любому по ссылке, а на пользовательских данных крутят модели (opt-out ожидаемо зарыт в Enterprise-тарифе).
Решил проверить, можно ли собрать ассистента целиком на своём железе. Спойлер: можно. Работает каждый день на M1 Max под Apache 2.0.
Charoite слушает микрофон и системный звук (через BlackHole, никаких криповых ботов в звонке), транскрибирует на лету, различает голоса, отвечает на вопросы по ходу встречи. А после стопа — обновляет граф знаний в Obsidian: люди, системы, решения, сквозные темы.
Всё крутится локально. GigaAM v3 для русского STT, qwen3.6:35b-a3b и qwen3.5:4b через Ollama, ERes2Net для эмбеддингов голосов.
Про эхо отвечу сразу, пока не набежали в комменты. Микрофон и системный звук пишутся в разные каналы. Когда в BlackHole идёт речь, одновременный микрофонный чанк просто дропается — такой вот дешёвый канальный AEC. В оффлайн-проходе то же самое делается точнее, фильтром по перекрытию интервалов. Так что сидеть в наушниках не обязательно.
Ниже — самые интересные грабли. Их было много.
Нужен был нормальный русский и скорость. GigaAM v3 (Сбер, MIT, крутится через onnx_asr) жует 3-секундный чанк за 0.1–0.6 секунды на M1 Max. И главное — ставит пунктуацию из коробки. Хвостовой знак вопроса триггерит у меня быстрый ответ. Собеседник что-то спросил — через пару секунд на экране висит черновик ответа от первого лица.
Почему не Whisper-large-v3-turbo? На коротких русских чанках он люто галлюцинирует. Он заточен под 30-секундные окна, и обрезки без паддинга просто ломают ему механизм внимания [1]. (Для английского в конфиге есть Parakeet TDT — у него WER 6.32% против 7.44% у Whisper на Open ASR Leaderboard).
Диаризация на лету через эмбеддинги — та еще боль [2]. Все, кто пилил продукты на pyannote, в курсе: DER около 19% на датасете AMI, метки скачут прямо посреди фразы.
Я бенчил три модели эмбеддингов на своих реальных записях: ERes2Net, CAM++ и TitaNet. Выиграла ERes2Net — лучше всего сепарирует своих и чужих. Но чуда не случилось, распределения всё равно пересекаются. У одного и того же человека косинус между чанками обычно держится высоко, а на плохом телефонном канале проседает до 0.29 (при том, что чужие голоса там же дают ≤0.16).
Поэтому в лайве работает не жёсткий порог, а гистерезис. Смена спикера триггерится, только если другой голос стабильно ближе. Серая зона тянется за текущим спикером, новый голос заводится по двум согласным чанкам подряд. Один провалившийся чанк никого не дробит.
А после встречи запускается второй проход. Полная запись диаризуется заново по каналам, эхо режется по перекрытию. Кластеры, наговорившие меньше 25 секунд суммарно за всю встречу, вливаются в соседей — это осколки, а не люди (однажды так вылезло 23 «голоса»). Сегменты заново транскрибируются, и текст собирается в чистые абзацы по говорящим. Живая версия остаётся черновиком рядом.
Самый тупой баг прилетел откуда не ждали. Модель должна мапить имена на метки «Собеседник N» по стенограмме. На первом же групповом созвоне кто-то говорит: «Саш, ну кто это заапрувит?». И LLM радостно вешает на говорящего ярлык «Саша».
Обращение — это почти всегда имя другого человека, но логика [3] модели тут пасует. Плюс звательные падежи. Плюс всякие междометия типа «Опа», которые она принимает за имя.
Лечится двумя слоями. Во-первых, промпт явно объясняет разницу: «имя внутри реплики — это обращение; говорящий — тот, кто ответил следом». Во-вторых, фильтр в постобработке отклоняет имя, если оно звучит только в репликах самой метки и это не самопредставление. Ну и жёсткое правило: имя владельца компа никогда не угадывается — берется только из конфига.
Markdown вместо тяжелых баз. Каждая встреча после стопа парсится в заметку с вики-ссылками. Узлы (Люди/Системы) апдейтятся датированными фактами. Старое не затирается, а помечается как вытесненное. Сквозные темы живут в «Ядрах»: у каждого свой статус и хроника по встречам.
На следующем созвоне, если зацепили старую тему, в тезисы прилетает алертом: «⏮ обсуждали 15.07, статус был такой-то». Матчинг тем пока наивный — стемминг по обрезанным окончаниям. Для коротких названий ядер этого хватает, честная лемматизация в планах.
По сути, это трёхслойная архитектура а-ля Graphiti/Zep (эпизоды → сущности → сообщества), только на чистом маркдауне. Зато работает grep, Obsidian и git.
Перед выбором модели я закопался в свежие статьи по темпоральным графам знаний (Zep, ATOM, T-GRAG, LightRAG). Вывод: планка для structured-экстракции — это модели 30B-класса. Всё, что ниже, начинает сыпать битым JSON и терять онтологию. Поэтому основная модель у меня qwen3.6:35b-a3b. На реальной стенограмме она отдает первый токен за 0.27 с, а полный ответ за 2.2 с (против 1.08 с и 4.5 с у плотной gemma4:26b). MoE с ~3B активных параметров реально быстр на генерации. Prefill длинного контекста, понятно, упирается в память [4], поэтому контекст везде жёстко ограничен.
После встречи генерится выжимка по жесткому фреймворку. Сначала суть одной строкой (BLUF — Bottom Line Up Front, как в военных сводках). Потом темы списком, принятые решения, экшены в формате «кто-что-срок» и открытые вопросы.
В конце идет связка с прошлыми созвонами: «было: перенос релиза (15.07) — сегодня: подтвердили, срок август». Этот кусок собирается из хроники Ядер и двух предыдущих саммари (с отсечкой по дате, чтобы будущее не утекало в прошлое при перегенерации).
И никаких markdown-таблиц. Plain-текст читается везде, а таблицы без рендера превращаются в кровавую кашу.
Первый факап — контекст. У некоторых моделей в Modelfile захардкожено 262144. Если не передать явный num_ctx, Ollama честно пытается аллоцировать под него KV-кэш. Лишние гигабайты улетают в unified memory, мак уходит в жесткий своп, генерация падает на дно (а на тачках послабее это тупо OOM). Потерял на этом вечер. Теперь num_ctx: 8192 гвоздями прибито в каждом вызове.
Второй прикол — бюджет оперативки. 32 ГБ — это qwen3.6 (23 ГБ) плюс одна лёгкая моделька. Когда я на радостях подкинул третью, всё встало колом: Ollama начала вытеснять модели по кругу. В общем, две модели. Не три.
Кстати, про лёгкую модель: qwen3.5:4b против gemma4 (E4B). На моих задачах qwen точнее в классификации вопросов, быстрее в тезисах (2.9 с против 3.3 с) и жрет втрое меньше RAM в дефолтных сборках Ollama (3.4 против 9.6 ГБ). И да, это сравнение того, что реально скачивается по умолчанию, а не параметров при равном кванте. Пользователю в итоге важен резидентный размер.
Никуда. Сетевые вызовы идут строго на localhost к своей же Ollama. Проверяется не честным словом разработчика, а хоть через Wireshark, хоть через LuLu. Телеметрии ноль.
Полная аудиозапись лежит два дня (чисто для оффлайн-пересборки) и сносится. Эмбеддинги голосов живут в оперативке только во время звонка — слепков на диске нет. Фича с узнаванием голосов между встречами (когда я её допилю) этот принцип не сломает: она будет строго опциональной, с хранением эмбеддингов локально и явным opt-in. По дефолту биометрия по-прежнему не пишется. Облачный слой (разбор встречи через Claude CLI по подписке) в коде существует, но выключен по умолчанию и честно задокументирован.
Пилить меню-бар аппку под macOS, английские промпты и опциональное узнавание голосов между встречами (эмбеддинг → узел человека в графе). С последним пока нормально не справился ни один коммерческий продукт на рынке. Ну и просмотрщик графа.
Если кто-то собирал похожего монстра — залетайте в комменты. Особенно интересно послушать про диаризацию. Печёнкой чую, я там собрал ещё не все грабли.
Автор: Charoiteai
Источник [6]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/33385
URLs in this post:
[1] внимания: http://www.braintools.ru/article/7595
[2] боль: http://www.braintools.ru/article/9901
[3] логика: http://www.braintools.ru/article/7640
[4] память: http://www.braintools.ru/article/4140
[5] Код, бенчи и полная документация лежат на Гитахбе: https://github.com/charoiteai/Charoite_audio
[6] Источник: https://habr.com/ru/articles/1061864/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1061864
Нажмите здесь для печати.