Любая задача, где в тексте ФИО, телефоны, почты, ИНН, а обрабатывать хочется облачной моделью — это вопрос: как отдать модели смысл, но не отдать людей. Отлаживал это всё лето. Три подхода, каждый под свою задачу.
Маскирование с демаскированием. Основной механизм под поток данных. Имена меняются на
[PERSON_1], почты на [EMAIL_1], соответствие маска ↔ реальное значение сохраняется в таблице. Модель работает с масками, на выходе реальные данные подставляются обратно по таблице.Маски не проставляешь руками. Просишь модель написать два скрипта: один маскирует, второй демаскирует. LLM сама генерит инструмент, который чистит данные перед тем, как они попадут ей же на вход. А восстановление идёт по таблице, без LLM — детерминированно, без галлюцинаций.
Полное удаление. Вырезаешь персуху совсем — «кто-то написал кому-то». Под узкий массив для дашборда, где нужны только цифры и структура, вполне работает.
Псевдообезличивание. Компромисс, когда текст должен остаться читаемым для человека:
Иванова Виктория Сергеевна → Иванова В.
+7 (926) 710-76-35 → +7 (XXX) XXX-XX-35
ivanova@company.ru → i***@company.ru
ИНН 7707083893 → 77•XXX•XXXX
Аналитик видит связный текст, модель различает людей, восстановить полное ФИО нельзя. Под дашборды годится, под полный поток — ограниченно: защита слабее масок.
Как устроен pipeline на масках.
Сырьё падает в RED — папку с оригиналами, chmod 700. Облако туда не заглядывает никогда. Скрипт-анонимайзер прогоняет данные и складывает маскированную версию в GREEN. В облако уходит только GREEN.
Таблица соответствий —
mapping.json: на сегодня 1 245 персон, 3 483 телефона, 1 969 почт, 19 070 идентификаторов. Лежит в RED под chmod 600, машину не покидает.На выходе — демаскирование: модель отработала на масках, финальный текст прогоняется через второй скрипт, и
[PERSON_47] превращается обратно в реальное имя. Та же таблица работает в обе стороны: имя → маска на входе (например, для поиска по архиву), маска → имя на выходе.Где маски протекают.
Самое сложное — найти все имена. Телефоны, почты, ИНН ловятся regex детерминированно, ложняков ноль. А ФИО в свободном тексте regex не берёт: склонения, производные, слитное написание — «ЕвгеньевичМиженский», «Бровкине», «Вадимовна». На корпусе из 2 505 документов после первой версии анонимайзера осталось 2 821 утечка — 11%.
Второй слой — NER через локальную qwen3:14b: модель находит имена в любом месте текста, секунды на документ. Медленное другое — LLM-проверка, безопасен ли фрагмент для отправки в облако: 10 фрагментов в минуту, на полном архиве это 160 часов.
Третий заход — компонентная маскировка ФИО: фамилия, имя, отчество отдельно, плюс падежи. Утечки 2 821 → 71. В 40 раз меньше.
Где имена утекают не из текста.
Самая незаметная дыра — метаданные. NER читает тело письма, а поля sender, recipient, organizer, attendees не видит. Имена текли именно оттуда. Решение: эти поля маскируются принудительно, по структуре, без всякого NER. Правило: каждое поле, где может быть человек — маскируется всегда.
Принцип.
513 фрагментов после всех проверок так и не прошли в GREEN — остались локальными. Если фрагмент мог протечь — не пускаем. Лучше потерять кусок данных, чем утечь. Не надеяться, что замаскировал — проверять, что не утекло.
Облако никогда не видит RED. Если видит — это не баг, это тревога.
В этом посте копнул глубже обычного. Дайте знать в комментариях, насколько понятно — и нужно ли проще.