Защита данных LLM на практике: как встроить фильтр без потери стриминга
Компания, стоящая за Guardrails Filter, столкнулась с тем, что замаскировать персональные данные в запросах к большой языковой модели (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 часов с поддержкой потоковой выдачи
Пошаговая инструкция: как встроить фильтр данных между приложением и моделью
-
Перехватите входящий запрос до отправки в LLM. Запрос приходит в формате JSON и содержит массив сообщений (полную историю диалога). Каждое новое обращение пользователя отправляется вместе со всей перепиской, потому что модель ничего не помнит между вызовами. Фильтр обязан проходить по всей истории, а не только по последнему сообщению.
-
Найдите персональные данные регулярными выражениями. Пройдите по тексту каждого сообщения. Для русского контекста добавьте паттерны СНИЛС, серии паспорта, ИНН. Каждому найденному значению присвойте уникальный плейсхолдер с порядковым номером:
+7 999 123-45-67 → <PHONE_1>
+7 888 765-43-21 → <PHONE_2>
Иванов Пётр → <NAME_1>
123-456-789 00 → <SNILS_1>
-
Постройте таблицу соответствий. Перед маскировкой проверяйте: если такое значение уже встречалось в сессии, используйте тот же плейсхолдер. Один телефон всегда получает один и тот же идентификатор на протяжении всего диалога. Без этого шага восстановить данные после ответа модели невозможно.
-
Отправьте замаскированный запрос в LLM. Модель увидит
<PHONE_1>вместо реального номера и будет работать с ним как с обычным текстом. -
Обработайте ответ модели. Здесь два сценария.
Обычный JSON-ответ (без стриминга): получите ответ целиком, найдите в нём плейсхолдеры, подставьте исходные значения из таблицы, верните результат пользователю.
Потоковый ответ (SSE-стриминг): ответ приходит отдельными чанками (фрагментами по несколько символов). Плейсхолдер может оказаться разрезан между чанками. Решение: не отправляйте каждый чанк сразу, а накапливайте буфер. Размер буфера привяжите к максимальной длине плейсхолдера.
- Проверяйте буфер по четырём пунктам перед отправкой пользователю:
- Есть ли в тексте открывающая угловая скобка
< - Закрылась ли она символом
> - Находится ли внутри известный ключ из таблицы соответствий
- Можно ли восстановить исходное значение
Если совпадение найдено, подставляйте реальные данные. Если нет, отправляйте текст дальше без изменений.
-
Демаскируйте данные не только в тексте ответа. Ответ модели может содержать несколько полей: reasoning (цепочка рассуждений), content (итоговый текст), tool_calls (вызовы инструментов) и данные о расходе токенов (единиц текста, по которым считается стоимость запроса). Если модель вызывает инструмент и передаёт туда содержимое с плейсхолдером, файл создастся с заглушкой вместо пароля. Восстанавливайте значения во всех полях.
-
Определите завершение потока. По самому тексту понять, закончился ли ответ, нельзя. Ориентируйтесь на служебные сигналы 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 работают на российских серверах, но при использовании внешних моделей через прокси фильтр обязателен.
По моим наблюдениям, большинство тех, кто подключает LLM к рабочим процессам, вообще не задумываются о промежуточном слое. Данные летят в API открытым текстом. Описанный подход с таблицей соответствий и буферизацией стриминга, пожалуй, минимально необходимая архитектура. Она не требует дообучения (fine-tuning, обучения модели на ваших примерах под узкую задачу) и не ломает существующие интеграции.
Честная оговорка: регулярные выражения не покрывают все форматы данных. Имена без контекста ловятся плохо, нестандартные форматы СНИЛС или паспортных номеров могут проскочить. Для критичных сценариев стоит добавить второй слой на базе NER-модели (модель распознавания именованных сущностей). Но для 80% задач автора или маркетолога описанного метода хватает.
Фильтр между приложением и моделью не заменяет политику работы с данными, но решает конкретную инженерную задачу: не дать персональным данным уйти туда, куда они не должны попасть, и при этом сохранить стриминг, вызовы инструментов и структуру ответа.
Генератор промптов dzen.guru
Попробуйте создать промпт, который сразу учитывает маскировку данных и структуру запроса к LLM
Попробовать генератор
Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».
Читайте также

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

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

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