Безопасность · Наблюдаемость
Минимизация персональных данных в трассировках AI-агентов
Полные промпты, ответы инструментов и промежуточные состояния ускоряют отладку, но превращают систему наблюдаемости в копию пользовательских документов, переписки и коммерческих данных. Практическая цель — сохранить диагностическую ценность трассы, не сохраняя исходное содержание по умолчанию.
Начните с модели данных, а не с регулярных выражений
Трассировка — связанная последовательность событий одного выполнения агента: вызов модели, обращение к инструменту, переход состояния, ошибка и итог. Минимизация означает, что каждое событие содержит только данные, необходимые для заранее определённой задачи наблюдаемости.
Разделите информацию на четыре слоя:
- Маршрутизация: идентификаторы трассы и этапа, тип события, время, среда.
- Диагностика: длительность, размер, код результата, число повторов, версия модели или инструмента.
- Производные признаки: категории ошибки, счётчики, булевы флаги, хеши для сопоставления.
- Содержимое: промпты, ответы, аргументы инструментов, документы и память агента.
Первые три слоя обычно дают достаточно сигнала для метрик и поиска сбоев. Четвёртый следует считать запрещённым по умолчанию и включать только для конкретного сценария с отдельным доступом и коротким сроком хранения.
Шаг 1. Составьте реестр событий
Для каждого типа события зафиксируйте диагностическую цель и минимальный набор полей. Не используйте формулировку «может пригодиться»: поле либо поддерживает проверяемую операционную задачу, либо не попадает в трассу.
| Событие | Сохранять | Не сохранять по умолчанию | Рекомендуемый срок |
|---|---|---|---|
| Вызов модели | модель, длительность, токены, код результата, версия шаблона | полный системный и пользовательский промпт, полный ответ | 30 дней |
| Вызов инструмента | имя инструмента, схема операции, длительность, статус, размер результата | аргументы, HTTP-заголовки, тело ответа, содержимое файлов | 30 дней |
| Переход состояния | предыдущее и новое состояние, причина, счётчики | полный снимок памяти и контекста | 14–30 дней |
| Ошибка | класс, безопасное сообщение, компонент, стек из собственного кода | локальные переменные, URL с параметрами, сырой ответ провайдера | 30–90 дней |
| Аудит доступа | псевдоним субъекта, действие, ресурс-категория, решение политики | имя, email, содержимое ресурса | по утверждённой политике аудита |
| Отладочный образец | только явно разрешённые и очищенные поля | секреты, идентификаторы личности, необработанный контент | 24–72 часа |
Сроки в таблице — пример стартовой политики, а не универсальная юридическая норма. Итоговые значения должны учитывать назначение системы, договоры, применимое право и внутренние требования.
Шаг 2. Перейдите на список разрешённых полей
Удалять отдельные «опасные» ключи недостаточно: новый инструмент добавит новое поле, и оно автоматически окажется в хранилище. Безопаснее собирать событие заново из небольшого списка разрешённых атрибутов.
Пример конфигурации коллектора:
trace_policy:
default_action: drop
allowed_attributes:
common:
- trace_id
- span_id
- event_type
- timestamp
- environment
- service_version
model_call:
- model_id
- prompt_template_version
- input_token_count
- output_token_count
- duration_ms
- result_code
tool_call:
- tool_name
- operation
- duration_ms
- result_code
- request_size_bytes
- response_size_bytes
state_transition:
- state_from
- state_to
- transition_reason_code
error:
- error_class
- safe_error_code
- component
forbidden_attributes:
- prompt
- completion
- messages
- tool_arguments
- tool_response
- authorization
- cookie
- document_text
- memory_snapshot
- local_variables
Валидатор должен отклонять неизвестные поля, а не молча сохранять их. Счётчик отклонений оставьте как метрику: он покажет, где разработчики пытаются отправить незарегистрированные атрибуты.
Шаг 3. Выберите преобразование для каждого класса данных
| Задача | Преобразование | Что остаётся возможным |
|---|---|---|
| Поле не нужно для диагностики | Удаление | Агрегаты по остальным полям |
| Нужно связать события одного субъекта | Псевдонимизация через HMAC | Группировка без хранения исходного идентификатора |
| Нужна форма значения | Типизация или обобщение | Например, домен email или возрастной диапазон |
| Нужен фрагмент для расследования | Структурное маскирование | Контекст ошибки без открытых значений |
| Нужна статистика контента | Счётчик, длина или классификационная метка | Аналитика без текста |
Для устойчивого псевдонима используйте HMAC с отдельным ключом, ротацией и ограниченным доступом. Обычный хеш email или телефона уязвим для перебора по словарю.
function pseudonymize(value, key, keyVersion) {
const normalized = normalizeIdentifier(value);
return `hmac:${keyVersion}:${hmacSha256(key, normalized)}`;
}
function buildToolTrace(event, key) {
return {
trace_id: event.trace_id,
event_type: "tool_call",
tool_name: allowlistedToolName(event.tool_name),
operation: allowlistedOperation(event.operation),
subject_ref: event.user_id
? pseudonymize(event.user_id, key.secret, key.version)
: null,
duration_ms: clampInteger(event.duration_ms, 0, 3600000),
result_code: normalizeResultCode(event.result_code),
request_size_bytes: byteLength(event.arguments),
response_size_bytes: byteLength(event.response)
};
}
Это иллюстративный псевдокод. Он намеренно не записывает event.arguments и event.response. Ключ HMAC должен поступать из штатного хранилища ключей, а не из файла конфигурации или переменной, попадающей в дампы.
Шаг 4. Маскируйте структурно и до экспорта
Если содержимое всё же разрешено, обрабатывайте его внутри доверенной границы до очереди, экспортёра и стороннего сервиса наблюдаемости. Порядок фильтров:
- Разобрать известный формат: JSON, заголовки, параметры URL, сообщения чата.
- Удалить запрещённые ключи независимо от регистра.
- Заменить значения известных типов: email, телефон, платёжный идентификатор.
- Ограничить длину оставшихся строк.
- Применить эвристический поиск только как дополнительный слой.
- При ошибке разбора удалить всё содержимое, а не пропускать его без обработки.
redaction:
on_parse_error: drop_content
replacement: "[REDACTED]"
max_string_length: 256
keys:
exact:
- password
- access_token
- refresh_token
- authorization
- cookie
- email
- phone
- full_name
- address
suffix:
- _secret
- _token
- _credential
urls:
keep:
- scheme
- host
- normalized_path
drop:
- query
- fragment
- userinfo
headers:
allow:
- content-type
- content-length
- request-id
unstructured_text:
enabled: false
Отключение неструктурированного текста — безопасная исходная настройка. Регулярные выражения не распознают все языки, опечатки и контекстные идентификаторы, поэтому не могут быть единственным барьером.
Шаг 5. Отделите обычные трассы от защищённой отладки
Не добавляйте содержимое в общий поток через флаг debug=true. Создайте отдельный режим со следующими ограничениями:
- явное включение для конкретной трассы или короткого временного окна;
- авторизация по роли и регистрация причины доступа;
- локальное маскирование до передачи;
- изолированное хранилище и отдельные ключи шифрования;
- срок удаления 24–72 часа без автоматического продления;
- запрет на обучение, аналитику продукта и повторное использование;
- уведомление владельца системы и аудит чтения.
Сэмплирование уменьшает объём данных, но не снижает чувствительность отдельной записи. Один процент необработанных промптов остаётся утечкой персональных данных, только меньшего масштаба.
Шаг 6. Реализуйте сроки хранения как код
Срок должен вычисляться при записи. Это надёжнее, чем периодическая очистка без индивидуальной даты удаления.
retention:
model_call_metadata: 30d
tool_call_metadata: 30d
state_transition_metadata: 14d
error_metadata: 90d
protected_debug_content: 48h
storage:
set_expiry_at_ingest: true
backups:
inherit_expiry: true
maximum_lag: 24h
indexes:
delete_with_source: true
derived_exports:
inherit_classification: true
inherit_expiry: true
Проверьте не только основное хранилище, но и очереди, поисковые индексы, кэши, резервные копии, выгрузки аналитиков и вложения в задачи. Перемещение записи в архив не считается удалением.
Проверка результата
Создайте синтетический набор маркеров, которые заведомо не принадлежат реальным людям и не являются действующими секретами. Например: TRACE_TEST_EMAIL_7Q2, TRACE_TEST_PHONE_8K4 и TRACE_TEST_TOKEN_9M6. Передайте их через промпт, аргументы инструмента, ответ инструмента, ошибку и состояние агента.
Безопасный пример локальной проверки выгруженного файла:
rg -n \
'TRACE_TEST_EMAIL_7Q2|TRACE_TEST_PHONE_8K4|TRACE_TEST_TOKEN_9M6' \
./trace-export.jsonl
Ожидаемый результат — отсутствие совпадений. Затем проверьте схему разрешённых полей:
jq -r 'keys_unsorted[]' ./trace-export.jsonl \
| sort -u \
| comm -23 - ./allowed-trace-fields.txt
Если список разрешённых полей отсортирован, отсутствие вывода означает, что неизвестных полей нет. Это пример ручной проверки, а не утверждение о наличии готового теста в вашем проекте.
Наконец, выполните четыре проверки:
- Запись с неизвестным ключом отклоняется или ключ удаляется с метрикой нарушения.
- Ошибка парсинга приводит к удалению содержимого.
- Просроченная тестовая трасса исчезает из основного хранилища, индекса и резервного контура в установленный срок.
- Пользователь без отладочной роли не может читать защищённые образцы, а попытка фиксируется в аудите.
Типовые ошибки
- «Замаскируем данные уже в интерфейсе»
- Исходное значение остаётся в хранилище, индексах и API. Маскирование должно происходить до экспорта.
- «Сохраним всё в зашифрованном виде»
- Шифрование снижает риск кражи носителя, но не устраняет избыточный сбор, доступ приложения и последствия ошибочного запроса.
- «Хеш превратил данные в анонимные»
- Стабильный идентификатор сохраняет связность, а значения с малой энтропией можно перебрать. Псевдоним остаётся чувствительным атрибутом.
- «Stack trace безопасен»
- Библиотеки нередко включают URL, аргументы, фрагменты ответов и локальные переменные. Оставляйте код ошибки и контролируемые кадры собственного кода.
- «Удалили prompt, значит содержимого нет»
- Тот же текст может находиться в сообщениях, памяти, результате инструмента, атрибутах span, исключении или повторной попытке.
- «Срок хранения настроен в панели»
- Настройка одного сервиса не распространяется автоматически на резервные копии, выгрузки и производные индексы.
Ограничения подхода
Минимизация ухудшает возможность воспроизвести редкие семантические ошибки: по метаданным нельзя восстановить точную формулировку запроса. Компенсируйте это версионированием шаблонов, детерминированными тестовыми сценариями, кодами причин и краткоживущим защищённым режимом отладки.
Автоматическое распознавание персональных данных неизбежно даёт пропуски и ложные срабатывания. Оно не заменяет инвентаризацию полей, контроль доступа, оценку правового основания и процесс удаления по запросу. Для мультимодальных входов отдельно учитывайте изображения, аудио, OCR-текст и метаданные файлов.
Наконец, одинаковая политика не подходит всем событиям. Аудит безопасности может требовать более долгого хранения метаданных, тогда как содержимое отладочной трассы должно исчезать за часы. Такое различие следует документировать как исключение с владельцем, целью и датой пересмотра.
Итоговый контрольный список
- Для каждого события указана диагностическая цель.
- Схема построена на списке разрешённых полей.
- Промпты, ответы инструментов и память отключены по умолчанию.
- Идентификаторы заменяются HMAC-псевдонимами только при необходимости связности.
- Маскирование выполняется до очереди и экспортёра.
- Ошибка обработки приводит к удалению содержимого.
- Защищённая отладка изолирована, ограничена по ролям и времени.
- Дата удаления назначается при записи и наследуется копиями.
- Синтетические маркеры не обнаруживаются ни в одном контуре.
- Политика и исключения имеют владельца и дату пересмотра.