Hermes в телефоне: свой клиент вместо мессенджеров
Работать с Hermes через мессенджеры неудобно, поэтому я написал для него свой клиент под Android. Сегодня вышла первая сборка, v0.1.0-rc11.
Что такое Hermes
Hermes — открытый агент от Nous Research под лицензией MIT2. Он работает на сервере, а не на ноутбуке: модель выбирается любая (OpenRouter, OpenAI, свой endpoint и другие), инструменты выполняются в выбранном окружении — локально, в Docker, по SSH. У агента есть память, навыки, которые он сам создаёт и правит, поиск по прошлым сессиям, планировщик cron и подключение MCP-серверов.
С агентом можно говорить из терминала (hermes), а можно запустить шлюз (hermes gateway) и писать ему из мессенджеров. Список каналов большой: Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Mattermost, Email, SMS, Microsoft Teams и ещё десяток платформ3. Шлюз — один процесс: он держит подключения ко всем каналам, ведёт сессии по каждому чату, запускает задачи cron и доставляет их результаты в нужный канал.
Третий способ — веб-панель hermes dashboard. В ней есть чат, а ещё настройки: модель, config.yaml, секреты, задачи cron, навыки, каналы, журналы.
У меня Hermes крутится в контейнере на домашнем сервере, каналов два: бот в Telegram и бот на моём Matrix-сервере. Память агенту даёт memcue, о ней я писал вчера.
Чем неудобны мессенджеры
Неудобства сводятся к двум вещам: отдельный разговор трудно начать и потом трудно найти.
Треды. Каждой задаче нужна своя сессия, иначе контексты разных разговоров смешиваются. В личке с ботом мессенджер даёт для этого треды, но их надо создавать руками. В Telegram это темы в личке: режим включается командой /topic, а новая тема создаётся кнопкой «+»4. Эта схема у меня ещё и ломалась: мобильный клиент иногда присылает первое сообщение новой темы без номера темы, и ответ уходит в общий чат. Разбор этой истории — в отдельном посте. В Matrix Hermes по умолчанию не заводит треды в личке, их включает отдельная настройка, либо тред приходится начинать самому5.
Поиск. Через неделю нужно вернуться к разговору, а где он был? В Telegram или в Matrix, в какой теме или треде? Поиск мессенджера находит сообщение, но про сессии Hermes ничего не знает и ищет только в своём канале. Команда /sessions search фильтрует сессии лишь по названию или идентификатору3, а содержимое разговоров ей не видно.
Hermes for Android
Приложение — клиент для hermes dashboard, проверенное на Hermes v0.22.56. Сервер ставить отдельно не нужно: приложение пользуется тем же API, что и веб-панель. Код на Kotlin и Jetpack Compose, лицензия MIT.
Чат
Чат устроен как в приложении ChatGPT. Список чатов — в боковой панели. Каждый чат — отдельная сессия, и новый открывается кнопкой с карандашом или если потянуть ленту вверх за последнее сообщение. Создавать тред руками больше не нужно. Ответ приходит потоком через WebSocket панели: Markdown, рассуждения модели над ответом, а вызовы инструментов свёрнуты в одну строку, которую можно раскрыть. Код в ответе копируется одним нажатием. На вопросы агента и запросы на подтверждение отвечают кнопками на панели, которая выезжает снизу чата.
В боковой панели чаты сгруппированы по дате и по источнику: свои, из Telegram, из Matrix. Закреплённые чаты сверху, архив и запуски cron — в отдельных разделах. Поиск идёт по полнотекстовому индексу на сервере, то есть по тексту разговоров, и находит в том числе то, что писалось из Telegram и Matrix.
Вложения: фото, камера, любой файл. Картинки, PDF, Markdown, текст и код, аудио и видео открываются внутри приложения, в том числе файлы, которые прислал сам агент. Диктовка отправляется в тот провайдер распознавания речи, который настроен в Hermes.
Работа в фоне и уведомления
Ход агента не обрывается, если свернуть приложение: сокет держит foreground-сервис, а ответ приходит уведомлением. На запрос подтверждения можно ответить прямо из уведомления.
Чтобы узнавать о готовых ответах без постоянного соединения, в репозитории лежит плагин hermes-push для сервера. Он отправляет Web Push после каждого хода в приложении, в Telegram, в Matrix или в cron. Доставка идёт через UnifiedPush, например через ntfy. Сообщение шифруется ключами телефона (RFC 8291), так что сервер push видит только шифротекст. Без плагина приложение проверяет новости периодически, не чаще раза в 15 минут, или держит постоянное соединение, если его включить: своего push у панели нет.
Управление сервером
Раздел сервера показывает состояние и позволяет менять то, ради чего раньше открывался SSH: шлюз, модель, config.yaml (по схеме или сырым YAML), секреты .env, задачи cron, навыки, наборы инструментов, MCP-серверы и каналы. Здесь же журналы и расход токенов. Всё, что может сломать или удалить что-то на сервере, сначала спрашивает подтверждение.
Соединение
У каждого сервера свой маршрут:
| Маршрут | Когда |
|---|---|
| Напрямую | Телефон видит панель сам: публичный адрес, приложение Tailscale, домашний Wi-Fi |
| Встроенный Tailscale | Приложение само входит в tailnet через tsnet, слот VPN не нужен |
| SOCKS5, HTTP | Есть прокси, который видит сервер; логин по желанию |
Встроенный Tailscale — это узел Tailscale, собранный из Go через gomobile и запущенный внутри процесса приложения. VpnService он не использует, поэтому работает рядом с любым другим VPN. Узлу нужен адрес control-сервера Tailscale или Headscale и, по желанию, pre-auth ключ; без ключа приложение покажет ссылку для входа. Журналы узла остаются на телефоне.
Сервер можно добавить по ссылке, чтобы не набирать ключ на телефоне:
org.evsyukov.hermes://add-server?url=hermes.example.org&route=tailscale&control=https://hs.example.org&key=<pre-auth key>
Вход идёт через поток native_pkce панели. Если сервер доступен напрямую, вход открывается в Custom Tab, и там работают passkey. Если сервер за прокси или во встроенном tailnet, страница входа открывается во WebView, чей трафик идёт тем же маршрутом.
Интерфейс и язык
Интерфейс на английском с русским переводом. На Android 13 и новее язык приложения можно выбрать отдельно от системного.
Ограничения и статус
Это первая сборка, и пока выходят только release candidate. Приложение совсем новое, и проверено оно пока на одном сервере — моём. Известные ограничения перечислены в README:
- без плагина
hermes-pushновости приходят с задержкой или требуют постоянного соединения; - на обычном
hermes dashboardREST-методы сессий не трогают сессии, созданные в процессе панели. Поэтому переименование, архив и удаление идут через сокет, а закрепить такой чат может не получиться: метода для этого в сокете нет; - режим панели со шлюзом (
session_authority) поддержан только для основных вызовов.
APK подписан, собран для arm64 и требует Android 12 или новее. Скачать можно на странице релизов1.
Попробуйте
Если вы держите Hermes у себя и пишете ему с телефона, поставьте приложение и скажите, что не так. Что неудобно, чего не хватает, на каком сервере и маршруте что-то сломалось — всё это лучше всего оставлять в issue в juev/hermes. Пожелания по функциям туда же: приложение только начинается, и сейчас самое время решить, каким оно будет.
Hermes 0.1.0-rc1, pre-release, файл
hermes-0.1.0-rc1.apk. ↩︎ ↩︎NousResearch/hermes-agent, README и документация. ↩︎
Messaging Gateway, список платформ и команд внутри чата, в том числе
/sessions,/resume,/approve,/deny. ↩︎ ↩︎Matrix: треды в личке включают
MATRIX_DM_AUTO_THREADилиMATRIX_DM_MENTION_THREADS, по умолчанию обе выключены. ↩︎juev/hermes, README. ↩︎