Персональный ИИ агент на Android, или как я до этого докатился
Как всё начиналось.
Полтора года назад, собрав себе нормальный комп для игрушек, я зачем-то полез в тему ИИ. Установил LM Studio, Ollama и открыл, как говорится, для себя чудный мир локальных LLM моделей. Я запойный, со мной так нельзя. Комп стал использоваться исключительно для бесконечных экспериментов, типа: а что можно из него еще выжать, а как будет работать модель превышающая в разы мою доступную память на видюшке. Захотелось иметь своего агента (я тогда еще слабо понимал что это, и как оно должно работать). Просто было желание создать "умного болванчика", чтобы работал локально, без прослоек и абонентской платы, изучить как это работает и что вообще значит "модель способна использовать инструменты", а еще понять какую практическую пользу из этого можно извлечь кроме беспонтовой генерации текстов.
Начал с Python. На коленке собрал скрипт, который подтягивал GGUF-модели прямо в память — без Ollama, LM Studio и прочих тяжелых штук. Просто качаешь файл, запускаешь скрипт, модель завелась. Прикрутил интерфейс на Tkinter, набросал базовую библиотеку навыков и динамическое управление параметрами семплирования (это сейчас уже такие умные слова знаю).Работало это, честно говоря, криво. Память безбожно текла, скрипт периодически падал с невнятными ошибками, но сам факт того, что в одно лицо можно собрать работающего ИИ-помощника, дико драйвил (потом сдох SSD со всеми исходниками, и с тех пор делаю бэкапы в 4 различных местах. Сам дурак.)
Ещё были эксперименты с веб-мордами на JS, PHP и связках с тем же Python. В общем, засосало знатно. Ну и очередным этапом было использование подручного мобильного железа, а именно телефона. Очередной эксперимент на тему: "а что из этого получится?".
Почему Android, а не очередной CLI-скрипт
Большинство ИИ-приложений под Android сегодня — это либо просто красивая обертка над API OpenAI/Anthropic, либо платные подписки, либо то, что в принципе не делает что я хочу.
Мне же хотелось, чтобы агент реально жил в экосистеме телефона. Имел доступ к контактам, календарю, GPS, мог читать уведомления, отправлять SMS и понимал контекст: где я нахожусь, какая погода за окном и кто мне звонил час назад. Не просто чат-бот, а реальный "агент".
Нюанс в том, что Kotlin я не знал от слова совсем. Но раз уж на дворе эпоха «вайб-кодинга», я решил пойти по хардкору: описывал концепт нейронке, тестировал сгенерированное, правил промпты, ругался на баги и снова запускал, объяснял "на пальцах" что я хочу, как должна выглядеть архитектура процесса, и т.д. Кто-то скажет, что это не настоящий кодинг — и спорить не буду. Но такой подход освобождает кучу времени от зубрежки синтаксиса в пользу проектирования логики. Под капотом, задеплоенного в локальном докере openclaw, крутился DeepSeek, со сложными конструкциями справлялся сносно, а мой бюджет не улетел в стратосферу.
Что получилось в итоге
Больше скриншотов в репозитории
Проект получил имя PAi Android (сокращение выбрано от PygmalionAi , как-то прицепился этот PAi ко всем моим поделкам). Это персональный агент, где вся логика, память и менеджер задач крутятся локально на устройстве, а LLM используется исключительно как «мозг» для разбора намерений и принятия решений.
Упрощенная схема работы выглядит так:
1. Пользователь пишет запрос в чат.
2. DecisionEngine собирает контекст (координаты, заряд батареи, календарь, уведомления).
3. LLM анализирует кучу этой информации и решает, какой инструмент вызвать.
4. TaskQueue выполняет команду, а результат возвращается в чат.
Ключевые фичи, которые удалось завести:
● ReAct-цикл без хардкода: Агент сам решает, какие навыки запустить на основе системного промпта. Никаких жестких ветвлений if/else для сценариев.
● Локальный запуск: Прямо в приложение встроен рантайм LiteRT (от Google AI Edge). Можно гонять урезанные Gemma 4 E2B или Qwen3 0.6B прямо в офлайне (был отдельный гемор заставить их работать).
● Умная маршрутизация (Smart Router): Система умеет делить задачи между локальной моделью (для простых запросов) и сетевым API (для тяжелой логики). Причем порог сложности пользователь может крутить в настройках.
● Набор базовых навыков: Интеграция с системными вызовами (звонки, SMS, контакты, GPS), поиск в сети (через Google/Tavily/DuckDuckGo) и работа с почтой по IMAP/SMTP.
Отдельная гордость и боль — Python-песочница. Тянуть за собой Termux и пытаться бесшовно подружить UI на Jetpack Compose с его фоновыми процессами показалось диким оверкиллом. В итоге взял Chaquopy — плагин, который встраивает Python-рантайм прямо в APK. Скрипты исполняются нативно и имеют доступ к файловой системе. Да, для продакшна библиотека платная, но для личного хобби-проекта — идеальный костыль. А ещё это даёт агенту возможность создавать себе навыки под конкретную задачу (там на самом деле тоже есть куча заморочек с тем как модель может сгенерировать код, но это больше вопрос к самой используемой модели).
Немного про вес APK: Chaquopy — штука тяжелая. Базовый APK весил в районе 85 МБ, а после добавления рантайма Python и библиотеки numpy раздулся до 160 МБ. ProGuard и R8 немного спасают ситуацию, но глобально с этим приходится просто смириться. Зато работает из коробки.
● Контекстная память: Агент вытаскивает факты из диалогов («пользователь живет в Мск», «предпочитает есть вяленые персики», «профессиональный переворачиватель пингвинов») и сохраняет их в базу Room с разным уровнем уверенности, подмешивая к следующим запросам (это даёт ту самую изюминку персонализации общения). В принципе, после накопления достаточного количество фактов, агент достаточно лихо может управляться с ними, строить маршруты по знаковым точкам которые сохранил по собственной инициативе или по просьбе пользователя, понимать что сейчас он находится не рядом с "домом", учитывать предпочтения пользователя в музыке, еде и пр. Факты можно редактировать вручную, есть отдельный пункт меню для работы с памятью.
● Внутренний планировщик: Навесил cron-задачи прямо внутри ForegroundService (чтобы Android не прибил процесс). Теперь агент может каждый час делать heartbeat-проверку погоды, а утром и вечером выдавать новостные дайджесты.
Как это выглядит вживую
Простой пример: В чат пишется стандартный человеческий запрос: «Отправь маме SMS, что буду через час, и закинь встречу с клиентом в календарь на завтра в час дня».Модель не просто парсит текст, а возвращает структурированный JSON:
Вот такого типа:
{ "tool": "SmsSkill.send", "params": { "to": "Мама", "body": "Буду через час" } }
После выполнения первого шага TaskQueue подхватывает вторую часть запроса и дергает CalendarTool. Всё происходит в рамках одного диалога без прыжков по приложениям.
Что далось сложнее всего (Технические факапы)
Многоступенчатые задачи и галлюцинации
Заставить агента доводить до конца цепочку из 3-4 действий — это был отдельный ад. Типичный сценарий: просишь найти в почте письмо, вытащить оттуда адрес, пробить его по GPS и скинуть отчет. Модель могла прочитать письмо, обрадоваться результату первого шага и выдать ответ в чат, напрочь забыв про остальные пункты. Или, что еще хуже, упасть в ошибку на середине и сделать вид, что так и надо (локалка в принципе может это и сейчас делать из-за ограничений контекстного окна и не особо развитыми способностями работы с инструментами).
Пришлось переписать логику на JSON-статусы (completed, failed, needs_input) для каждого микро-шага. Теперь TaskQueue жестко контролирует выполнение цепочки. Кроме того, пришлось прописать в промптах жесткое правило: если модель пытается использовать данные, которые она «якобы помнит», но физически не вызывала инструмент — этот ответ блокируется как галлюцинация. Помогло, но костыли в логике еще торчат.
Настройка гибридного роутера
Идея с функцией "Гибрида" на бумаге выглядела красиво: модель, выбранная основной, разбивает запрос на подзадачи (с системой баллов сложности), в зависимости от настроек порога сложности, задачи распределяются между моделями для исполнения. На практике локальные модели в RAM постоянно завышают свои возможности, путают параметры инструментов и ломают весь флоу.
Сейчас это работает так: облако разбивает задачу на подшаги и выставляет каждому оценку сложности от 1 до 10. Если оценка ниже порога, заданного пользователем, запрос уходит на локальную модель (LiteRT). Если выше — отрабатывает сетевая. Но если локальный шаг падает (например, модель ошиблась в синтаксисе параметров), то часто ломается вся цепочка. Фича до сих пор экспериментальная, отказоустойчивость там пока слабая.
Борьба с энергосбережением Android
Начиная с Android 14, система закручивает гайки фоновым процессам со страшной силой. Единственный способ заставить планировщик задач жить дольше пяти минут — это запуск ForegroundService с неудаляемым уведомлением в шторке. Да, плашка «PAi работает в фоне» постоянно мозолит глаза, зато агент не засыпает и исправно собирает логи.
Что касается локальных моделей, то Gemma 4 E2B отъедает около 3.6 ГБ оперативной памяти. На средних смартфонах это выносит половину доступных ресурсов. Чтобы телефон не превращался в кирпич, пришлось написать таймер автовыгрузки.В настройках можно выставить «прибить модель через 30 секунд/5 минут после перехода на сетевую модель».
Вопросы безопасности
Тут коротко:
1. Все API-ключи хранятся только у вас в локальной базе Room. Ни на какие сторонние сервера они не улетают.
2. С Python-песочницей пока все небезопасно. Полной изоляции кода внутри Chaquopy нет, сгенерированные скрипты выполняются с теми же правами, что и само приложение. В планах — прикрутить белый список разрешенных импортов и ограничить доступ к системным папкам.
В общем-то описывать это детище можно долго, и получается у меня немного сумбурно, проще попробовать самим. Роудмэпа как такового нет, буду что-то приделывать по необходимости. Была идея сделать полноценный навык для управления умным домом, но там есть свои нюансы со сканированием сети и получением списка устройств(есть пока в зачаточном состоянии в меню навыков, но не работает еще как задумано). Фичи с внешними и "самосозданными" навыками тоже носят экспериментальный характер, что-то работает слёту, что-то докручивать приходится вручную. В целом есть куда расти и развиваться.
Я тестировал это всё на Samsung S22 Ultra и в эмуляторе Android Studio.
Почему open-source и бесплатно
Проект делался для себя, продавать тут нечего. Никаких скрытых подписок или «PRO-версий». Если у вас крутится Ollama на домашнем ПК или LM Studio на ноутбуке — просто вбиваете свой IP/порт в кастомный эндпоинт и пользуетесь. Нужен DeepSeek или OpenAI — вставляете свой ключ. Для универсальности пришлось еще и поработать над англоязычной локализацией (изначально проект был только русскоязычным).Ссылки на исходники и готовый APK оставляю ниже. Код местами корявый (все-таки вайб-кодинг дает о себе знать), так что пулл-реквесты и здоровая критика в issues только приветствуются. Особенно интересны идеи по изоляции питоновской песочницы и оптимизации локального рантайма.
Спасибо за внимание!