Игорь Градов
Игорь Градов
6 мин
ai

Защита данных LLM на практике: как встроить фильтр без потери стриминга

Компания, стоящая за Guardrails Filter, столкнулась с тем, что замаскировать персональные данные в запросах к большой языковой модели (LLM, нейросеть, которая генерирует текст по промпту) оказалось проще, чем встроить этот фильтр в реальный рабочий процесс без потери стриминга и вызова инструментов.

Защита данных LLM на практике: как встроить фильтр без потери стриминга
Почему это важно

Защита данных LLM перестаёт быть задачей «найти и заменить»: фильтр обязан корректно обрабатывать потоковую выдачу, вызовы инструментов и полную историю диалога, иначе приложение ломается или данные утекают в открытом виде.

Разработчики Guardrails Filter описали архитектуру решения, которое ставится между приложением и языковой моделью. Фильтр перехватывает запросы, маскирует персональные данные (телефоны, почту, паспорт, СНИЛС, имена), а после ответа модели возвращает исходные значения. Проблема в том, что каждый из этапов содержит подводные камни, о которых не пишут в документации.

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

Что понадобится?

  • Доступ к API любой LLM, поддерживающей Chat Completions (OpenAI, совместимые прокси, в РФ подойдут YandexGPT или GigaChat через их API)
  • Промежуточный сервис или прокси-слой, через который проходят запросы (можно реализовать на Python, Node.js или готовом решении вроде Guardrails Filter)
  • Набор регулярных выражений для российских типов персональных данных: телефон (+7), СНИЛС (формат XXX-XXX-XXX XX), серия и номер паспорта, email, ФИО
  • Хранилище для таблицы соответствий «плейсхолдер (заглушка, подставляемая вместо реального значения) : исходное значение» (хватит словаря в оперативной памяти на время сессии)
  • Время на внедрение: от 2 до 4 часов для базовой версии без стриминга, от 8 часов с поддержкой потоковой выдачи

Пошаговая инструкция: как встроить фильтр данных между приложением и моделью

  1. Перехватите входящий запрос до отправки в LLM. Запрос приходит в формате JSON и содержит массив сообщений (полную историю диалога). Каждое новое обращение пользователя отправляется вместе со всей перепиской, потому что модель ничего не помнит между вызовами. Фильтр обязан проходить по всей истории, а не только по последнему сообщению.

  2. Найдите персональные данные регулярными выражениями. Пройдите по тексту каждого сообщения. Для русского контекста добавьте паттерны СНИЛС, серии паспорта, ИНН. Каждому найденному значению присвойте уникальный плейсхолдер с порядковым номером:

+7 999 123-45-67  →  <PHONE_1>
+7 888 765-43-21  →  <PHONE_2>
Иванов Пётр      →  <NAME_1>
123-456-789 00    →  <SNILS_1>
  1. Постройте таблицу соответствий. Перед маскировкой проверяйте: если такое значение уже встречалось в сессии, используйте тот же плейсхолдер. Один телефон всегда получает один и тот же идентификатор на протяжении всего диалога. Без этого шага восстановить данные после ответа модели невозможно.

  2. Отправьте замаскированный запрос в LLM. Модель увидит <PHONE_1> вместо реального номера и будет работать с ним как с обычным текстом.

  3. Обработайте ответ модели. Здесь два сценария.

Обычный JSON-ответ (без стриминга): получите ответ целиком, найдите в нём плейсхолдеры, подставьте исходные значения из таблицы, верните результат пользователю.

Потоковый ответ (SSE-стриминг): ответ приходит отдельными чанками (фрагментами по несколько символов). Плейсхолдер может оказаться разрезан между чанками. Решение: не отправляйте каждый чанк сразу, а накапливайте буфер. Размер буфера привяжите к максимальной длине плейсхолдера.

  1. Проверяйте буфер по четырём пунктам перед отправкой пользователю:
  2. Есть ли в тексте открывающая угловая скобка <
  3. Закрылась ли она символом >
  4. Находится ли внутри известный ключ из таблицы соответствий
  5. Можно ли восстановить исходное значение

Если совпадение найдено, подставляйте реальные данные. Если нет, отправляйте текст дальше без изменений.

  1. Демаскируйте данные не только в тексте ответа. Ответ модели может содержать несколько полей: reasoning (цепочка рассуждений), content (итоговый текст), tool_calls (вызовы инструментов) и данные о расходе токенов (единиц текста, по которым считается стоимость запроса). Если модель вызывает инструмент и передаёт туда содержимое с плейсхолдером, файл создастся с заглушкой вместо пароля. Восстанавливайте значения во всех полях.

  2. Определите завершение потока. По самому тексту понять, закончился ли ответ, нельзя. Ориентируйтесь на служебные сигналы API: поле finish_reason в ответе Chat Completions или событие [DONE] в SSE-потоке. Только после этого отправляйте остаток буфера пользователю.

Пример: маскировка конфигурации с паролем

Пользователь отправляет модели конфигурацию сервиса:

{
  "db_password": "S3cretPass!",
  "admin_email": "ivan@company.ru",
  "admin_phone": "+7 999 123-45-67"
}

После маскировки модель видит:

{
  "db_password": "<PASSWORD_1>",
  "admin_email": "<EMAIL_1>",
  "admin_phone": "<PHONE_1>"
}

Пользователь просит: «Измени порт на 8080 и сохрани в новый файл». Модель делает tool_call на создание файла. В аргументах tool_call остаётся <PASSWORD_1>. Фильтр перехватывает аргументы, подставляет S3cretPass!, и файл создаётся с настоящим паролем. Без обработки tool_calls пользователь получил бы файл с заглушкой.

Частые ошибки

Замена без нумерации. Если два разных телефона получают одинаковый плейсхолдер <PHONE>, восстановить, какой куда, невозможно. Всегда нумеруйте: <PHONE_1>, <PHONE_2>.

Обработка только последнего сообщения. LLM не хранит контекст. Каждый запрос содержит всю историю. Если фильтр маскирует только новое сообщение, старые данные уйдут в модель открытым текстом.

Отправка стриминговых чанков без буферизации. Плейсхолдер <PHONE_NUMBER_1> может прийти тремя кусками: <PHO, NE_NUM, BER_1>. Без буфера пользователь увидит обрывки вместо номера.

Демаскировка только в content. Если модель вызывает инструмент (tool_call), данные в аргументах вызова тоже содержат плейсхолдеры. Пропустите это поле, и созданный файл или отправленное письмо получат заглушку вместо реальных данных.

Преждевременная отправка остатка буфера. Не отправляйте последние символы, пока не получите сигнал завершения потока (finish_reason или [DONE]). Иначе обрежете плейсхолдер на полуслове.

Что делать с этим прямо сейчас, по ролям

Автору Дзена и копирайтеру. Если вы передаёте в ChatGPT или YandexGPT тексты с реальными контактами клиентов, адресами, телефонами для рерайта или анализа, вручную замените их на условные значения перед отправкой. Это ручной аналог описанного фильтра. Для регулярной работы настройте прокси-скрипт или используйте инструменты с защитой данных LLM на стороне сервера.

Маркетологу. При интеграции LLM в CRM или чат-бота клиентские данные проходят через внешний API. Без промежуточного фильтра имена, телефоны и email утекают провайдеру модели. Убедитесь, что ваш подрядчик объяснил, как именно реализована защита данных LLM в его решении.

Предпринимателю в РФ и СНГ. Российское законодательство о персональных данных (152-ФЗ) требует согласия субъекта на передачу данных третьим лицам. Отправка СНИЛС или паспортных данных в API зарубежной модели без маскировки создаёт юридический риск. Из доступных в РФ решений: YandexGPT и GigaChat работают на российских серверах, но при использовании внешних моделей через прокси фильтр обязателен.

Мнение редакции dzen.guru

По моим наблюдениям, большинство тех, кто подключает LLM к рабочим процессам, вообще не задумываются о промежуточном слое. Данные летят в API открытым текстом. Описанный подход с таблицей соответствий и буферизацией стриминга, пожалуй, минимально необходимая архитектура. Она не требует дообучения (fine-tuning, обучения модели на ваших примерах под узкую задачу) и не ломает существующие интеграции.

Честная оговорка: регулярные выражения не покрывают все форматы данных. Имена без контекста ловятся плохо, нестандартные форматы СНИЛС или паспортных номеров могут проскочить. Для критичных сценариев стоит добавить второй слой на базе NER-модели (модель распознавания именованных сущностей). Но для 80% задач автора или маркетолога описанного метода хватает.

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

Генератор промптов dzen.guru

Попробуйте создать промпт, который сразу учитывает маскировку данных и структуру запроса к LLM

Попробовать генератор
Поделиться:TelegramVK
Игорь Градов
Игорь Градов

Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».

Комментарии

Читайте также

Qwen 3.8 27B обошла Claude Opus 4.6 и работает на домашней видеокарте с 24 ГБ
ai

Qwen 3.8 27B обошла Claude Opus 4.6 и работает на домашней видеокарте с 24 ГБ

Qwen 3.8 27B вышла на Hugging Face в июне 2025 года, и это первая мультимодальная модель такого размера, которую реально запустить на домашней видеокарте с 24…

5 мин
Французский Kog ускоряет инференс в 30 раз: оптимизация GPU без замены оборудования
ai

Французский Kog ускоряет инференс в 30 раз: оптимизация GPU без замены оборудования

Французский стартап Kog привлёк первых клиентов после майской демонстрации, на которой показал инференс (генерацию ответов нейросети) со скоростью 3 000…

6 мин
Needle 2 весит 14 МБ и работает без GPU: лёгкие нейросети дошли до умных часов
ai

Needle 2 весит 14 МБ и работает без GPU: лёгкие нейросети дошли до умных часов

Считаю лид: «Компания Cactus Compute в июне 2025 года выпустила Needle 2, открытую нейросеть на 45 миллионов параметров, которая умещается в файл размером 14…

6 мин