Приложение Т—Ж
В нем читать удобнее

Я создал виртуального голосового ассистента, который работает без интернета

Обсудить

Этот текст написан в Сообществе, в нем сохранены авторский стиль и орфография

Введение

Вообще, Siri на Mac — штука странная. Чтобы она просто открыла приложение или сказала время, нужен интернет. Без сети она молчит. Вообще никак. Это бесит особенно, когда сейчас начали жестко глушить интернет. Ну, сами знаете.

Но я понимаю, почему Apple требует интернет для работы Siri. Apple обрабатывает голос в облаке, там своя нейросеть, свои алгоритмы. Но когда я говорю "открой Safari" — зачем тут облако? Это же просто команда.

К тому же меня напрягает, что мой голос куда-то улетает. Даже если Apple ничего с ним не делает. Просто не нравится.

В какой-то момент я подумал: а почему бы не сделать своего ассистента?

Причем такого, который работает локально. Без интернета. Без отправки данных.

Как я начал

Сначала я, конечно же погуглил. Оказалось, что на macOS есть встроенные штуки для распознавания речи. SFSpeechRecognizer. Он умеет распознавать голос в текст прямо на устройстве. Без облака. Это стандартная библиотека от Apple.

Я хотел взять готовые библиотеки, скрипты и прочие штуки и написать крутого ассистента. Но все, что находил, работало через облако или было написано на Swift с привязкой к экосистеме Apple. А я хотел сделать независимую и очень быструю штуку. Которая не умрет, если Apple что-то поменяет.

Поэтому я решил писать на чистом C (без внедрения остальных языков, имеется в виду).

Работа со звуком операционной системы

Прежде чем приступить к написанию проекта, мне нужно было уяснить, как вообще работает звук. Как работает микрофон. Что такое потоки звука, что такое сэмплы, что за дуплексные и полнодуплексные устройства. Естественно, ответы на эти вопросы я не знал. К счастью, в документации PortAudio (библиотека для работы со звуком) это рассказывалось. Я очень обрадовался.

В общем и целом на обучение — что такое звук, как он представляется в операционной системе, как компьютер понимает, что я говорю в микрофон и тому подобное — у меня ушло 15 дней. Да, я запомнил эти дни.

Захват звука — PortAudio

Чтобы взять звук с микрофона, я использовал PortAudio. Это кросс-платформенная библиотека для работы с аудио. На macOS она общается с CoreAudio, но мне не пришлось лезть в низкоуровневые дебри.

PortAudio старый, но благо, надежный.

Распознавание речи — Whisper

Чтобы распознавать голос, я взял библиотеку Whisper. Она весьма нелегкая по сложности, но работает локально, без интернета. Качество — очень приличное, даже на фоне шума.

Я уделил документации по этой библиотеке примерно неделю, написал несколько тестовых программ.

Даже на старом MacBook Air от 2015 года команда распознается за доли секунды. На M1 — вообще мгновенно. Хах, это не может не радовать.

Графика

Интерфейс приложения — окошко, кружок, который пульсирует, текст, настройки. Всё это нарисовано с помощью raylib. К счастью, на raylib я раньше разрабатывал игры и представляю, как с ней работать.

Я просто создал окно, нарисовал кружок и текст сверху.

Вручную.

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

Парсер. Никакого AI

Когда я только начинал, меня пугала мысль: "Как научить программу понимать, что сказал человек?"

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

Поэтому я пошел другим путем.

Я написал простой анализатор команд. Он работает так: берет текст, приводит к нижнему регистру, выбрасывает всякую ерунду типа "пожалуйста", "прошу", "ну-ка" и потом смотрит на ключевые слова.

Например, если в тексте есть слово "open", а после него название приложения — значит, надо открыть программу. Если есть "brightness" и рядом число — значит, изменить яркость. Если есть "shutdown" или "выключи" — да, подтверждение, но потом выключить компьютер.

Никаких нейросетей. Простой алгоритм, который работает быстро, предсказуемо и без интернета.

Контекст. Забываем про команды сразу после выполнения

Я специально не стал делать поддержку диалогов. Почему? Потому что ассистент не должен запоминать, что ты сказал минуту назад. Это называется stateless.

Сказал команду — выполнил — забыл. Никакой памяти между запусками. Никаких "помни, что я открыл браузер пять минут назад".

Так проще для приватности. Программа не хранит историю твоих команд (если ты сам не включишь логирование, но по умолчанию — выключено).

Текстовый режим

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

Нажимаешь T (обычная буква, без модификаторов), открывается поле ввода. Печатаешь команду, Enter — выполняется. Алгоритм парсинга тот же самый, что для голоса.

Кстати, это очень помогло при отладке. Я мог тестировать команды без произношения вслух, просто вбивая текст в окошке.

Что в итоге

Command Siri — это голосовой ассистент, который работает локально, не шпионит и не требует интернета.

Он написан на чистом C, использует Whisper для распознавания речи, PortAudio для захвата звука и raylib для графики. Без Swift, без XIB, без магии Apple.

Он весит копейки, запускается в три клика после скачивания, и им может пользоваться любой владелец Mac.

А что с обычной Siri

Apple сделала сложную систему, которая умна, но требует облака и интернета. Мой ассистент — нет.

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

Но он выполняет основные команды даже в бункере без связи.

И не шпионит.

Ну, я себе такой сделал. Может, кому-то тоже пригодится.

Вопросы под постом отвечу.

Вот что еще мы писали по этой теме
Сообщество