Bollywood HungamaRamayana producer Namit Malhotra calls Los Angeles premiere his “Oscar moment” ahead of November 5 releaseEl ComercioLa hoja de ruta de Estados Unidos para Venezuela: reformas, vacancia de Delcy Rodríguez y eleccionesPunchBREAKING: NELFUND introduces token system to stop double payment of school feesThe Jerusalem PostQatari mediators present peace deal to Iran in mediation effort, official saysInquirer EntertainmentDonny Pangilinan felt ‘awkward’ over Showtime Baby Doll Juby’s dream about himDaily MaverickWildlife is not livestock — Aucamp’s dangerous plan to turn conservation into agricultureInquirerMimaropa drug bust in Palawan yields ₱51,884 worth of ‘shabu’ZDF heuteEntdecken Sie das ZDF-NachrichtenstudioSky TG24Meteo, freddo e maltempo in Italia nel weekend di Halloween e Ognissanti. Le previsioniVariety‘One Cut of the Dead’ Director Ueda Shinichiro on Turning a Microdrama Into Tokyo Feature ‘Earthlings in Love’NumeramaÀ mi chemin entre le VTC et le longtail, le vélo électrique Nakamura Crosscity+ perd 500 €DeadlineRussell Brand Forcefully Denies Charges As He Gives Evidence During Rape Trial
The Daily Newsstand · Free, Always
Wednesday, October 28, 2026

Дообучаем Gemma 26B на MacBook с 24 ГБ памяти: как устроен голосовой помощник, который работает без интернета

Translate

Полгода я собираю голосового помощника для macOS, который не просто отвечает, а выполняет действия на компьютере: открывает папки, ищет файлы, включает фильмы, читает почту. Главное условие, которое я себе поставил, — языковая модель должна работать локально, на том же Mac, без облака и подписок. В статье расскажу, из чего состоит такая система, как я дообучал модель на ноутбуке и на какие грабли наступил по дороге.

Общая схема

Приложение написано на Electron. Вокруг окна работают несколько отдельных процессов:

  • Слух — процесс на Python: детектор речи (VAD) и распознавание через Whisper в сборке под MLX (Apple Silicon).

  • Мозг — llama.cpp (llama-server) с моделью Gemma 4 26B A4B в 4-битной квантизации и LoRA-насадкой в формате GGUF.

  • Голос — синтез речи на Qwen-TTS по эталонной записи голоса.

  • Помощник на Swift — системные вещи macOS: звук, микрофон, эхо.

Процессы общаются с окном сообщениями. Если какой-то из них падает, его перезапускает сторож, — например, голос поднимается заново примерно за 7 секунд.

Мозг: модель выбирает инструмент, а не пишет ответ

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

{"think": "...", "tool": "list_folder", "args": {"place": "Загрузки"}, "say": "Открываю загрузки", "next": false}

tool — имя инструмента из списка (папки, поиск файлов, видео, почта, браузер, заметки и т. д.), args — параметры, say — что сказать вслух, next — нужен ли следующий шаг. Окно выполняет инструмент и, если надо, отдаёт результат обратно модели. Всё, что меняет или удаляет файлы, выполняется только после голосового подтверждения.

Тот же формат шага отдают и внешние модели (ChatGPT, Claude, Gemini), если пользователь их подключил: для них инструменты передаются родными вызовами функций, а окно не замечает разницы. Когда выбран внешний мозг, локальный llama-server останавливается и освобождает около 13 ГБ памяти.

Зачем дообучать

Без дообучения Gemma с подробной системной подсказкой (около 5 тысяч токенов правил и примеров) справлялась неплохо: 89 % верных шагов на моём наборе проверок при медиане 1,94 секунды на ответ. Но длинная подсказка дорога по времени, а ошибки были системные: модель путала похожие команды («поставь» фильм или музыку), забывала параметры, переспрашивала там, где всё ясно.

Идея — перенести правила из подсказки в веса: дообучить небольшую LoRA-насадку и обходиться короткой подсказкой.

Стенд — главное, что я сделал

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

Два урока, которые дорого мне стоили:

  • Сравнивать надо весь разговор, а не одну реплику. Пока проверка шла по отдельным репликам, из неё выпадало больше 400 примеров с цепочками — и картина была слишком радужной.

  • Примеры, собранные по ошибкам стенда, завышают оценку на этом же стенде. Для честной проверки нужен свежий набор, на котором не учились ни модель, ни я сам, когда писал примеры.

Как учить 26B на ноутбуке

Обучение — QLoRA через mlx-lm на MacBook с 24 ГБ общей памяти. Модель 26B — MoE с ~4B активных параметров, в 4 битах она помещается, но впритык:

  • Длина примера — не больше ~1400 токенов, иначе Metal падает с Insufficient Memory. Поэтому учить с полной подсказкой на Mac невозможно — ещё одна причина переносить правила в веса. Перед обучением отдельный скрипт отбрасывает примеры, которые не влезают.

  • Скорость — 0,25–0,4 шага в секунду, один проход — около трёх часов. Учу ночью.

  • Mac засыпает. Однажды он уснул после обучения, и проверка на стенде шла шесть часов. Теперь вся цепочка (обучение → перевод в GGUF → стенд) запускается под одним caffeinate -i -s, а не каждый шаг отдельно.

  • Обучение срывается по памяти. Рядом работают браузер, индексация Spotlight, своп доходит до 6–7 ГБ. Срывы совпадали с проверкой на валидационной выборке каждые 200–400 шагов. Решение: сохранять точки, автоматически продолжать с последней (--resume-adapter-file), а валидацию на поздних этапах отключить.

  • Не правьте shell-скрипт, пока он выполняется. Bash читает файл по ходу, и правка на лету уронила цепочку с syntax error на строке, которой в момент запуска ещё не было.

  • Для Gemma 4 понадобились две небольшие правки в mlx-lm, а для перевода насадки в GGUF — отдельное окружение с torch.

Что получилось по версиям

Версия

Стенд

Медиана

Без насадки, полная подсказка

475 / 533 (89 %)

1,94 с

v3, полная подсказка

487 / 533 (91 %)

1,63 с

v4 (новые разделы)

684 / 766

—

v4b, короткая подсказка

703 / 766

1,64 с

Самое интересное — версия v4. Я добавил в обучение новые разделы (вопросы к документам, чертежи, английский, цепочки), и на них она выросла, а на старых командах просела: «включи <фильм>» стало запускать музыку, «найди фильм» потеряло тип поиска, «свою папку» модель начала путать с папкой заметок, потому что слово часто встречалось в новых примерах.

Помогла не переделка с нуля, а ремонтный проход: 416 примеров ровно на эти случаи плюс случайная выборка старых и новых данных (всего около 1100), доучивание v4 ещё на ~1000 шагов с меньшим шагом обучения (1,5e-5 вместо 3e-5), без валидации. Это заняло около 70 минут — и дало лучший результат за всё время. Обучение с нуля, наоборот, заставляло модель забывать старое.

Слух: имя должно быть услышано

Помощник выполняет команды только после своего имени. Сложность в том, что во время фильма имя тонет в звуке колонок, и Whisper его просто не слышит.

Решение — приглушать фильм не по имени, а по началу речи. Детектор речи считает, на сколько децибел голос громче «плавающего пола» — медленного среднего громкости без речи. Если речь громче фона на 6 дБ и это не эхо собственного голоса помощника, звук фильма сразу становится тише. Если за 2,5 секунды имя не прозвучало — громкость возвращается.

Голос: «сэр» и «сёр»

Синтез детерминирован: для одной и той же фразы всегда выбирается один и тот же «дубль» (seed считается по тексту). Выяснилось, что примерно каждый третий дубль произносит «сэр» как «сёр». Менять написание бесполезно: «сер», «сээр», «с эр» ломают соседние слова.

Помогла акустика. У гласной «э» вторая форманта около 2000–2300 Гц, у «ё» и английского sir — около 750–900 Гц. После синтеза слово находится по таймингам Whisper, гласная измеряется LPC-анализом (12-й порядок), и если F2 слишком низкая — фраза пересинтезируется другим дублем. Проверка занимает около 3 мс; на наборе проверок детектор ловит 39 сбоев из 46. Сам Whisper как детектор не годится: он пишет «сэр» по контексту, даже когда слышно «сёр».

«Второй мозг» без своего индекса

Вопрос «какая сумма в смете на ремонт?» должен находить ответ в документах, хотя пользователь не называет файл. Свою векторную базу я делать не стал — на macOS уже есть Spotlight с полнотекстовым индексом. Слова вопроса (без стоп-слов) ищутся в содержимом документов, копии одного файла схлопываются, два-три лучших файла читаются, и модели отдаются окна текста по ~600 знаков вокруг найденных слов. Ответ — только по этим выдержкам и с названием документа; если в них ничего нет, помощник так и говорит. Поиск занимает 60–700 мс и не ест память.

Чертежи DWG без AutoCAD

Формат DWG закрытый, своего просмотрщика у macOS нет. Его читает LibreDWG, собранная в WebAssembly: чертёж разбирается в отдельном потоке (чтобы окно и голос не замирали) и превращается в SVG. Результат кэшируется по пути и времени изменения файла, поэтому повторное открытие мгновенное.

Выводы

  • Замороженный стенд важнее любой модели: без него невозможно понять, стало лучше или хуже.

  • На ноутбуке можно дообучить 26B, если резать длину примеров, сохранять точки и держать машину без сна.

  • Новые умения лучше доучивать поверх прежней насадки на смеси старых и новых данных; переобучение с нуля забывает старое.

  • Перенос правил из подсказки в веса дал и точность, и скорость: короткая подсказка с насадкой обогнала длинную.

Следующий шаг — личная память и самообучение: помощник будет запоминать собственные слова пользователя и учиться на фразах, которые не понял. Об этом, если будет интересно, расскажу отдельно.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.

Дообучаем Gemma 26B на MacBook с 24 ГБ памяти: как устроен голосовой помощник, который работает без интернета — KioskNews