Минимизация данных AI-агента для более безопасных вызовов инструментов
Минимизация данных для AI-агентов не даёт данным клиентов попадать в вызовы инструментов: используются узкие входные данные, ограниченные ответы и доверенные границы выполнения.

AI-агенту не нужно получать карточку клиента только потому, что позже ему может понадобиться один факт из неё. Для каждого вызова инструмента нужен более узкий контракт: действие, минимальный набор полей для его выполнения и ответ, который объясняет агенту результат, не выдавая ему новую порцию данных клиента.
Обычно команды теряют данные на стыке способного агента и удобного внутреннего API. Агенту дают общий поиск клиента, возвращают весь ответ и называют его полезным контекстом. После этого каждый следующий промпт, стенограмма, повторная попытка, отладочная запись и результат инструмента становятся местом, где может распространиться исходная запись.
Решение не в хитром промпте для редактирования данных. Нужна дисциплина проектирования: описать каждое действие до публикации инструмента, проверять узкий запрос на границе выполнения и сделать необработанные ответы внешних сервисов недоступными агенту.
Право вызвать инструмент не даёт агенту доступа ко всей записи
Право вызвать API и право увидеть каждое поле, которое API может вернуть, это разные решения. Команды часто смешивают их, потому что служебная учётная запись может читать широкий объект, тогда как инструменту агента нужна лишь небольшая его часть.
Представьте агента, который должен решить, отправлять ли напоминание об оплате. Сервис доставки может требовать адрес получателя, идентификатор шаблона и ссылку на счёт. Самому агенту достаточно получить eligible: true, безопасное отображаемое имя и ссылку на действие. История платежей, заметки о клиенте, налоговая информация и адрес, который отправитель использует внутри, ему не нужны.
Широкий инструмент get_customer создаёт опасный соблазн. Когда он уже существует, его начинают применять для несвязанных задач, потому что это кажется дешевле, чем создать отдельный инструмент для нужного действия. Затем проблему пытаются решить инструкциями вроде «не раскрывай конфиденциальные поля». Инструкции не удаляют поля из JSON-ответа.
Разделяйте три вопроса:
- Может ли этот процесс агента запустить действие?
- Какие поля нужны исполнителю для его выполнения?
- Какие факты должен получить агент после завершения?
Для каждого вопроса нужно отдельное ограничение. Контроль доступа отвечает на первый. Проверка запроса, на второй. Формирование ответа, на третий. Один универсальный токен API и гибкая JSON-точка не дают хорошего ответа ни на один из них.
Это особенно важно, когда агент многократно использует инструмент. Однократный широкий поиск может выглядеть приемлемо в демонстрации. В реальном запуске агент повторит его после ошибки, процитирует результат в следующем запросе или передаст его другому инструменту. Первое лишнее поле превращается во множество ненужных копий.
Стройте карту данных вокруг действия, а не таблицы базы данных
Полезная карта данных начинается с глагола и внешнего эффекта. «Прочитать клиента» для этой задачи не действие. «Проверить, просрочен ли счёт» и «создать транспортную этикетку» подходят лучше, потому что у каждого действия есть конкретный получатель, цель и ожидаемый результат.
Перед созданием схемы для каждого предполагаемого инструмента запишите небольшую карточку:
| Пункт | Пример: отправить напоминание об оплате |
|---|---|
| Инициатор | Процесс агента службы поддержки по вопросам оплаты |
| Эффект | Отправляет один одобренный шаблон подходящему получателю |
| Минимальный вход | invoice_ref, template_code |
| Доверенный поиск | Адрес получателя, языковые предпочтения, правила проверки права на отправку |
| Результат, видимый агенту | sent, suppressed или needs_human_review |
| Запрещённый вход | Адрес электронной почты, история платежей, заметки аккаунта, полный объект клиента |
| Запрещённый выход | Адрес доставки, необработанный ответ провайдера, платёжные данные |
Именно столбец доверенного поиска выполняет основную работу. В нём указана информация, которая может понадобиться исполнителю, но не нужна агенту. Перенесите этот поиск за границу. Агент отправляет ссылку на счёт, а подконтрольный вам сервис определяет получателя только после проверки действия.
Не превращайте invoice_ref в замаскированную копию адреса электронной почты или составной идентификатор с именем клиента. Непрозрачные ссылки уменьшают случайное раскрытие данных, но сами по себе не делают систему приватной. Если по ссылке любой пользователь может запросить объект клиента, ей всё равно нужны авторизация, срок действия и ограничение аудитории.
Общий регламент ЕС по защите данных формулирует нужный принцип в статье 5(1)(c): персональные данные должны быть адекватными, относиться к делу и ограничиваться необходимым для цели объёмом. Именно слова «для цели» часто заставляют инженерные команды терять осторожность. Цель не в том, чтобы «помочь агенту выполнить работу». Речь идёт о конкретной операции на границе, например об отправке одного напоминания или открытии одного обращения в поддержку.
Карта также показывает поля, которые вообще не должны пересекать границу ни в одном направлении. Свободные заметки заслуживают отдельной строки. В них регулярно оказываются вставленные письма, документы, удостоверяющие личность, учётные данные, медицинская информация и необработанная жалоба клиента. У общего поля notes нет ограниченного смысла, поэтому ему не место в обычном запросе к инструменту.
Строгие схемы останавливают лишние поля до выполнения
Схема должна отклонять поля, которые не нужны для действия. Тихо игнорировать дополнительные свойства кажется удобным, но это скрывает утечку во время тестирования и создаёт у разработчиков впечатление, что поле дошло до исполнителя.
Предположим, агенту нужно запросить проверку возврата средств. Этот контракт разрешает только ссылку на обращение и выбранную причину. Он отклоняет суммы, адреса и произвольные заметки, которые может передать клиент.
{
"name": "request_refund_review",
"description": "Create a review task for an existing support case.",
"input_schema": {
"type": "object",
"additionalProperties": false,
"required": ["case_ref", "reason_code"],
"properties": {
"case_ref": {
"type": "string",
"pattern": "^case_[A-Za-z0-9]{16}$"
},
"reason_code": {
"type": "string",
"enum": ["duplicate_charge", "service_not_received", "other"]
}
}
}
}
Запрос с customer_email, shipping_address, amount или conversation_text должен завершиться ошибкой проверки с явным результатом, например:
{
"error": "invalid_request",
"message": "Unexpected property: customer_email"
}
Такая ошибка подсказывает агенту пользоваться контрактом, а разработчику сообщает, что лишнее поле дошло до границы. Не включайте отклонённое значение в ошибку. Обработчики ошибок вызывают больше утечек, чем многие рабочие пути, потому что для отладки сериализуют весь неудачный запрос.
Одной проверки схемы недостаточно для защиты полей, которыми управляет сервер. Запрос может содержать похожую на настоящую case_ref, принадлежащую другому аккаунту, или допустимый reason_code вместе со ссылкой на действие, выданной другому агенту. Исполнитель должен связать ссылку с выдавшим её субъектом, назначенным действием и сроком действия. Считайте ссылку на действие талоном, а не общедоступным первичным ключом базы данных.
Используйте сборщик запроса, когда агент начинает работу с недоверенным или слишком широким контекстом. Сборщик должен извлечь разрешённые поля и создать новый объект. Не берите большой объект, удаляя из него несколько заведомо опасных ключей. Редактирование по чёрному списку перестаёт работать, когда появляется новое поле, меняется структура вложенного объекта или разработчик вызывает инструмент через псевдоним с другим названием.
ALLOWED_REASONS = {"duplicate_charge", "service_not_received", "other"}
def build_refund_request(case_ref, reason_code):
if not isinstance(case_ref, str) or not case_ref.startswith("case_"):
raise ValueError("invalid case_ref")
if reason_code not in ALLOWED_REASONS:
raise ValueError("invalid reason_code")
return {"case_ref": case_ref, "reason_code": reason_code}
Эта небольшая функция предотвращает распространённую ошибку: передачу целого словаря case в библиотеку клиента, потому что библиотека принимает произвольные именованные аргументы. Явный возвращаемый объект выглядит скучно. На границе данных клиента скука полезна.
Для ответа инструмента нужен отдельный контракт
Необработанный ответ инструмента становится контекстом агента, даже если инструмент не получает данные клиентов на входе. Считайте каждый ответ материалом, который агент может процитировать, сохранить, преобразовать или отправить в другую систему.
Ответ провайдера после создания отправления может содержать полный адрес получателя, номер телефона, сведения об аккаунте перевозчика, данные этикетки, информацию о маршруте и внутренние диагностические поля. Обычно агенту нужны только ссылка на отправление и признак того, можно ли сообщить клиенту, что заказ уже в пути.
Определяйте ответ для агента отдельно от результата для сервиса:
{
"status": "created",
"shipment_ref": "ship_Q7J4K2P8",
"customer_message_allowed": true
}
Сервис выполнения может сохранить подробный ответ перевозчика или передать его туда, где он нужен операционным сотрудникам. Возвращать его агенту не следует, даже если это удобно для отладки. Если оператору нужна квитанция, предоставьте ему защищённый интерфейс. Не превращайте стенограмму агента в базу данных для устранения неполадок.
Ошибки требуют такого же подхода. Внешний API может вернуть отклонённый адрес, номер аккаунта или фрагмент запроса в кавычках. Преобразуйте это в ограниченный код ошибки для агента, например recipient_unavailable, reference_invalid или provider_retryable. Защищённые диагностические сведения храните в системе для операторов.
Спецификация Model Context Protocol описывает инструменты как вызываемые функции со структурированными входными данными и результатами. Такая структура даёт разработчикам удобное место для проверки типов ответа. Инструмент, возвращающий текстовый массив или произвольный JSON, теряет это преимущество. Узкая схема ответа также упрощает тестирование поведения агента: следующее решение может зависеть только от известных полей.
Не возвращайте поле с названием details, если вы не можете точно описать его структуру и правила работы с конфиденциальными данными. Расплывчатая лазейка быстро становится постоянной. Во время инцидента кто-нибудь поместит туда необработанный результат, а потом забудет его удалить.
Редактирование, псевдонимы и секретность, это разные меры защиты
Замена имени токеном не означает, что данные безопасно распространять. Стабильный токен, который можно сопоставить с базой клиентов, в большинстве практических моделей угроз остаётся персональными данными. Одноразовая ссылка на действие, доступная одному вызывающему субъекту и действующая недолго, создаёт гораздо более узкий сценарий утечки.
Редактирование удаляет известные значения из объекта. Это помогает, когда внутренней системе нужно показать запись оператору, но для агентов такой подход ненадёжен как основная граница. Имена полей меняются. Данные перемещаются во вложенные структуры. В свободном тексте оказываются сведения, которые не может надёжно найти ни один фиксированный список для удаления.
Псевдонимизация заменяет прямой идентификатор другим идентификатором. Она уменьшает раскрытие, если получатель не может восстановить соответствие. Подход перестаёт работать, когда тот же агент может вызвать широкий инструмент поиска по этому идентификатору, когда токен используется в разных несвязанных инструментах или когда само значение что-то сообщает. acme-health-urgent-001 не становится непрозрачным только потому, что в нём нет символа @.
Секретность обеспечивается хранением данных для разрешения ссылок и учётных данных на доверенной стороне границы. Агент отправляет ограниченную инструкцию, а сервис разрешает защищённые сведения только для разрешённого действия. Именно это отличает безопасную архитектуру от привычной ошибки, когда агенту передают «очищенный» объект клиента и считают задачу выполненной.
Минимизацию данных также нужно отделять от авторизации. Правильно авторизованный агент всё равно может получить слишком много данных. Неавторизованный вызывающий субъект, наоборот, может получить совсем немного, но и этого может хватить для вреда. Применяйте оба механизма и проверяйте их независимо.
Широкие права делают узкие схемы менее убедительными
Идеальная схема запроса не поможет, если у агента есть учётные данные для прямого вызова исходного API. Если агент может прочитать секрет или использовать его из своей среды выполнения, он обойдёт тщательно спроектированный инструмент и запросит у провайдера более подробный ответ.
Храните учётные данные там, где выполняется действие, а не там, где модель рассуждает. Исполнитель добавляет заголовок авторизации или SSH-идентификатор после проверки узкого запроса. Агент видит результат, а не секрет, заполнитель или копию в переменной окружения.
OAuth 2.0, описанный в RFC 6749, использует области действия, чтобы ограничивать доступ клиента. Области полезны, но во многих реализациях они превращаются в пропуск для целого отдела. Область customers.read всё ещё может разрешать поиск полной записи. Сочетайте область учётных данных с точками для конкретных действий и фильтрацией ответа. Иначе область лишь ограничит коллекцию больших объектов, которую агент может искать.
Разделяйте полномочия по эффектам. Агент, который может создать черновик, не должен иметь права отправить его. Агент, который может запросить проверку возврата, не должен сам оформлять возврат. Такое разделение снижает давление, из-за которого для запуска одного процесса добавляют универсальные административные права.
Для команд macOS, использующих Sallyport, приложение хранит API- и SSH-учётные данные в зашифрованном хранилище и выполняет HTTP- или SSH-действие, не раскрывая учётные данные агенту. Это помогает только при условии, что сам вызов остаётся узким: защищённый токен всё равно может разрешить слишком широкий запрос.
Рабочий процесс поддержки показывает, как распространяются данные
Распространённая утечка начинается с разумной просьбы: дать агенту поддержки подготовить ответ о неудачной доставке. В первой реализации появляется get_order(order_id), который возвращает заказ, профиль клиента, адрес доставки, статус оплаты, историю поддержки и события перевозчика. Агенту нужны событие перевозчика и право отправить одно одобренное сообщение.
Агент вызывает поиск и получает полную запись. Затем он передаёт выбранные сведения инструменту составления сообщения. В промпте теперь есть адрес и история, хотя ни то ни другое не влияет на текст. Ошибка инструмента отправляет весь промпт в журнал ошибок. Инженер копирует эту ошибку в задачу для диагностики. Один широкий ответ превращается в четыре отдельные проблемы хранения.
Постройте процесс иначе. Дайте агенту действие assess_delivery_update, принимающее order_ref. Сервис выполнения проверяет полномочия вызывающего субъекта, получает заказ внутри системы, читает статус перевозчика, применяет правило связи и возвращает только это:
{
"status": "contact_allowed",
"event_code": "delivery_delayed",
"approved_template": "delivery_delay_notice",
"order_ref": "ord_9VJ3R6M1"
}
Агент может решить, требует ли ситуация одобренного сообщения. Отдельное действие send_approved_delivery_update принимает order_ref и approved_template. Оно не принимает адрес электронной почты или свободный текст. Сервис определяет получателя и формирует сообщение по шаблону после проверки согласия и состояния заказа.
Такая схема кажется более ограничительной, потому что так и есть. В этом и состоит смысл ограничения. Агент не может случайно использовать профиль клиента для другой задачи, а следующий инструмент не получит эти данные по ошибке.
Не возражайте, что инструменту нужна «гибкость». Гибкость должна оставаться внутри доверенного кода приложения, где её можно тестировать, проверять и контролировать обращение с данными. Передача гибкости агенту через широкие объекты запроса и ответа перекладывает расходы на каждый промпт и каждую последующую систему.
Экраны подтверждения не могут проверять каждое скрытое поле
Подтверждение защищает от несанкционированных действий, но не может надёжно контролировать слишком большие запросы. Человек видит краткое описание, принимает решение в условиях нехватки времени и одобряет действие. Если сервис прячет в запросе ненужный адрес или историю, подтверждение никак не помогло минимизировать раскрытие.
Подтверждение также создаёт плохой стимул, когда становится заменой проектированию инструментов. Разработчики продолжают добавлять поля, потому что «пользователь подтверждает каждый вызов». Вскоре карточка подтверждения становится слишком подробной для чтения или слишком краткой для оценки. Люди одобряют повторяющиеся на вид безопасные действия и не замечают, что в одном запросе появилось новое поле.
Показывайте действие и его ограниченные параметры в интерфейсе подтверждения, но применяйте список разрешённых полей ещё до его появления. Подтверждающий должен выбирать, разрешить ли send approved delivery update for ord_9VJ3R6M1, а не вручную изучать сериализованную запись клиента.
Используйте подтверждение каждого вызова для действий, которые действительно требуют внимания человека. Не превращайте его в сканер персональных данных. У человека, который видит экран подтверждения, нет ни времени, ни контекста, чтобы решать, было ли необходимым каждое вложенное поле.
Хорошая проверка проста: уберите подтверждающего из этой истории. Сможет ли контракт инструмента по-прежнему не допустить выхода лишних данных из доверенного сервиса? Если нет, граница делает слишком мало.
Аудит должен подтверждать действия, но не превращаться в ещё одно хранилище данных
Вам нужны доказательства того, что агент действовал от имени клиента. Для этого не нужен постоянный архив полных запросов, доступный агенту.
Записывайте название действия, инициирующий процесс или сессию, временную метку, результат, решение об авторизации и ссылку для сопоставления. Если нужна проверка целостности запроса, сохраняйте криптографический дайджест канонического защищённого представления, а не само представление. Храните названия принятых полей, но не их конфиденциальные значения.
Например, событие аудита может иметь такой вид:
{
"action": "send_approved_delivery_update",
"session_ref": "sess_4KH8N2",
"order_ref_digest": "sha256:8e4c...",
"accepted_fields": ["order_ref", "approved_template"],
"outcome": "sent",
"authorized_by": "per_call"
}
Дайджест не уничтожает риски для приватности. Если входное значение взято из небольшого известного набора, злоумышленник может перебрать варианты и сравнить хеши. Используйте защищённую внутреннюю ссылку, если операторам нужно получать подробности, и ограничьте доступ к системе, где хранится исходная запись. Никогда не считайте обычный хеш адреса электронной почты анонимным.
Храните операционные диагностические сведения отдельно от результата инструмента, который получает агент. Сотрудникам поддержки может понадобиться защищённый доступ к телу ошибки внешнего сервиса на короткий срок. Агенту это не нужно. Такое разделение также упрощает удаление и управление сроками хранения, потому что необработанные записи не накапливаются в каждом журнале.
Sallyport записывает проекты агентов и отдельные вызовы в защищённый от записи зашифрованный журнал аудита с цепочкой хешей; команда sp audit verify проверяет эту цепочку офлайн без ключа хранилища. Проверка целостности отвечает на вопрос, изменялось ли записанное событие. Но именно схема события определяет, не содержит ли эта запись слишком много данных клиента.
Проверяйте границу намеренным избыточным обменом данными
Проверка приватности только на корректных сценариях не показывает поведение, которое чаще всего приводит к случайному раскрытию. Тестируйте, что произойдёт, если агент отправит полный объект, внешний сервис вернёт неожиданное поле или исключение возникнет в середине запроса.
Используйте тестовый набор с легко узнаваемыми вымышленными конфиденциальными значениями и проверяйте, что они не пересекают границу агента. В наборе должны быть вложенные поля и свободный текст, потому что плоские примеры позволяют коду редактирования пройти слишком простой тест.
{
"case_ref": "case_Ab92Kx71LmQ4Rt8P",
"reason_code": "duplicate_charge",
"customer": {
"email": "[email protected]",
"address": "17 Example Lane",
"payment_note": "card ending 4242"
},
"conversation_text": "Customer says their medical appointment depends on delivery."
}
Ожидаемый результат, это ошибка проверки, в которой указано только неожиданное свойство. Затем проверьте четыре места: ответ, видимый агенту, журналы приложения, записи системы отслеживания ошибок и события аудита. Разработчики часто проверяют запрос, но забывают, что промежуточное ПО для исключений записало исходное тело.
Добавьте и контрактные тесты ответа. Смоделируйте ответ внешнего сервиса, содержащий полный объект клиента, и проверьте, что инструмент выдаёт только задокументированные поля. Повторяйте это при каждом изменении клиента провайдера. Обновление SDK может добавить поля ответа, даже если никто не менял промпт агента.
Наконец, проведите тест стенограммы. Дайте агенту обычную задачу, сохраните все входы инструментов и результаты, которые получает агент, и найдите в них тестовые значения. Такой тест обнаруживает случайную вставку данных в промпт и отладочный текст, который могут пропустить тесты схемы.
Обычно первым нужно переделать широкий инструмент поиска. Замените его одним действием, которое создаёт реальный внешний эффект или возвращает одно ограниченное решение. Если новый контракт кажется слишком узким и неудобным, это часто говорит о том, что система полагалась на агента, который переносил данные, никогда ему не требовавшиеся.
Вопросы и ответы
Что означает минимизация данных при вызовах инструментов AI-агента?
Вызов инструмента должен содержать только поля, необходимые для конкретного действия, и возвращать полезный результат. Ему не нужна вся карточка клиента лишь потому, что она оказалась в контексте агента. Безопасный вариант по умолчанию, это запрос для конкретного действия, который собирает доверенный код.
Как понять, какие данные клиента действительно нужны AI-агенту?
Начните с действия инструмента, а не с источника данных. Опишите решение, которое должен принять внешний сервис, затем перечислите точные поля, нужные для этого решения. Если вы не можете одним предложением объяснить присутствие поля, уберите его и проверьте, работает ли действие без него.
Достаточно ли убрать имена и адреса электронной почты, чтобы защитить данные клиентов?
Нет. Удаление очевидных полей, например адресов электронной почты и номеров телефонов, помогает, но идентификаторы аккаунтов, номера счетов, временные метки, местоположение и свободный текст тоже могут раскрыть личность клиента или сведения о нём. Считайте редактирование одним из элементов более широкой системы, которая также ограничивает входные данные, ответы, права доступа и сроки хранения.
Стоит ли передавать AI-агенту идентификаторы клиентов?
Идентификатор клиента нужен агенту только тогда, когда он необходим для поиска или изменения конкретной записи. Стабильная непрозрачная ссылка обычно безопаснее полной карточки или понятного человеку идентификатора. Не раскрывайте внутренние ID ради удобства, если доверенный сервис может вместо этого разрешить короткоживущую ссылку на действие.
Почему ответ инструмента создаёт риск для данных клиентов?
Ответ инструмента часто раскрывает больше, чем его входные данные, потому что разработчики для удобства возвращают необработанный ответ внешнего сервиса. Определите контракт ответа, в котором будут только статус и факты, необходимые агенту для следующего решения. Квитанции и подробные записи храните в сервисе или системе аудита, а не в журнале агента.
Может ли подтверждение человеком сделать широкие запросы агента безопасными?
Подтверждение человеком может остановить действие, но не исправляет запрос, в котором уже есть лишние данные. Тот, кто подтверждает вызов, может видеть краткое описание, а не каждое поле, и усталость от постоянных подтверждений делает подробную проверку ненадёжной. Сократите запрос до границы подтверждения.
Как аутентифицировать инструменты, не передавая секреты агенту?
Используйте отдельные учётные данные или шлюз действий, который добавляет их после проверки узкого запроса. Давайте инструменту только права, необходимые для его действия, и никогда не помещайте API- или SSH-ключи в контекст агента. Учётные данные ограничивают возможности вызова, а схемы ограничивают передаваемые данные, поэтому нужны оба механизма.
Что записывать в журнале действий AI-агента?
В журнале должно быть достаточно сведений, чтобы доказать, какое действие произошло, кто или что его инициировало, когда оно произошло и завершилось ли оно успешно. Полные тела запросов, необработанные ответы инструментов и постоянные копии записей клиентов не нужны. Вместо этого храните дайджест, список разрешённых полей, результат и защищённую ссылку для сопоставления.
Безопасно ли передавать свободный текст в инструмент агента?
Свободный текст опасен, потому что в нём часто оказываются сведения, которые невозможно заранее описать схемой: имена, адреса, медицинские данные, учётные данные и скопированная переписка. Не передавайте такие поля по умолчанию. Извлекайте доверенным кодом только конкретный факт или используйте процесс с проверкой человеком, если текст действительно необходим.
Как проверить, не раскрывает ли инструмент агента данные клиентов?
Проверяйте границу намеренно слишком большими и некорректными запросами. Хороший тест показывает, что инструмент отклоняет неизвестные поля, удаляет поля, которыми управляет сервер, возвращает ограниченный ответ и не оставляет конфиденциальные значения в журналах, видимых агенту. Проверяйте и ошибки, потому что исключения часто выводят необработанные тела ответов внешних сервисов.