Перейти к содержимому
PD
AI-автоматизация 8 мин чтения

У агента должна быть своя личность, а не ваш пароль

Большинство AI-агентов работают под человеком: общие логины, root-ключи API, браузерный профиль основателя. Рассказываю, как я даю агенту собственный principal, узкие права и рубильник, который реально работает.

PD

Pavel Duglas

AI Automation & MVP Architect

Любое демо агента заканчивается ровно перед самым интересным. Агент погуглил, составил план, вызвал тул, красиво отчитался. И никто не задаёт вопрос, от которого зависит, сможет ли эта штука вообще работать без присмотра в живой компании: кто такой этот агент? Не «какая модель», а какой аккаунт. Чьим токеном он ходит. Что этот токен может сделать в три ночи, когда цикл поедет. Кому придёт письмо от службы поддержки, если агент нарушит правила сервиса.

Я сдавал автоматизации, где ответ звучал так: «он работает из Gmail Сергея, потому что Сергей его и настраивал». Схема живёт до первого из трёх событий: Сергей уходит, агент отправляет 400 писем, или клиент просит показать, какие именно из 12 000 строк в CRM трогал бот. Identity - это не пункт из чеклиста комплаенса, который прикручивают в конце. Это разница между агентом, которого можно отладить, и агентом, за которого придётся извиняться.

Три паттерна, которые я стабильно нахожу в чужом коде

1. Агент - это человек. Логинится в SaaS-панели под реальным сотрудником, иногда с seed для 2FA, вставленным прямо в конфиг. В логах любого сервиса каждое действие подписано «Сергей». Вы навсегда потеряли возможность ответить на вопрос «это мы или бот?».

2. Root-ключ в .env. Один ключ с полными правами на аккаунт, прокинутый в процесс агента и доступный всем тулам, которые агент может вызвать, включая тот, что запускает shell-команды. Если агент умеет читать файлы и делать сетевые запросы - а полезный агент умеет - ваш root-ключ в одном prompt injection от того, чтобы уехать наружу.

3. Браузерный профиль основателя. Классика browser automation. Человек направляет скрипт на свой рабочий Chrome, потому что там уже все сессии. Теперь бот унаследовал сессию банка, рекламный кабинет и личный Telegram Web. Однажды у меня на глазах скрипт закрыл таб, где висела недооплаченная транзакция.

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

Шаг первый: выдать агенту principal

Principal - это просто «сущность, которую можно аутентифицировать и авторизовать». У агента он должен быть свой. Конкретно, на каждый проект я создаю:

  • сервисный аккаунт или бот-пользователя в каждой системе, куда агент ходит, с говорящим именем: svc-invoice-agent, bot-lead-enricher, svc-bas-parser-01;
  • отдельный email на поддомене, если без регистрации никак: svc-invoice-agent@bots.clientdomain.com;
  • отдельный набор кредов - не расшаренный между двумя агентами и никогда не расшаренный с человеком.

Соглашение об именах важнее, чем кажется. Через полгода, когда клиент спросит, почему во вторник изменились 200 страниц в Notion, вы хотите сделать один grep по строке и закрыть вопрос. Общая личность превращает пятиминутный ответ в двухдневную криминалистику.

Правило, которого я держусь: один principal на агента на окружение. Staging-агент и production-агент - разные личности. Если у них один ключ, то баг на стейдже автоматически становится инцидентом в проде.

Шаг второй: брокер кредов вместо переменных окружения

Когда у агента появилась личность, перестаньте выдавать ему долгоживущие секреты. Процесс агента не должен видеть токен, который ему прямо сейчас не нужен.

Я ставлю между агентом и хранилищем маленький брокер. Агент просит не секрет, а capability. Брокер решает, разрешено ли это сейчас, выдаёт узкий короткоживущий креденшл и пишет факт запроса в лог.

# broker.py - единственный модуль, который умеет читать хранилище
import time, uuid, logging

CAPABILITIES = {
    "sheets.append":   {"secret": "GS_WRITER",    "ttl": 300,  "max_per_hour": 200},
    "crm.read":        {"secret": "CRM_READONLY", "ttl": 900,  "max_per_hour": 500},
    "crm.write":       {"secret": "CRM_WRITER",   "ttl": 120,  "max_per_hour": 50,
                        "requires_approval": True},
    "email.send":      {"secret": "SMTP_BOT",     "ttl": 120,  "max_per_hour": 30},
}

def lease(principal: str, capability: str, reason: str, run_id: str):
    spec = CAPABILITIES.get(capability)
    if not spec:
        raise PermissionError(f"{principal} просит неизвестный capability {capability}")
    if not policy_allows(principal, capability):
        raise PermissionError(f"{principal} не имеет права на {capability}")
    if over_quota(principal, capability, spec["max_per_hour"]):
        raise PermissionError(f"квота исчерпана: {capability}")
    if spec.get("requires_approval") and not approval_granted(run_id, capability):
        raise PermissionError(f"нужно подтверждение человека для {capability}")

    lease_id = str(uuid.uuid4())
    logging.info({"lease": lease_id, "principal": principal, "cap": capability,
                  "reason": reason, "run_id": run_id, "ts": time.time()})
    return {"lease_id": lease_id,
            "token": vault_read(spec["secret"], ttl=spec["ttl"]),
            "expires_at": time.time() + spec["ttl"]}

Три вещи, которые вы получаете сразу.

Строка reason. Агент обязан сказать, зачем ему креды. Эта строка ложится в лог рядом с run id. Разбирая неделю запусков, вы читаете намерения агента, а не только последствия.

Квоты на каждый capability. Ушедший в разнос цикл упирается в стену на 30 письмах, а не на 3000. Это самый дешёвый способ ограничить радиус поражения - и почти никто его не делает.

Approval-гейты как данные. «Запись в CRM только с подтверждением человека» - это строка в словаре, а не ветка кода, про которую забудут при добавлении нового тула.

Если у вас нормальный secret manager, замените vault_read на динамические секреты, которые сами истекают. Если стек маленький - зашифрованный файл плюс кеш в процессе всё равно на порядок лучше, чем плоский .env, примонтированный в контейнер агента.

Шаг третий: записать, что этой личности разрешено

Скоупы должны лежать в файле, который можно вслух прочитать клиенту. Я держу по одному YAML на агента, в репозитории, с ревью как у кода:

principal: svc-invoice-agent
environment: production
owner: pavel@example.com
allow:
  - crm.read
  - sheets.append
  - email.send:
      recipients: ["*@clientdomain.com"]
deny:
  - crm.delete
  - billing.*
limits:
  runs_per_day: 48
  spend_usd_per_day: 6
kill_switch: flags/svc-invoice-agent.enabled

Обратите внимание: email.send ограничен внутренними адресами. Одна эта строка убирает самый позорный сценарий отказа - недописанный черновик, уехавший клиенту. Если агенту всё же нужно писать наружу, это отдельный capability с обязательным подтверждением.

Шаг четвёртый: аудит-запись, ради которой всё это и делалось

Identity без следа - это театр. На каждый запуск я сохраняю одну append-only запись. Не болтовню модели, а решения и эффекты:

{
  "run_id": "2026-02-11T09:14:02Z-8f2a",
  "principal": "svc-invoice-agent",
  "trigger": {"type": "cron", "schedule": "0 9 * * 1-5"},
  "model": "claude-sonnet-4.5",
  "prompt_hash": "sha256:19c4...",
  "leases": [
    {"cap": "crm.read", "reason": "забрать неоплаченные счета", "calls": 3},
    {"cap": "email.send", "reason": "напоминание финансам", "calls": 1}
  ],
  "effects": [
    {"system": "sheets", "op": "append", "rows": 12, "idempotency_key": "inv-2026-02-w6"}
  ],
  "cost_usd": 0.41,
  "outcome": "ok"
}

Самое недооценённое поле здесь - prompt_hash. Когда в четверг поведение изменилось, а кода никто не деплоил, хеш сразу говорит, менялся ли системный prompt или шаблон. Инциденты в жанре «бот отупел» я закрывал за две минуты только благодаря этому полю.

Неприятная часть: сервисы, которые не дадут боту аккаунт

Вот здесь начинается честная инженерия. Есть куча сервисов без сервисных аккаунтов, без API и с правилами, прямо запрещающими автоматизацию. Мои варианты в порядке предпочтения:

  1. Официальный API и бот-пользователь. Всегда первый выбор, даже если это платное место в тарифе. Место дешевле инцидента.
  2. OAuth с делегированием от человека. Человек авторизует один раз, агент держит refresh token с конкретными правами, человек может отозвать доступ одним кликом, не меняя пароль. Хороший компромисс для календарей, почты, облачных дисков.
  3. Browser automation с выделенным профилем. В BAS или Playwright у агента своя папка профиля, свой fingerprint, свой прокси, своя банка с куками. Никогда не рабочий браузер оператора. Профиль - это тоже креденшл: его надо бэкапить, ротировать и уметь сжечь.
  4. Человек на последней миле. Агент готовит, человек нажимает. Для платежей, подписей и регистраций на платформах, где боты запрещены, это не компромисс, а правильный дизайн.

Про регистрацию аккаунтов отдельно: если платформа требует, чтобы условия принял человек, агент, создающий аккаунты, - это бизнес-риск, а не техническая задача. Я говорю это клиентам до подписания договора, а не после.

Ротация и рубильник

Два операционных правила, которые я не пропускаю никогда.

Ротация - одна команда. Если для смены кредов агента нужно править три конфига и редеплоить, её никто не сделает. Нужен make rotate PRINCIPAL=svc-invoice-agent, который выпускает новые секреты, обновляет хранилище и перезапускает воркер. И раз в квартал специально прогнать его на живом проекте.

Kill switch живёт вне агента. Флаг в файле, строка в таблице, переменная, которую читают на каждой итерации цикла. Брокер проверяет её перед каждой выдачей кредов. Флаг выключен - токен не выдаётся, запуск завершается с outcome: disabled. Именно так, а не «остановить контейнер»: остановленный контейнер теряет аудит-запись и возвращается к жизни на следующем деплое.

Миграция за один вечер

Если у вас уже крутятся агенты под человеческими кредами, порядок такой:

  1. Выписать все креды, до которых агент дотягивается. Включая браузерные профили и webhook-URL.
  2. Создать по одному бот-principal на агента там, где это возможно. С отличимым именем.
  3. Спрятать секреты за функцию брокера. Пусть даже на 40 строк. Убрать их из окружения агента.
  4. Написать файл скоупов. И поспорить о нём с тем, кто владеет данными.
  5. Добавить run-запись и флаг kill switch.
  6. Отозвать старый человеческий доступ и запустить агента. Всё, что отвалится, было правом, которое вы не собирались давать.

Последний шаг - настоящий тест. Когда я делаю это на живом проекте, всегда что-нибудь отваливается. И это всегда то, чего агент делать вообще не должен был.

Вопросы и ответы

У меня MVP на пять пользователей. Это не оверинжиниринг?

Минимальный вариант делается за вечер и стоит того. Отдельный бот-аккаунт вместо вашего личного, отдельный браузерный профиль, функция-брокер на 40 строк с квотами по capability, JSON-запись на каждый запуск и файловый флаг для остановки. Без Vault, без policy engine, без RBAC. Обратный порядок дороже: переезд с человеческих кредов на бот-принципалы на живом проекте с интеграциями - это уже несколько дней и обязательно что-нибудь отваливается в проде.

Что делать, если у сервиса нет сервисных аккаунтов и API?

По порядку: сначала OAuth с делегированием от человека - агент получает refresh token с узкими правами, доступ отзывается в один клик без смены пароля. Если и этого нет, browser automation, но обязательно на выделенном профиле со своими куками, fingerprint и прокси, никогда не в рабочем браузере оператора. Если правила сервиса прямо запрещают автоматизацию или требуют, чтобы условия принял человек, последнюю милю делает человек: агент готовит действие, человек подтверждает. Это не слабость архитектуры, а осознанное решение по рискам, и его надо обсудить с клиентом до старта.

Насколько подробным должен быть аудит-лог? Складывать ли туда весь диалог модели?

Нет. Полные трейсы модели полезны для отладки промптов, но как аудит они бесполезны: их слишком много, они шумные и часто содержат персональные данные. Мне нужна одна структурированная запись на запуск: run_id, principal, триггер, модель, хеш промпта, список выданных capability с причинами и числом вызовов, реальные эффекты в внешних системах с idempotency-ключами, стоимость, итог. Такая запись отвечает на вопросы клиента и аудитора, занимает килобайт и хранится годами. Сырые трейсы держите отдельно и с коротким TTL.

Похожие статьи

  • #AI agents
  • #security
  • #automation
  • #identity
  • #API design

Есть идея? Давайте превратим её в работающий продукт.

Пропустите месяцы неопределённости. Получите понятную архитектуру, рабочий MVP и систему, которую можно тестировать, продавать и масштабировать.