Давным-давно, а точнее два года назад, мы сделали AI-сервис Daisy для быстрого доступа ко всем передовым моделям. Под капотом у Daisy не просто токены к LLM, а многоуровневая архитектура со сложной логикой связок и своей системой безопасности.
Собственно, про безопасность мы сегодня и поговорим.
Суть проблемы PII 😕
Чтобы гарантировать пользователям Daisy защиту персональных данных и не «светить» их во внешний API, мы встроили в пайплайн обработки запросов PII-анонимизатор.
Почти сразу мы упёрлись в проблему: анонимизатор плохо обрабатывал таблицы. Он маскировал только первые пару строк, а остальные данные улетали во внешнюю модель без маскировки.
Обычно таблицу перед отправкой переводят в Markdown. Модели так удобнее: структура сохраняется, строки и колонки легко читаются. А вот анонимизатору — наоборот.
Он проверяет значения по правилам, но для многих типов данных этого мало — нужен контекст. Например, слова вроде «паспорт», «ИНН» или «карта».
В таблице весь этот контекст уходит в шапку. Чем больше строк, тем дальше значения от заголовков и тем хуже детекция.
| ФИО | Паспорт | ИНН |
| Иван Петров | 4509 217634| 771234567890 |
Быстрый фикс с подвохом 😊
Первое очевидное решение — переписать Markdown и добавить ключ внутрь каждой ячейки.
| ФИО: Иван Петров | Паспорт: 4509 217634 |
Точность детекции сразу растёт: подпись снова рядом, пропусков меньше.
Но у такого подхода есть обратная сторона. Этот обогащённый Markdown увидит и сама модель. Таблица раздувается, токены тратятся впустую, а модель получает служебные подсказки, которые нужны только детектору.
Сначала ячейки, потом другое 😊
Мы зашли с другой стороны и решили не «сплющивать» таблицу до анонимизации.
Идея в том, чтобы разделить два представления таблицы: одно для детектора, другое для модели.
Держим таблицу как структуру — ячейки, строки, колонки, координаты. И отдельно, только для детектора, собираем detect-context: временную строку для каждого значения, где прописаны положение заголовка, метки, ближайшие соседи.
Паспорт: 4509 217634
Эта строка живёт отдельно и в модель не попадает — поэтому её можно делать сколь угодно избыточной, лишь бы детектору было удобно.
Детектор находит PII в этой строке. А раз мы знаем координаты — возвращаемся к нужной ячейке, достаём точное значение и маскируем именно его. И только потом собираем Markdown для модели.
Чистый промпт и детекция 😍
Мы отвязали детекцию от итогового формата. Детектор точно маскирует персональные данные по изолированному контексту, а в модель уходит чистый Markdown без служебного мусора. В итоге качество анонимизации зависит только от точности детектора, а не от структуры данных.
Автор этого поста и бессменный исследователь вопросов безопасности, Андрей Иванов — NLP-инженер в R&D red_mad_robot.
#Безопасность