С чего всё началось
Заметил за собой такую штуку: рабочие задачи хорошие мысли приходят обычно не за ноутбуком, а когда я от него далеко — в метро, в очереди, на прогулке. Тогда, когда сесть и начать общаться с нейронкой писать код в моменте не получится. Пока дойдешь до рабочего места, пока вникнешь в контекст, пока вспомнишь, что вообще собирался делать — уже и весь фокус над задачей потеряешь. Можно, конечно и с телефона код писать, открыв Termux и судорожно набирать его, но попытки эти были не самыми успешными.
И все же идея писать код с телефона не давала мне покоя. Развил я ее до того, что стал через Termux идти по ssh на удаленный сервер, на котором крутится cursor/claude/codex. Но необходимость держать соединение открытым в течении всего времени работы агента вносила свои коррективы в удобство такой схемы. Что если мне надо будет в моменте ответить кому-либо на сообщение в одном голубом мессенджере или сделать что-то еще?
И я подумал: а что мешает мне дёргать своего агента через бота в том самом мессенджере с самолетиком? Пусть он так же крутится где-то удаленно, пока я ему отправляю через чат задачи.
Звучало как задача на вечер. Но чем больше я работал над чат ботом, тем больше я от него хотел.
В начале был бот
Самая первая версия была предельно простой. Никакого мини аппа, никаких нормальный ответов, только краткое «задача отправлена» и «задача сделана» с небольшими пояснениями.
Я писал ему задачу обычным сообщением. Агент отрабатывал. А результат валился прямо в чат: текстом — что он сделал, и куском diff’а — что именно изменилось. Без подсветки синтаксиса, без удобной навигации по diff. Просто простыня(а иногда и две простыни) текста в мессенджере, которую приходилось листать пальцем.
Но всего этого хватило, что бы я влюбился в идею. Да, читать diff в чате удовольствие не из приятных. Но сам факт того, что я с дивана пишу «почини вот это», а через минуту вижу в том же чате готовый коммит — закрывал ровно ту потребность, ради которой всё затевалось.
Первое, что меня напугало: этот бот доступен всему интернету
Беззаботность продлилась ровно до момента, пока я не открыл бота с другого аккаунта, так как было лень переходить на основной. Тут-то я и понял, что мой агент с кодовой базой торчит на весь интернет. Еще хуже был факт, что мой бот запускает код на моей машине и пушит в мой git. Если до него доберётся посторонний, он сможет гонять агента в моих репозиториях, тратить мои токены, а в плохом сценарии — вообще убить мне компьютер.
Пришлось придумывать защиту доступа. Сделал это в два слоя.
Слой первый — белый список. Бот знает мой Telegram user ID и обслуживает только его (ну, и пару доверенных ID, которые я добавил вручную). Любое сообщение не из списка — молча летит в мусор, бот даже не отвечает. Просто, но это сразу отсекает 99,9% случайных гостей.
Слой второй — токены. Одного ID мало, просто для доп безопасности была добавлена логика, что если сообщение пришло без токена, который для каждого пользователя свой, то бот точно так же просто не ответит.
Итого, чтобы заставить мою систему что-то сделать, нужно одновременно: (а) быть в белом списке и (б) предъявить валидный токен. Случайный прохожий, наткнувшийся на бота, упирается в стену на первом же шаге. Спать стало спокойнее — но, как выяснилось позже, это была лишь половина истории про безопасность (об этом в конце).
Что я в итоге собрал (для себя)
Схема предельно простая на словах:
Телефон (мессенджер) → очередь задач → агент на моей машине → git ↑ │ └──────────────── результат (diff, ветка, статус) ────────────┘
Я пишу в чат что-то вроде «добавь валидацию email в форме регистрации и напиши на неё тест». Сообщение улетает в очередь. На моей машине его подхватывает воркер, запускает агента на нужном репозитории, тот правит код, делает коммит, пушит ветку — и мне обратно прилетает уведомление с тем, что изменилось.
Дальше я просто смотрю diff и решаю, надо ли что-то доработать или можно открывать PR.
На чём всё это написано
Пара слов про стек — потому что выбор тут был не только технический, но и, честно говоря, личный. Очень давно мне хотелось попробовать Go со всей его историей про микросервисы.
И язык, кстати, лёг сюда на удивление удачно. Долгоживущие процессы, передача контекста, фоновые процессы — все это пришлось применять в той или иной степени в процессе работы. Ровно то, для чего Go и придумывали: лёгкие сервисы-демоны, которые тихо стоят и обрабатывают поток сообщений. Так что «хотелось попробовать» неожиданно совпало с «оказалось хорошим выбором».
Остальное — по мелочи и без экзотики:
-
Очередь — RabbitMQ. Через неё ходят и задачи, и стадии, и ответы агенту.
-
Хранилище — PostgreSQL.
Проблема первая: всё должно быть асинхронным
Первая наивная версия была синхронной: отправил задачу — жди ответа и пей кофе. Это оказалось ужасно. Агент на сложной задаче может работать минуты, иногда десятки минут. Сидеть и судорожно ждать ответа от агента постоянно думая, сколько вообще еще осталось, мне надоело уже на 4 задаче, что я ему отправил.
Поэтому я развёл всё через очередь сообщений и сделал задачи полностью фоновыми. Поставил задачу — убрал телефон в карман — пошёл по делам. Когда агент закончил, прилетает пуш. Можно запустить три задачи подряд на разных кусках проекта и не ждать ни одну.
Чтобы понимать, что вообще происходит, я ввёл явные стадии жизненного цикла задачи: в очереди → выполняется → коммитит → пушит → ждёт ревью. Плюс отдельная стадия «ждёт моего ответа». Стадии стримятся мне в реальном времени, так что я вижу, на каком шаге агент.
Проблема 2: с агентом нужно уметь поговорить посреди задачи
В первой версии диалога не было: задача — ответ, задача — ответ. В моменте агент доходил до развилки, в лучшем случае выбирал не тот вариант, и мне приходило совсем не то, что требовалось. Приходилось переформулировать и гонять заново.
Решение — долгоживущая интерактивная сессия. Технически я держу процесс агента живым и общаюсь с ним через stdin потоком JSON-сообщений (stream-json). Если агент задаёт уточняющий вопрос — он прилетает мне в чат, я отвечаю прямо реплаем, и он продолжает с того же места, не теряя контекст.
Проблема 3: большие гонки
Самое неприятно вылезло в момент, когда я подумал, что все уже сделал. Оказалось, что простой запуск 2 задач в одном репозитории ломал алгоритм работы полностью. И вместо двух веток с двумя сделанными задачами я получал одну ветку идентичную main и одну, в которой есть изменения из двух задач.
Пришлось сериализовать задачи по пути к проекту: на один репозиторий — одна активная операция за раз, остальные ждут своей очереди.
Что в итоге изменилось в моей работе
— Баги из категории «починю вечером» теперь чинятся за пару минут прямо с телефона, пока я еду. — Рутину (тесты, бойлерплейт, мелкий рефакторинг) я почти перестал делать руками — раскидываю агенту фоном. — Появился странный комфорт: проект движется, даже когда я физически не за работой. Поставил задачи, сделал подход жима лежа, а все уже готово.
Дальше
Доступ к боту я закрыл — но это, как и обмолвился выше, только половина истории про безопасность. Ограничить, кто может поставить агенту задачу, — это одно. А вот что сам агент способен натворить, уже получив доступ к машине, — совсем другое и куда страшнее.
В какой-то момент мой агент, которому полагалось работать строго внутри проекта, начал в процессе работы запускать скрипты, которые я не мог бы никак остановить. Вот тогда я испугался по-настоящему и пошёл строить песочницу. Про это — в следующей части.
В комментариях призывай всех подискутировать на тему подобного пайплайна к разработке, кто что думает, делитесь мнениями.
ссылка на оригинал статьи https://habr.com/ru/articles/1054360/