# AI-агенты, отправляющие письма через API: безопасные ограничения

Агент, который умеет вызывать email API, может связываться с клиентами, поставщиками и партнерами с машинной скоростью. Это удобно для регулярных уведомлений и рабочих напоминаний. Но небольшая ошибка в запросе, устаревшая запись или взломанная сессия агента могут превратить ситуацию во внешнюю коммуникационную проблему еще до того, как кто-то прочитает черновик.

Безопасная архитектура начинается не с улучшения запроса. Сначала нужно сделать так, чтобы путь отправки отклонял небезопасных получателей, требовал подтверждения, когда сообщение пересекает заданную границу риска, и сохранял достаточно данных для восстановления каждого решения. Если на вопрос «Кому этот агент может написать?» вы отвечаете «любому человеку из CRM», границы у вас нет. Вы просто передали агенту адресную книгу.

## Учетные данные email не должны определять полномочия агента

Учетные данные, с помощью которых вызывается API провайдера, подтверждают, что сервис может передать ему письмо. Они не доказывают, что конкретный процесс агента должен связаться с конкретным человеком по конкретной причине. Команды часто смешивают эти вопросы, потому что токены провайдера легко выдавать и трудно проверять после утечки.

Храните токен провайдера в компоненте, который выполняет отправку. Агент должен передавать намерение, а не владеть многоразовым токеном. Такой компонент может определить запуск агента, разрешить идентификаторы получателей, проверить сообщение, запросить решение при необходимости и вызвать провайдера только после прохождения этих проверок.

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

Необработанный API-токен также усложняет отзыв доступа. Токен можно отозвать, но это может прервать работу всех легитимных процессов, которые им пользуются. Вместо этого выдавайте каждому процессу агента отдельный идентификатор сессии. Завершайте сессию вместе с процессом. Если процесс ведет себя неправильно, отзывайте только его сессию, не останавливая остальную работу.

Не помещайте токен email API в переменные окружения агента, файлы проекта, историю командной строки, конфигурацию инструментов или запрос. Маскирование не исправляет такую архитектуру. Если модель или инструментальный процесс уже прочитал секрет, надежно доказать, куда он попал, невозможно.

Для команд, которые используют автономных агентов программирования на Mac, Sallyport может выполнить HTTP-вызов email API, не раскрывая учетные данные агенту. Это решает вопрос хранения секрета, но не отменяет правила для получателей и содержания, описанные ниже.

## Ограничения получателей должны опираться на записи, а не на поиск по строкам

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

Сделайте реестр получателей главным источником данных. Каждый внешний получатель должен получить стабильный идентификатор и адрес, а также сведения, которые нужны сервису отправки для оценки запроса: тип связи, ответственный владелец, разрешенные назначения сообщений, статус согласия, если он важен, и необходимость проверки человеком для каждой отправки. Агент запрашивает `contact_4821`, а не `ap@northwind.example`.

Отправитель должен разрешать идентификатор только после проверки записи. В запросе можно передавать отображаемое имя, но оно не должно переопределять адрес из реестра. Это предотвращает распространенную ошибку интеграций с агентами: разработчики проверяют идентификатор контакта, а затем доверяют свободному полю `to` из того же запроса.

Используйте отдельные списки для таких случаев:

- клиенты, согласившиеся получать определенный класс уведомлений
- действующие операционные контакты поставщиков, закрепленные за конкретным сотрудником
- внутренние тестовые получатели для периода запуска
- исключительные получатели, для которых всегда нужно решение человека

Не принимайте `to`, `cc`, `bcc` и reply-to как неограниченные строки в интерфейсе агента. К Bcc нужен особый подход. Он полезен в узких процессах соблюдения требований или ведения дел, но также создает скрытый путь раскрытия данных. Отключите его по умолчанию. Для каждого разрешенного получателя Bcc требуйте документированную причину и отдельное подтверждение.

Reply-to может создать не меньше проблем, чем список получателей. Агент способен отправить безобидное уведомление с адресом reply-to, который направит ответы клиентов в заброшенный почтовый ящик. Разрешайте значения reply-to из короткого списка профилей отправителей, а не принимайте их от агента.

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

## Личность отправителя должна ясно показывать назначение сообщения

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

Создайте профили отправителей, например `billing-notices`, `service-status` или `vendor-operations`. В каждом профиле укажите адрес From, адрес для ответов, разрешенные классы сообщений и доступные шаблоны. Агент выбирает идентификаторы профилей. Он не должен самостоятельно формировать произвольные заголовки From или reply-to.

Такое разделение ограничивает и ущерб, и путаницу. Если агент, который обрабатывает обновления по обращениям в поддержку, может также использовать `accounts-payable`, он способен выдать запрос на изменение платежных данных за настоящий. Если все рабочие сообщения отправляются с одного общего адреса, сотрудники не поймут, пришло ли неожиданное письмо от контролируемого процесса или от человека.

Проверяйте подлинность каждой личности отправителя. SPF сообщает принимающим системам, какая инфраструктура может отправлять почту от имени домена. DKIM добавляет к сообщению подпись домена. DMARC публикует желаемый способ обработки, если SPF и DKIM не проходят выравнивание. Ни одна из этих записей не определяет, выбрал ли агент правильного получателя. Они защищают репутацию домена и помогают получателям оценить подлинность.

RFC 5322 определяет формат интернет-сообщений и разделяет такие поля, как From, Sender, Reply-To, To, Cc и Bcc. Стандарт допускает множество вариантов, которые почтовый клиент может корректно отображать. Интерфейс агента должен быть намного уже возможностей этого формата. Гибкость почтового стандарта не дает агентам права создавать почтовые ящики, заголовки или списки получателей.

Сдержанно оформляйте отображаемые имена. Сообщение от `"Accounts Payable" <billing-notices@...>` может ввести поставщика в заблуждение, если в нем запрашиваются чувствительные изменения банковских реквизитов. Используйте имена, соответствующие реальной функции, и запрещайте формулировки, из которых следует, что письмо отправил или проверил конкретный сотрудник, если этого не было.

## Проверять нужно готовое сообщение, а не краткое описание от агента

Подтверждение человеком работает только тогда, когда проверяющий видит именно то решение, которое система собирается выполнить. «Агент хочет уведомить клиента о счете» не является объектом подтверждения. В таком описании нет клиента, суммы, отправителя, текста, пути для ответа и списка вложений.

Сначала сформируйте сообщение, разрешите всех получателей, подставьте все переменные шаблона и создайте неизменяемую запись предлагаемой отправки. Затем покажите эту запись для проверки. Финальный вызов отправки должен ссылаться на одобренную запись по идентификатору и отклонять любые изменения после подтверждения.

Предлагаемая запись должна иметь как минимум такую форму:

```json
{
  "request_id": "req_01J...",
  "agent_session": "sess_01J...",
  "purpose": "vendor_invoice_query",
  "sender_profile": "vendor-operations",
  "to": [{"contact_id": "vendor_4821", "address": "ap@example.test"}],
  "cc": [],
  "bcc": [],
  "subject": "Question about invoice INV-1048",
  "body_sha256": "6af1...",
  "attachment_sha256": [],
  "approval_required": true
}
```

Храните сформированное содержимое в защищенном хранилище либо сохраняйте дайджест и долговечную копию в соответствии с правилами хранения. Один дайджест доказывает неизменность байтов только при условии, что сами байты все еще можно получить. Для чувствительных сообщений сохраняйте и сформированное MIME-сообщение, и его дайджест.

Условия для подтверждения должны отражать возможный ущерб, а не расплывчатую оценку уверенности. Оценки уверенности выглядят привлекательно, потому что обещают адаптивность, но проверяющему приходится разбираться, почему сообщение с оценкой 0,74 ушло, а сообщение с оценкой 0,71 остановилось. Используйте простые условия, которые оператор может проверить.

Требуйте подтверждение, если запрос:

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

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

Не одобряйте сессию агента на целый день, считая это проверкой. Так вы выдаете широкие полномочия и скрываете последствия отдельных действий. Подтверждение сессии может разрешить агенту готовить запросы. Для внешних сообщений за пределами малорискового предварительно одобренного класса решение должно приниматься отдельно для каждого сообщения.

## Шаблоны уменьшают вариативность, но не дают разрешения на отправку

Шаблоны полезны: они ограничивают формулировки и упрощают проверку. Но письмо не становится безопасным, если агент может выбрать любого получателя, заполнить поля непроверенными данными или выбрать шаблон, назначение которого не соответствует событию.

Для каждого шаблона укажите класс сообщения, разрешенные профили отправителей, допустимый тип связи с получателем, обязательные поля и максимальное количество получателей. Средство формирования шаблона должно отклонять неизвестные переменные, а не молча оставлять заполнители или принимать произвольный HTML.

Рассмотрим уведомление о техническом обслуживании. Агент может подставить имя клиента, окно обслуживания и канал поддержки из записей действующей учетной записи. Ему нельзя подставлять свободное описание из заявки, включать идентификатор другого клиента или добавлять вложение только потому, что агент считает его полезным.

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

```yaml
message_class: scheduled_maintenance
sender_profile: service-status
recipient_relationship: active_customer
max_recipients: 1
allowed_template: maintenance_notice_v3
approval:
  required_if:
    - attachment_present
    - recipient_status_not_active
    - maintenance_window_changed_after_render
```

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

Шаблоны тоже должны находиться за границей полномочий агента. Агент может запросить идентификатор шаблона и структурированные значения. Сервис отправки должен загрузить шаблон и экранировать значения в соответствии с контекстом вывода. Если агент передает готовый HTML, он может спрятать лишний текст разметкой, добавить неразрешенные средства отслеживания или изменить визуальный смысл сообщения.

Не используйте шаблоны для маскировки рекламных рассылок. Транзакционные уведомления и маркетинговые сообщения требуют разного согласия, имеют разную частоту и разные требования к отписке. Если назначение сообщения нельзя четко определить, направьте его человеку, а не проталкивайте через удобный шаблон.

## Вложения и цитируемые цепочки содержат данные, которые легко не проверить

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

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

Никогда не разрешайте агенту отправлять запрос вроде `attach: "/Users/shared/contracts/latest.pdf"`. «Последний» файл не является записью, а путь в файловой системе не доказывает, кому предназначен документ. Человек или одобренный процесс работы с документами должны создать запись вложения с классификацией, владельцем, именем файла, дайджестом и сроком действия.

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

Изображения и созданные PDF-файлы нужно проверять отдельно. Извлечение текста помогает искать номера счетов или персональные данные, но не гарантирует, что все обнаружит в визуальной разметке. Предварительный просмотр в промежуточной области медленнее слепой автоматизации. Зато он гораздо быстрее, чем объяснение, почему поставщик получил счет другого поставщика.

## События доставки подтверждают факт, но не разрешают бесконечные повторы

Ответ email API обычно означает, что провайдер принял ваш запрос. Это не значит, что почтовый ящик получателя принял сообщение, человек его прочитал или ответ попадет в контролируемую очередь. Сохраняйте идентификатор сообщения провайдера и связывайте последующие события с внутренним идентификатором запроса.

RFC 5321 описывает поведение SMTP при передаче, включая различие между принятием сообщения одним сервером и последующими результатами доставки. Провайдеры API скрывают этот транспорт за более удобным ответом, но не устраняют различие. Считайте успешный ответ API свидетельством передачи запроса.

Записывайте события доставки, окончательного и временного возврата, жалобы, отписки и отклонения провайдером, если провайдер их предоставляет. Используйте их для обновления права получателя на сообщения. Контакт с окончательным возвратом не должен получать автоматическую рабочую почту, пока владелец не исправит запись. Жалоба должна немедленно исключать адрес из соответствующего класса, а не только после следующего похожего запуска агента.

У повторных попыток должны быть ограничение и ответственный владелец. Временные сбои могут оправдывать ограниченное повторение с той же одобренной записью сообщения. Не поручайте агенту самостоятельно переписывать и отправлять отклоненное письмо. Измененный повтор может обойти защиту от дубликатов и превратить одно неудачное уведомление в несколько противоречивых писем.

Устраняйте дубликаты до вызова провайдера. Выводите значение идемпотентности из идентификатора одобренной отправки, а не из изменяемой темы или текущей задачи агента. Если после передачи запроса произошел сетевой тайм-аут, агент должен проверить запись отправки, а не считать операцию неудачной и отправлять запрос повторно.

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

## Аудиторская запись должна отвечать на неудобные вопросы

Когда клиент спрашивает: «Почему вы отправили это мне?», строки `sent` на панели недостаточно. Нужно определить, какой запуск агента запросил отправку, какая учетная запись или рабочий процесс его инициировали, какая запись получателя соответствовала адресу, какое именно содержимое ушло, кто его одобрил и что принял провайдер.

Ведите два связанных журнала. Журнал запусков отслеживает личность и срок жизни процесса агента, его полномочия и отзыв доступа. Журнал вызовов отслеживает каждую предлагаемую отправку, решение проверки, подтверждение, передачу провайдеру и событие доставки. Их объединение должно выполняться по идентификатору, а не путем расследования по разрозненным журналам приложения.

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

Минимальная последовательность событий выглядит так:

```text
2025-04-03T09:12:04Z proposed  req_01J... sess_01J... digest=6af1...
2025-04-03T09:12:10Z approved  req_01J... reviewer=user_17 digest=6af1...
2025-04-03T09:12:11Z submitted req_01J... provider_id=msg_92...
2025-04-03T09:12:14Z delivered req_01J... provider_event=evt_44...
```

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

Sallyport хранит отдельные представления сессий и активности в одном зашифрованном аудиторском журнале с цепочкой хешей, а `sp audit verify` проверяет цепочку офлайн без ключа хранилища. Такая независимая проверка полезна, когда команде нужно выяснить, изменялась ли запись после запуска агента.

## Контролируемый запуск выявляет ошибочные предположения до того, как их заметят клиенты

Не начинайте с разрешения агенту писать всем действующим контактам. Начните с внутреннего реестра получателей и одного класса сообщений, который не затрагивает финансы, договоры, доступы или юридические вопросы. Цель состоит в том, чтобы найти расхождения между записями, которые, как вам кажется, у вас есть, и записями, которыми на самом деле пользуется путь отправки.

Проведите внутренний пилот с людьми, согласившимися получать тестовые сообщения. Намеренно проверьте ошибки, которые интерфейс должен отклонять: неизвестный адрес, дополнительного получателя Cc, запрос Bcc, изменение переменной шаблона после подтверждения, вложение из неподготовленного пути и повторную отправку после смоделированного тайм-аута. Запишите, отклонила ли система каждый запрос и объясняет ли аудиторский журнал причину отказа.

Затем добавьте один внешний сценарий с небольшим списком получателей, за который кто-то отвечает. Оставляйте подтверждение включенным для каждой отправки, пока не изучите достаточно реальных записей и не поймете типичные исключения. Не отключайте подтверждения только потому, что сообщения выглядят одинаково. Убирайте их лишь тогда, когда источник получателей, шаблон, профиль отправителя, поля данных и поведение повторных попыток ограничены и находятся под наблюдением.

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

Дайте людям немедленный способ остановки, который блокирует будущие отправки сессии и не позволяет предложениям в очереди пройти финальную проверку передачи. Затем проверьте его при наличии ожидающих запросов в очереди. Кнопка отзыва, работающая только до начала процесса, всего лишь создает успокаивающую видимость контроля.

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