Приветствую, товарищи разработчики. В этой статье я бы хотел поднять небольшой эксперимент, на который я потратил 2 недели ленивого анализа и разработки. Как понятно из названия, речь пойдет о десктопном агенте на базе open-source языковой модели от Google - Gemma 3 на 1 миллиард параметров. Сразу уточню, что это моя первая статья, поэтому если возникнет критика по поводу оформления или подачи - прошу расписать в комментариях.
С чего началось
А началось все со статьи на форуме Reddit, где парень, используя Ollama, Gemma 3 1b и
n-ное количество API, создал себе настольного браузерного агента, который бы мог переводить текст, присылать погоду, а так же серфить просторы Интернета с помощью API Serp.dev.
Сама идея мне очень понравилась, потому как бо́льшая часть решений в области настольных агентов/помощников предполагала использование API популярных моделей, что сразу же делало человека зависимым от Интернет соединения и оплаты токенов или подписок. Но что мне не давало покоя именно в этом решении, так это использование API для веб-поиска, перевода и информации о погоде, что так же делало вас зависимым от провайдеров, для определенных функций. И тут я задумался, а как можно все это сделать, без использования API?
Продолжилось тем, что у меня уже был код-франкенштейн голосового помощника, но без языковой модели и который мог только выполнять какие то определенные базовые команды (по типу: сделай громкость 5, открой телеграм, закрой телеграм и т.д). На основе этого кода я и продолжил разработку собственного агента.
Ради эксперимента, я использовал библиотеку llama-cpp-python (библиотека на основе llama.cpp), вместо Ollama, но вам все же советую использовать Ollama, т.к для установки llama-cpp-python, так же потребуется установить файлы для компиляции C++ (я в этом плане не ломал голову и просто установил Visual Studio со всеми нужными файлами, что в итоге заняло у меня 11 гб памяти).
К сожалению первого варианта кода не осталось, поэтому объясню вкратце, как было и как стало. До добавления языковой модели, был простейший алгоритм, который по голосовым командам, которые обрабатывались с помощью библиотеки vosk, малой модели vosk-small-ru-0.22 и pyttsx3, мог выполнять самые простые и базовые действия: измени громкость, открой приложение, открой ютуб и т.д. (По поводу моделей vosk, советую вам посетить их страницу на huggingface, т.к видел там модель vosk-small-streaming-ru, по всей видимости как раз для стриминговой записи).
К сожалению,я так до сих пор и не разобрался, как сделать автоматическое открывание всех установленных приложений на ПК (была мысль поместить ярлыки в ту же папку, что и алгоритм и прописать открытие ярлыков от туда, или же сделать скрипт, который бы запускался при первом запуске и парсил расположения всех приложений с папки Microsoft/Windows/Programs, но руки так и не дошли до этого), поэтому создал файл config, в котором прописаны расположения нужных мне файлов, которые сможет открывать алгоритм.
Итак, взяв статью, взяв модель Gemma 3 1b, взяв свой алгоритм и потратив около 2-х недель на «разработку» — получился агент, который может: отвечать на базовые вопросы, используя Gemma, искать информацию в интернете по ключевому слову «найди» и выдавать краткую выжимку из сайтов, все так же используя Gemma, для этого используется python библиотеки bs4, request, urllib.parse (никаких API!), открывать закрывать приложения (пути которых прописаны в отдельном файле config), говорить текущее время, ставить таймеры, напоминания, выключать/перезагружать/отправлять в сонный режим ПК. Возможно можно добавить еще какой то функционал, но пока для себя считаю это базовым минимумом.
Пояснения по работе:
1) агент работает по принципу ловли ключевых слов: найди, какое время, сколько время, поставь напоминание на, громкость 5(или громкость 15%), если же ключевое слово отсутствует, в таком случае запрос передается LLM и выводится ее ответ.
2) агент работает ТОЛЬКО в голосовом режиме, то есть он постоянно прослушивает микрофон, но пока не услышит ключевое слово - не реагирует, в режиме прослушки агент нагружает процессор (AMD Ryzen 5 5600H) на +-1%, визуально это выглядит так:

То есть vosk постоянно записывает, что вы говорите и разбивает на партии, как только проходит 2 секунды тишины - партия заканчивается и создается полноценное сообщение, которое уже анализируется отдельной функцией на предмет активационного слова и ключевых слов, если активационное слово отсутствует - то vosk с pyttsx3 просто дальше прослушивает до момента активационного слова, если активационное слово есть, а ключевых нет, то агент отвечает базово: "Я слушаю, какую команду выполнить", если же вместе с активационным словом есть какие то не ключевые слова, тогда сообщение передается запросом к LLM.
(В будущем я думаю добавить ему визуальное оформление, сделать страницу в браузере через localhost, либо может использовать какие-нибудь уже готовые open-source решения)
3) Gemma 3 1b не поддерживает вызов инструментов, как ее старшие сестры Gemma 3 12b и Gemma 3 27b, поэтому в очень много возни с базовым функционалом, но выбор пал именно на Gemma 3 1b в виду ее малых требований и в виду эксперимента, как/зачем/почему вообще можно использовать такие маленькие модели. (Планирую в дальнейшем использовать Gemma 3 4b, т.к она мультимодальна и поддерживает обработку фотографий, на этом фоне постараться сделать мультимодального агента, который сможет самостоятельно заходить в браузер, искать информацию и т.д, но это только в будущем)
Больше внимание я бы хотел заострить на функции web_search, потому как в нее было вложено больше усилий. Работает функция по такому принципу: агент, получая ключевое слово найди, направляет запрос в браузер duckduckgo lite(был выбран именно lite, т.к мне была важна скорость парсинга и соответственно ответа) то есть как это выглядит: «Агент, найди кто такой Алан Тьюринг» — функция сразу улавливает ключевое слово «найди» и все что после него — отправляет в виде запроса в браузер, через библиотеку request. На поиск страниц отводится 6 секунд, эта настройка указывается в файле config, можно и меньше, но я выбрал 6 секунд, оптимально для разного Интернет соединения. Затем функция парсит первые +-10 страниц с помощью библиотеки bs4, но для запроса в LLM использует только первые 2, с которых она смогла собрать информацию, предварительно очистив от всяких скриптов, стилей хедеров, футеров и т.д и приведя весь текст к единому стилю. После этого, вся собранная информация вместе с отдельным промптом передается в запрос к LLM, выглядит так:

Без использования функции web_search, модель выдает такой ответ:

Единственная проблема — это долгая обработка запроса, то есть 6 секунд на поиск информации, затем парсинг страниц (с каждой страницы по 2500 символов, можно больше — но тогда будет еще медленнее), затем приведения к единому стилю, после этого запрос в LLM и только потом ответ, в итоге вся процедура занимает около 10–20 секунд, что на самом деле очень долго, но пока это максимальный минимум, который я смог я выжать. (Для уточнения, я использую llama‑cpp‑python на CPU, т.к не смог разобраться, как использовать на GPU, вернее было установлено CUDA, но почему то llama‑cpp‑python постоянно скачивает CPU колесо, даже с явным указанием на скачиванием CUDA колеса, если кто то знает, в чем проблема — подскажите, пожалуйста).
Касательно остальных функций, я думаю нет особого смысла углубляться, т.к это базовые функции, которые сможет реализовать даже 8-ми классник.
Весь код проекта оставлю у себя на Github, зайдите, посмотрите, форкните. tripleguard
ИТОГ: В целом сама идея не нова, но лично я не особо люблю быть от кого то зависимым, именно поэтому я решил писать все функции так называемых «инструментов» сам, без использования API или каких то сторонних коммерческих решений.
Я думаю, что в ближайшем будущем начнут появляться более улучшенные версии настольных агентов, не требующих обязательного подключения к Интернету и не требующих огромных вычислительных мощностей. Было бы замечательным конечно, если бы вдруг вышла мультимодальная модель, весом в 1гб (мечтать не вредно!), но пока довольствуемся тем, что есть. В целом, учитывая вес модели — возможности феноменальны, а это только я реализовал базовый минимум, что будет при роскошном максимуме? Агент умеет искать информацию в интернете, имеет определенный контроль над вашим ПК, может поговорить с вами и если честно, все еще не верится, что Джарвис это не фантастика, а уже сегодняшний день. Мне, как любителю все автоматизировать очень нравится сама суть того, что ты можешь уйти по делам, предварительно дав команду агенту найти какую‑то информацию или же подготовить какой‑то список, или же запустить игру и попросить его погриндить какую‑нибудь локацию, что в определенной степени очен ь сильно экономит время и улучшает производительность.
Следующий так называемый эксперимент хочу провести с Gemma 3 4b, и попробовать через постоянные скриншоты добиться (минимум!) результата нахождения информации в интернете и переход по вкладкам.
janvarev
Учитывая описанное в статье, все-таки рекомендую посмотреть в сторону опенсорс голосового помощника Ирины: https://habr.com/ru/articles/932072/ (1000 звезд Гитхаба уже)
Есть:
vosk stt для стримингового входа (дешево, быстро)
плагинная система (писать свои команды)
при необходимости ai tools интеграции с OpenAI-совместимым сервером (хоть с внешним сервисом, хоть с чем-то запущенным в ollama, без разницы)
Ну и еще куча всего по мелочи - клиент-сервер, плагины от комьюнити и прочее.
Насчет "долгой работы с интернетом" - в предложенном в статье варианте да, практически неизбежна задержка в 10-20 секунд. Лучшие варианты - только напрямую вызывать perplexity llm онлайн по api, она быстро у себя сделает и даст ответ, иначе все +- долго.
tripleguard Автор
спасибо за ссылку Ирины, обязательно посмотрю. А по поводу API perplexity, я не спорю, я бы мог использовать и API Serp.dev, как у автора с реддита. Но задумка была именно в том, чтобы не использовать API, да, это с одной стороны не современный подход, но хотелось бы сделать все самостоятельно, без надобности от кого то зависеть.
janvarev
Просто если "искать в интернете" - все равно от кого-то зависеть придется )))
Если было бы чисто LLM, то можно было бы да, все автономно сделать. Но поиск так не сделаешь.