Безопасность · Наблюдаемость

Минимизация персональных данных в трассировках AI-агентов

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

Продвинутый уровень До 10 минут Результат: политика состава, маскирования и хранения трасс

Начните с модели данных, а не с регулярных выражений

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

Разделите информацию на четыре слоя:

  1. Маршрутизация: идентификаторы трассы и этапа, тип события, время, среда.
  2. Диагностика: длительность, размер, код результата, число повторов, версия модели или инструмента.
  3. Производные признаки: категории ошибки, счётчики, булевы флаги, хеши для сопоставления.
  4. Содержимое: промпты, ответы, аргументы инструментов, документы и память агента.

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

Шаг 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. Маскируйте структурно и до экспорта

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

  1. Разобрать известный формат: JSON, заголовки, параметры URL, сообщения чата.
  2. Удалить запрещённые ключи независимо от регистра.
  3. Заменить значения известных типов: email, телефон, платёжный идентификатор.
  4. Ограничить длину оставшихся строк.
  5. Применить эвристический поиск только как дополнительный слой.
  6. При ошибке разбора удалить всё содержимое, а не пропускать его без обработки.
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

Если список разрешённых полей отсортирован, отсутствие вывода означает, что неизвестных полей нет. Это пример ручной проверки, а не утверждение о наличии готового теста в вашем проекте.

Наконец, выполните четыре проверки:

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

Типовые ошибки

«Замаскируем данные уже в интерфейсе»
Исходное значение остаётся в хранилище, индексах и API. Маскирование должно происходить до экспорта.
«Сохраним всё в зашифрованном виде»
Шифрование снижает риск кражи носителя, но не устраняет избыточный сбор, доступ приложения и последствия ошибочного запроса.
«Хеш превратил данные в анонимные»
Стабильный идентификатор сохраняет связность, а значения с малой энтропией можно перебрать. Псевдоним остаётся чувствительным атрибутом.
«Stack trace безопасен»
Библиотеки нередко включают URL, аргументы, фрагменты ответов и локальные переменные. Оставляйте код ошибки и контролируемые кадры собственного кода.
«Удалили prompt, значит содержимого нет»
Тот же текст может находиться в сообщениях, памяти, результате инструмента, атрибутах span, исключении или повторной попытке.
«Срок хранения настроен в панели»
Настройка одного сервиса не распространяется автоматически на резервные копии, выгрузки и производные индексы.

Ограничения подхода

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

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

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

Итоговый контрольный список

  • Для каждого события указана диагностическая цель.
  • Схема построена на списке разрешённых полей.
  • Промпты, ответы инструментов и память отключены по умолчанию.
  • Идентификаторы заменяются HMAC-псевдонимами только при необходимости связности.
  • Маскирование выполняется до очереди и экспортёра.
  • Ошибка обработки приводит к удалению содержимого.
  • Защищённая отладка изолирована, ограничена по ролям и времени.
  • Дата удаления назначается при записи и наследуется копиями.
  • Синтетические маркеры не обнаруживаются ни в одном контуре.
  • Политика и исключения имеют владельца и дату пересмотра.