В сентябре мы опубликовали пост «Корпоративные MCP» — про то, как устроен доступ наших агентов к продакшн-данным: отдельный шлюз, только чтение, никаких ssh-сессий на прод. Это была архитектурная статья: схема, обоснования, почему мы выбрали MCP единым способом подключения агентов к данным и что из этого получилось на старте.
Сейчас — про эксплуатацию. К этому моменту шлюз отработал полгода в реальном бою: с живой прод-базой, живыми логами и агентами, которые читают данные каждый рабочий день. Такого срока как раз хватает, чтобы отделить «работает» от «красиво выглядело на схеме»: часть решений подтвердилась, кое-что пришлось дописать, а пара мест честно мешает до сих пор.
Ниже — что мы добавили за эти полгода, какие наблюдения накопились, почему узкие инструменты обошли универсальный SQL и три правила доступа агента к прод-данным, которые мы теперь считаем обязательными.
Table Of Contents
Архитектура в пяти строках
Короткое напоминание, чтобы читать дальше можно было без первого поста:
- MCP (Model Context Protocol) — открытый стандарт от Anthropic, который описывает подключение инструментов и данных к агентам. Для нас это единственная дверь от агента к прод-окружению: что не через шлюз — то не через ничего.
- База — Postgres в Kubernetes под управлением CrunchyData. Агент ходит в неё только через шлюз и только на чтение.
- Авторизация — OAuth2 через Authentik, транспорт закрыт TLS от letsencrypt. Изоляция по базам: учётка агента видит ровно свою базу и ничего вокруг.
- Логи агент читает через MCP к VictoriaLogs, историю кода — через MCP к нашему Gitea.
- Чего у агента нет: ssh-доступа к проду и прав записи в него. Изменения в код — только через PR, и мержит человек.
Почему именно так, а не «выдали агенту консоль и надеемся», разобрано в посте «Корпоративные MCP». Дальше — что с этой схемой стало после полугода работы.
Что пришлось добавить за полгода
Главная мысль эксплуатации: схема «поставили шлюз — и живём» не выживает. Не потому, что она неверная, а потому, что живые агенты сразу задают вопросы, которых нет в документации протокола. Вот пять доделок, без которых шлюз к концу этого срока уже не обходится.
OAuth-авторизация клиентов. Через шлюз хотят ходить разные потребители: постоянные агенты, разовые скрипты, новые задачи. Каждому — собственный клиент OAuth2 в Authentik, никакой «общей служебной учётки на всех». Отсюда растёт всё остальное: клиента видно в журнале по имени, и его можно отрезать, не трогая остальных.
Отдельные учётки под каждую задачу. Продолжение той же логики: одна задача — одна учётка — одна база — один токен. Когда у агента своя учётка, вопрос «кто это читал» перестаёт быть детективом, а изоляция по базам становится свойством конфигурации, а не устным обещанием. Про то, как изолированы сами агенты, мы рассказали отдельно — в посте «Агенты в контейнерах».
Лимиты на тяжёлые запросы. Самая болезненная доделка. Агент не оценивает стоимость запроса: «посчитай по всей таблице» выглядит для него как обычное чтение, а для прод-базы это долгий скан, который роняет latency для людей. Лимиты на стороне шлюза превратили это из регулярной боли в штатное ограничение: слишком тяжёлый запрос просто не доходит до базы.
Журнал вызовов. Каждый вызов инструмента записывается: кто, когда, каким инструментом, к какой базе. Это не паранойя, а рабочая вещь: когда агент приносит странный вывод, первым делом мы смотрим, что именно он читал, — и почти всегда ответ находится там.
Отзыв токена без перезапуска. Отозвать доступ должно быть так же просто, как выдать. У нас отзыв токена срабатывает без перезапуска шлюза — пока такой механики нет, отзыв превращается в дело, которое «сделаем потом». А «потом» — это ровно тот момент, когда отрезать доступ нужно срочно.
Warning
Самый первый практический вывод этих полугодий — не про безопасность, а про производительность: без лимитов тяжёлый запрос агента бьёт по людям.
Он роняет latency прод-базы для всех, кто работает с ней в тот же момент, — и сам агент даже не узнает, что виноват.
Что показала эксплуатация
Сразу оговорка: это качественные наблюдения, а не замеры. Честных цифр вида «сколько строк прочитал агент» у нас нет, а выдумывать проценты мы не будем — не тот жанр.
Агент почти никогда не читает «всю базу». Типичный сценарий выглядит скромно: посмотреть структуру таблиц и последние записи. Что за объекты есть, как называются поля, что появилось за последние часы. Запрос «дай всё» — редкость и почти всегда признак плохо сформулированной задачи. Страшная картинка «агент сливает базу» на практике выглядит как «агент посмотрел схему и несколько свежих строк».
Самые полезные инструменты — узкие. Не «выполнить SQL», а вопросы вида «покажи ошибки за сутки» или «покажи коммиты по файлу». Они отвечают в том виде, в котором ответ сразу можно использовать, и не требуют от агента знания нашей схемы наизусть. Универсальный SQL остаётся для редких случаев, когда узких инструментов не хватает, но в повседневной работе он проигрывает по всем статьям.
Тяжёлые запросы без лимитов портят жизнь людям. Мы уже сказали об этом в доделках, но в списке наблюдений эта строка обязана быть: пока лимиты не появились, самый быстрый способ сделать прод медленным для человека — позволить агенту свободно формулировать запросы.
И вся эта активность — не развлечение, а рутина: через шлюз агенты разбирают очередь поддержки. Экономика такого саппорта — отдельная история, мы посвятили ей отдельный разбор.
Почему узкие инструменты лучше универсального SQL
На первый взгляд «выполнить SQL» — идеальный инструмент: один вместо двадцати, умеет всё. На практике «умеет всё» — это ровно «слишком много».
Площадь ошибки. У универсального доступа огромная поверхность: любой запрос, любой объём, любая ошибка агента уходит в базу как есть. Узкий инструмент описан человеком заранее — у него конечный набор действий, и нагрузку от него можно прикинуть до того, как агент впервые его вызвал.
Цена ошибки. Агент иногда путается в формулировках — это нормально, он текстовая машина. В узком инструменте ошибка стоит дёшево: «не тот фильтр в готовом запросе». В универсальном SQL та же ошибка превращается в тяжёлый запрос к прод-базе — знакомую нам историю из предыдущего раздела.
Читаемость журнала. «Показать ошибки за сутки» в журнале вызовов — понятная запись, которую можно прочесть и через месяц. Произвольный SQL — строка, которую надо разбирать глазами. Журнал по-настоящему полезен, когда вызовы семантические.
Поэтому мы продолжаем дробить: каждый вопрос, который агенты задают прод-данным часто, — кандидат в отдельный узкий инструмент.
Матрица доступа: что агент может, а что нет
Собрали текущее состояние прав в одну таблицу.
| Инструмент | Что может | Что не может |
|---|---|---|
| Postgres (через шлюз) | читать структуру таблиц и данные своей базы, смотреть последние записи | писать и менять схему, видеть чужие базы, уходить в тяжёлые запросы без ограничений |
| VictoriaLogs (через MCP) | искать по логам — «покажи ошибки за сутки», читать свежие записи | менять и удалять логи, трогать конфигурацию |
| git (Gitea через MCP) | читать историю, диффы, коммиты по файлу | мержить PR — это делает человек, доставлять изменения в прод напрямую |
| Файлы | читать файлы проекта | изменять файлы и конфигурацию прода |
Главное про эту таблицу: каждое «не может» — не обещание агента вести себя хорошо, а отсутствие технической возможности. Разница между «нельзя по правилам» и «нельзя по правам» — это разница между регламентом и архитектурой.
Где read-only не хватает
Самое заметное ограничение: агент не может воспроизвести баг, для которого нужна запись. Если проблема проявляется только при создании заявки, агент не соберёт её целиком из данных и логов — до момента, когда запись уже сделана человеком. И это правильно: read-only делает цену ошибки агента предсказуемой. Половинный диагноз лучше сломанного прода.
Как обходим: агент собирает из чтения всё, что можно, — данные, логи, историю кода, — формулирует гипотезу и готовит патч; изменения уходят через PR, а применяет и проверяет человек, у которого права записи есть. Да, это медленнее, чем «дать агенту консоль», — зато не требует верить агенту на слово.
Второе честное место — лимиты мешают большим аналитическим выборкам. Иногда агент упирается в ограничение и вместо одного большого ответа собирает ответ из нескольких маленьких. Мы пока считаем эту цену справедливой: latency для людей дороже удобства агента.
Info
Изменения в код — только через PR, и мержит человек. Это не процессный ритуал, а та же модель доверия, что и read-only у шлюза: агент предлагает, человек решает.
Итог: три правила доступа к прод-данным
Полгода эксплуатации сжались в три правила, которые мы теперь применяем к каждому новому контуру с агентами:
- Записи у агента не существует. Не «запрещено регламентом», а физически нет прав — read-only на уровне шлюза и учётки. Правило, которое держится на дисциплине, рано или поздно нарушается; правило, встроенное в доступ, — никогда.
- Узкие инструменты вместо универсального SQL. Каждый частый вопрос к прод-данным — отдельный инструмент с предсказуемой нагрузкой и читаемой записью в журнале.
- Каждое действие — под именем. Своя учётка и свой токен на задачу, журнал вызовов, отзыв без перезапуска. Тогда «разобраться, что произошло» — это минута, а не расследование.
Если хотите такую же схему у себя — доступ агентов к прод-данным без риска для прода и без ssh — это часть наших ИИ-работ: мы строим такие контуры под задачи клиента. Соседние части конвейера — изоляция агентов и экономика агентного саппорта — разобраны в статьях цикла, ссылки выше.
