Liza Studio ← На главную

Безопасность · Персональные данные

Обфускация данных в Лизе

Как прикрутить к Лизе слой маскирования персональных данных перед отправкой запроса во внешнюю модель (Claude / любой облачный ИИ).

Зачем. Пока Лиза работает как шлюз на Claude, каждый запрос пользователя физически уходит в Anthropic (США). Сырые персональные данные (ПДн) россиян в таком запросе = трансграничная передача + риск по 152-ФЗ. Обфускация маскирует ПДн до отправки и восстанавливает их в ответе. Когда Лиза переедет на свою ИИ внутри периметра — слой можно ослабить или выключить одним флагом.

01Главный принцип: обфускация — это отдельный слой, а не правка ядра

Никогда не зашивай маскирование в бизнес-логику бота. Сделай его прослойкой между ядром Лизы и адаптером модели:

Пользователь
   ↓
[Ядро Лизы: роутинг агентов, промпты, контекст]
   ↓
[СЛОЙ ОБФУСКАЦИИ]  ← mask() на входе, unmask() на выходе   (вкл/выкл флагом)
   ↓
[Адаптер модели: Claude | GPTunnel | своя ИИ]
   ↓
   ответ → unmask() → пользователь

Это даёт два бонуса:

02Что именно маскировать

Маскируем всё, что позволяет опознать человека (PII). Минимальный набор для русскоязычного бота:

ТипПримерЗаглушка
Имя/ФИОИван Петров[PER_1]
Телефон+7 900 123-45-67[PHONE_1]
Emailivan@mail.ru[EMAIL_1]
Telegram-username/id@ivan, 123456789[TG_1]
Карта/счёт4276 1600 …[CARD_1]
Паспорт/СНИЛС/ИНН4509 123456[DOC_1]
Адресг. Москва, ул. …[ADDR_1]
Дата рождения14.03.1990[DOB_1]
Гео-координаты55.75, 37.61[GEO_1]

Важно: нумеруем заглушки ([PER_1], [PER_2]), чтобы модель различала разных людей и сохраняла связность ответа.

03Два режима — выбери под задачу

A. Псевдонимизация (обратимая) — рабочий режим по умолчанию

Храним карту заглушка → оригинал локально на время запроса. После ответа модели восстанавливаем реальные значения. Пользователь видит нормальный текст.

B. Обезличивание (необратимое)

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

Правило: диалог → режим A, логи/аналитика → режим B.

04Жизненный цикл одного запроса

1. Пользователь пишет в Лизу.
2. mask(text) → текст с заглушками + map {заглушка: оригинал}.
3. В Claude уходит ТОЛЬКО маскированный текст. map НИКОГДА не уходит в модель.
4. Модель отвечает заглушками ([PER_1] записан на [PHONE_1]).
5. unmask(answer, map) → реальные значения возвращаются.
6. Пользователь получает нормальный ответ.
7. map уничтожается после запроса (не пишем в долгую память сырьём).

Ключевое: карта соответствия живёт только в оперативной памяти на время обработки и никогда не покидает твой сервер.

05Реализация (Python, псевдокод-референс)

Бот на Python — вот минимальный слой. Адаптируй под свою структуру.

import re

# --- паттерны PII (расширяй под свои данные) ---
PATTERNS = {
    "EMAIL": r"[\w.+-]+@[\w-]+\.[\w.-]+",
    "PHONE": r"(?:\+7|8)[\s\-]?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}",
    "CARD":  r"\b(?:\d[ \-]?){13,19}\b",
    "TG":    r"@[A-Za-z0-9_]{4,32}",
}

def mask(text: str):
    """Возвращает (маскированный_текст, карта_соответствия)."""
    mapping = {}
    counters = {}
    def repl(kind):
        def _r(m):
            original = m.group(0)
            # один и тот же оригинал → одна и та же заглушка
            for token, val in mapping.items():
                if val == original:
                    return token
            counters[kind] = counters.get(kind, 0) + 1
            token = f"[{kind}_{counters[kind]}]"
            mapping[token] = original
            return token
        return _r
    for kind, pat in PATTERNS.items():
        text = re.sub(pat, repl(kind), text)
    return text, mapping

def unmask(text: str, mapping: dict):
    """Возвращает реальные значения по карте."""
    for token, original in mapping.items():
        text = text.replace(token, original)
    return text

Использование в обработчике сообщения:

OBFUSCATION = True  # ← флаг. На своей ИИ ставишь False.

async def handle(user_text: str) -> str:
    if OBFUSCATION:
        safe_text, mapping = mask(user_text)
    else:
        safe_text, mapping = user_text, {}

    answer = await call_model(safe_text)   # сюда уходит ТОЛЬКО safe_text

    return unmask(answer, mapping) if OBFUSCATION else answer

Для имён, адресов и ФИО регулярками не обойтись — используй NER-библиотеку (natasha для русского языка, spaCy с русской моделью) и добавляй найденные сущности в ту же карту. Регулярки закрывают «структурные» PII (телефон, email, карта), NER — «текстовые» (имена, города, организации).

06Чего НЕЛЬЗЯ делать

07Тестирование слоя

Минимальный чек перед боем:

  1. Утечка: прогони реальный запрос с телефоном/именем → проверь, что в payload к модели (перехвати на адаптере) PII отсутствует.
  2. Обратимость: unmask(mask(x)[0] …) возвращает исходный текст 1:1.
  3. Связность: один и тот же человек в тексте получает одну заглушку ([PER_1] везде), не плодит [PER_1] [PER_2] для одного Ивана.
  4. Ответ модели: модель, получив заглушки, корректно их использует в ответе, и они восстанавливаются.
  5. Флаг: при OBFUSCATION=False бот работает как раньше (для будущей своей ИИ).

08Юридический контекст (коротко, не замена юристу)

Обфускация закрывает техническую сторону, но для шлюза на Claude остаются организационные требования 152-ФЗ:

Полное необратимое обезличивание выводит данные из-под этих требований. Псевдонимизация — снижает риск, но не гарантирует вывод из-под 152-ФЗ. Перед боевым запуском связку «что маскируем + уведомление РКН + согласие + локализация» проверь с профильным юристом по персональным данным.

09Дорожная карта