Обезличивание текста в Presidio: карта операторов строится вокруг AnonymizerEngine, который принимает текст, список RecognizerResult и словарь OperatorConfig. Детерминированный маршрут начинается с ручной разметки спана: вы сами указываете границы сущности, а движок применяет выбранный оператор. Вероятностное обнаружение AnalyzerEngine подключается отдельным шагом.
Что делает AnonymizerEngine и как устроена карта операторов
Документация проекта на presidio.dataprivacystack.org описывает AnonymizerEngine как главный класс модуля presidio-anonymizer: он получает текст, результаты анализа и операторы, после чего возвращает EngineResult с полем text и списком OperatorResult. Каждый OperatorResult хранит start, end, entity_type, text и имя применённого оператора. Такая схема даёт проверяемый след: по items видно, какой спан обработан и чем именно.
Карта операторов — это словарь, где ключом идёт тип сущности, а значением OperatorConfig с именем оператора и параметрами. Если словарь пуст или в нём нет ключа DEFAULT, документация называет поведение по умолчанию: применяется replace, а подстановкой служит имя типа сущности в угловых скобках, например
Модуль поддерживает два направления. Anonymizers заменяют текст сущности по оператору, Deanonymizers возвращают исходное значение там, где операция обратима. Документация перечисляет встроенные операторы: replace, redact, hash, mask, encrypt, custom, surrogate_ahds, keep. Для обратного прохода доступен decrypt. Набор расширяется: класс Operator с методом operate можно добавить в движок через add_anonymizer.
Практический вывод для карты: сначала определите типы сущностей, затем для каждого выберите оператор и его параметры, и только после этого собирайте словарь. Тип сущности в ключе должен совпадать со строкой entity_type в RecognizerResult, иначе оператор не найдётся и сработает поведение по умолчанию.
| Оператор | Что делает | Ключевые параметры |
|---|---|---|
| replace | Заменяет сущность заданным значением | new_value |
| redact | Удаляет сущность из текста | нет |
| hash | Хеширует текст сущности с солью | hash_type, salt |
| mask | Заменяет часть символов | chars_to_mask, masking_char, from_end |
| encrypt | Шифрует сущность ключом | key |
| custom | Подставляет результат функции | lambda |
| keep | Оставляет сущность без изменений | нет |
Ручной эталон и фактический выход модели
Учебный вход в этом разборе — строка «Email me at alex@example.test. Order A-17 costs 40 EUR.». Ручная разметка задаёт email-спан [12:29]: правая граница исключается, поэтому срез text[12:29] равен «alex@example.test». Проверка среза выполняется до обезличивания и отсекает неверные границы.
Ручной эталон результата: «Email me at
Фактический выход модели — отдельная величина. AnalyzerEngine строит спаны вероятностным способом, и его результат зависит от модели, языка и набора сущностей. В документации проекта на presidio.dataprivacystack.org прямо сказано, что автоматические механизмы обнаружения не дают гарантии нахождения всей чувствительной информации, поэтому применяются дополнительные системы и защиты. Полнота детектора в учебном контракте не измеряется.
Разделение важно для приёмки. Ручной эталон проверяет преобразование: правильно ли движок заменил заданный спан и не тронул лишнее. Выход модели проверяет обнаружение: попал ли реальный email в список RecognizerResult. Смешивание двух проверок в одну маскирует ошибку: неверный спан при верном операторе даст корректный на вид результат замены и оставит часть адреса в тексте.
Для второго шага документация проекта на presidio.dataprivacystack.org приводит установку: pip install presidio-analyzer, pip install presidio-anonymizer и загрузку модели spaCy командой python -m spacy download en_core_web_lg. Анализ выполняется вызовом analyzer.analyze с текстом, списком entities и языком, после чего results передаются в anonymizer.anonymize. Спаны из ответа стоит сверить с исходным текстом до преобразования.
Проверка положительного и отрицательного случаев
Положительный случай — корректный спан [12:29]. Срез совпадает с адресом, оператор replace подставляет
Отрицательный случай — спан [12:28]. Он теряет последний символ адреса, срез не совпадает с ожидаемым значением, и проверка границ отклоняет разметку до обезличивания. Если такую разметку пропустить, в тексте останется хвост адреса, а заменённая часть будет короче исходной сущности.
Второй отрицательный случай — потеря A-17 или 40 EUR. Оба фрагмента входят в учебный контракт как сохраняемые, поэтому их исчезновение считается ошибкой этого контракта. Проверка сравнивает результат с эталоном целиком.
Третий случай связан с детектором. Если AnalyzerEngine не вернул email в результатах, пустой список не означает защиту текста: адрес останется в исходном виде. Документация проекта на presidio.dataprivacystack.org относит такие ситуации к ложным пропускам и отсылает к разделу с вопросами по обработке необнаруженных сущностей.
from presidio_anonymizer import AnonymizerEngine
from presidio_anonymizer.entities import RecognizerResult, OperatorConfig
text = 'Email me at alex@example.test. Order A-17 costs 40 EUR.'
assert text[12:29] == 'alex@example.test'
result = AnonymizerEngine().anonymize(
text=text,
analyzer_results=[
RecognizerResult(entity_type='EMAIL_ADDRESS', start=12, end=29, score=1.0)
],
operators={'EMAIL_ADDRESS': OperatorConfig('replace', {'new_value': '<EMAIL>'})},
)
print(result.text)
Этот пример воспроизводит принятый учебный артефакт: ручная разметка, проверка среза, один оператор replace. Строку с assert можно заменить на сравнение с ожидаемым значением и выбросом исключения при расхождении.
Продуктовый путь, API и условия доступа
Документация проекта на presidio.dataprivacystack.org перечисляет варианты запуска: Python или PySpark, Docker, Kubernetes. Для HTTP-доступа используется серверный маршрут anonymizer. Запрос отправляется методом POST на /anonymize с телом JSON, где перечислены text, anonymizers и analyzer_results.
Для полного маршрута в Docker документация приводит два контейнера: ghcr.io/data-privacy-stack/presidio-analyzer на порту 5002 и ghcr.io/data-privacy-stack/presidio-anonymizer на порту 5001. Анализ вызывается POST на /analyze, обезличивание — POST на /anonymize. Тело запроса на обезличивание содержит текст, словарь anonymizers по типам сущностей и массив analyzer_results со start, end, score и entity_type.
Условия доступа к моделям описаны в документации проекта на presidio.dataprivacystack.org: движок по умолчанию построен на spaCy, доступен вариант с transformers, для ускорения на Linux с NVIDIA GPU ставят cupy-cuda12x, а на macOS с Apple Silicon поддержка MPS не заявлена, и анализатор использует CPU. Модель transformers и модель spaCy скачиваются при первом вызове AnalyzerEngine, если их нет локально.
Интеграцию с NeMo Guardrails описывает документация NVIDIA: Presidio подключается для обнаружения чувствительных данных на входе, выходе и в извлечённых фрагментах базы знаний. Установка требует presidio-analyzer, presidio-anonymizer, spacy и модели en_core_web_lg либо extras sdd. Порог score_threshold по умолчанию равен 0.2 и настраивается отдельно для каждого источника. Потоки маскирования переписывают проверяемый текст, а поток detect заставляет бота отказаться от ответа. Для retrieval-потоков документация указывает поддержку только в LLMRails, поскольку у IORails нет конвейера извлечения.
| Источник | Поток | Поддержка |
|---|---|---|
| Вход | detect, mask | LLMRails, IORails |
| Выход | detect, mask | LLMRails, IORails |
| Извлечение | detect, mask | LLMRails |
Границы применимости и что не гарантируется
Документация проекта на presidio.dataprivacystack.org называет Presidio инструментом управления и сопровождения чувствительных данных и одновременно предупреждает: автоматическое обнаружение не гарантирует нахождение всей чувствительной информации. Замена email на метку не превращает документ в анонимный по любому режиму обработки: номер заказа и сумма остаются в тексте по условиям учебного контракта.
Обратимые операторы требуют отдельного внимания. encrypt и decrypt работают с ключом, hash по умолчанию использует случайную соль для каждой сущности. Документация проекта на presidio.dataprivacystack.org отмечает изменение в операторе hash начиная с версии 2.2.361: одинаковые значения получают разные хеши, если соль не задана явно. Для сквозной согласованности записей соль передают параметром, хранят в защищённом месте и не включают в выходной текст.
Псевдонимизация сохраняет связь между записями, поэтому вопрос юридической достаточности решается вне библиотеки. Presidio выполняет преобразование текста по заданной карте; оценка соответствия требованиям к обработке данных остаётся задачей организации.
Отдельная граница — перекрытия спанов. Документация проекта на presidio.dataprivacystack.org описывает четыре сценария: без пересечения применяется оператор к каждой сущности; при полном перекрытии берётся сущность с более высоким score, а при равных score выбор произволен; когда одна сущность содержится в другой, используется более длинный фрагмент даже при меньшем score; при частичном пересечении каждая сущность обезличивается отдельно, и результат склеивается. Для текста «I’m George Washington Square Park.» с сущностями George Washington и Washington State Park результат принимает вид «I’m
Согласование scope, схемы данных и критериев приёмки
Scope фиксирует, какие сущности входят в контракт и какие фрагменты сохраняются. В учебном примере под замену идёт только EMAIL_ADDRESS, а A-17 и 40 EUR объявлены сохраняемыми. Любое расширение списка сущностей меняет эталон и требует нового прогона проверок.
Схема данных описывает вход и выход. Вход: строка текста, список RecognizerResult с полями entity_type, start, end, score, словарь операторов. Выход: EngineResult содержит преобразованную строку text и список OperatorResult с полями start, end, entity_type, text и operator. Исходную строку и ручные спаны сохраняйте отдельно для сверки. Эта схема совпадает с описанием классов в документации проекта на presidio.dataprivacystack.org.
Критерии приёмки формулируются проверяемо. Первый: срез text[start:end] совпадает с ожидаемой сущностью. Второй: результат равен ручному эталону посимвольно. Третий: сохраняемые фрагменты присутствуют в выходном тексте. Четвёртый: список items содержит запись для каждого заменённого спана. Пятый: при пустом результате анализа текст помечается как непроверенный.
Порядок работы: подготовить вход и разметку, проверить границы, собрать карту операторов, выполнить обезличивание, сверить результат с эталоном, зафиксировать расхождения. При подключении AnalyzerEngine добавить шаг сверки его спанов с текстом и отдельный набор проверок на пропуски.
Частые вопросы
Как выбрать оператор для email в Presidio?
Для замены на метку подходит replace с параметром new_value. Оператор redact удалит адрес полностью, mask закроет часть символов, hash вернёт необратимый отпечаток. Выбор зависит от того, нужна ли читателю связь между записями и требуется ли восстановление исходного значения.
Почему hash даёт разные значения для одинакового текста?
Документация проекта на presidio.dataprivacystack.org указывает, что начиная с версии 2.2.361 оператор hash использует случайную соль по умолчанию. Одинаковые значения получают разные хеши в разных вызовах. Для согласованности соль передают параметром и хранят в защищённом месте.
Что происходит при перекрытии спанов?
При полном перекрытии берётся сущность с более высоким score, при равных score выбор произволен. Когда одна сущность содержится в другой, используется более длинный фрагмент. При частичном пересечении каждая сущность обезличивается отдельно, и результаты склеиваются.
Можно ли запустить Presidio как HTTP-сервис?
Да, документация проекта на presidio.dataprivacystack.org приводит запуск через Docker на порту 5001 и через python app.py. Запрос на обезличивание отправляется методом POST на /anonymize с телом JSON, содержащим текст, словарь anonymizers и массив analyzer_results.
Достаточно ли замены email для анонимности документа?
Замена email закрывает один тип сущности. Номер заказа и сумма остаются в тексте по условиям учебного контракта. Документация проекта на presidio.dataprivacystack.org предупреждает, что автоматическое обнаружение не гарантирует нахождение всей чувствительной информации, поэтому применяются дополнительные системы и защиты.
