У меня на сайте уже живут ИИ-функции: дайджест метрик, чат морфера, автотеги. Но всё это — функции внутри приложения: они принимают текст, отдают текст и ничего не делают в мире. Мне же хотелось другого — исполнителя. Такого, которому можно написать «собери отчёт в Excel по этим данным» и получить файл, причём не на моей машине, а на сервере, из Telegram или из браузера.

Соблазн был простой: поставить агента рядом с сайтом. И сразу стало ясно, почему так делать нельзя.

Проблема: исполнитель — это не страница

Отличие ИИ-функции от ИИ-исполнителя — в том, что исполнитель запускает код. Он пишет скрипт с openpyxl, вызывает его, читает вывод. Это принципиально другой уровень риска: рядом с ним лежит всё, что дорого, — база посетителей, секреты, сертификаты, сам сайт.

Поэтому с самого начала я сформулировал не задачу, а ограничение: агент должен работать так, чтобы его ошибка (или чужой промпт-инъекция) не могла коснуться продакшена. Не «постараться не сломать», а физически не иметь доступа.

Выбор харнеса

«Просто вызвать LLM» тут недостаточно. Нужен харнес: цикл агента, инструменты, память, интеграции. Я взял Hermes Agent (Nous Research) — самостоятельный сервис, у которого уже есть Telegram-gateway, веб-панель и, что важно, Docker-терминал: инструмент исполнения по дизайну выносит код в отдельный контейнер, а не выполняет его в себе.

Из официального образа я вырезал лишнее — Node-панель терминала, неиспользуемые провайдеры, драйверы для мессенджеров, которыми не пользуюсь. Получилось чуть легче и, главное, понятнее, что внутри.

Изоляция: два уровня

Первый уровень — где живёт сам агент. Отдельный сервис в Docker-сети, без публикации портов наружу. Веб-панель доступна только через nginx по адресу /ai-agent/ и закрыта basic-auth, как мониторинг.

Второй уровень — где исполняется код. Агент запускает свои команды в одноразовом контейнере: с --cap-drop ALL, no-new-privileges, лимитами и выключенной сетью. В образ песочницы заранее вшиты библиотеки для Excel и PDF, чтобы типовые задачи не требовали интернета. Контейнер эфемерный: отработал — исчез, ничего не копится.

Здесь есть неочевидная ловушка, на которой я и споткнулся. Чтобы запускать такие контейнеры, агенту нужен доступ к Docker-сокету хоста. А при запуске «докера из докера» путь для монтирования берётся изнутри контейнера агента — и на хосте такого пути нет. Контейнер падал с ошибкой ещё до старта. Решение — монтировать рабочую папку по одному и тому же абсолютному пути и снаружи, и внутри: тогда то, что агент просит у докера, докер действительно находит. Звучит как мелочь, но именно из-за неё первая версия молча не работала.

Модели — те же, что у сайта

Никакого отдельного зверинца моделей. Агент использует те же, что и остальные ИИ-функции сайта: основная — deepseek-v4.1-flash (умеет вызов инструментов, это критично для агента), резерв — mistral-nemo. Есть ещё корпоративный эндпоинт DeepSeek-V4.1 — он объявлен, но по умолчанию выключен: он доступен только из внутренней сети и с публичного сервера недостижим. Провайдер один и тот же, ключи — из окружения, в коде их нет.

Результат

По адресу /ai-agent/ открывается веб-интерфейс агента (за паролем), а в Telegram бот отвечает в канале. Агент умеет запускать код: на проверке он собрал реальный xlsx-файл через openpyxl внутри песочницы и вернул его размер. Задачи из Telegram — включая «собери мне отчёт» — выполняются в изолированном контейнере, а не на хосте.

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

Честно о компромиссах

  • Памяти мало. Сервер — 2 ГБ, и под нагрузкой Docker-пробы агента отвечают

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

  • Docker-сокет — это root. Доступ к сокету эквивалентен правам на хосте,

а у пользователя и так есть sudo. Это доверенный операторский инструмент, а не песочница для чужих пользователей. Понимая это, я и держал акцент на изоляции исполняемого кода, а не на «безопасности агента вообще».

  • Keyless-поиск капризен. Бесплатные провайдеры веб-поиска отдают 403; для

устойчивости нужен ключ — это следующий шаг.

Что дальше

  • Вынести исполнение на отдельный маленький инстанс, чтобы память агента не

конкурировала с сайтом.

  • Разрешить выдачу файлов: сейчас агент собирает документы, но доставка файла

в Telegram — отдельная задача, которую я сознательно отложил.

  • Свои ключи для веб-инструментов, чтобы агент перестал зависеть от keyless-сервисов.

Что понял

Главная работа была не в том, чтобы «подключить LLM». Она была в том, чтобы очертить, до чего LLM не может дотянуться. Как только исполнитель получает право запускать код, вопрос безопасности перестаёт быть про промпты и становится про границы: где он живёт, чем дышит, куда ходит по сети. Изоляция — это не тормоз для такой задачи, а её условие. Без неё её просто нельзя было запускать.