Когда персональные данные пользователей попадают в LLM-агента, возникает выбор: либо передавать их как есть, либо предварительно анонимизировать. Второй вариант очевидно безопаснее, но появляется вопрос — насколько агент деградирует, если вместо "Иванов Иван" он видит "PERSON_1" или "XXXXXXXX"?
Мы проверили это на быстро поднятом минималистичном банковском агенте с помощью Hivetrace Dataclean. Отправили в него по 102 синтетических запроса в трёх вариациях входных данных (чистые, маска, псевдонимы), оценка через DeepEval LLM-as-a-judge. Результаты — ниже.
Цель исследования
В данной статье нам рассказали про инструмент для анонимизации данных.
Там основной фокус был смещен на продукт и его конкурентов, а также метрики NER моделей, в этой статье мы рассмотрим как инструменты для анонимизации персональных данных влияют на работу LLM систем.
Чтобы понять, что такое хорошо получить данные, которые потом можно сравнить с другими подходами или более новыми версиями инструментов и LLM, сделаем замеры в каких-то реальных числовых значениях. Тут нам понадобится датасет с выдуманными запросами пользователей, небольшой агент, который будет на всё это отвечать, и некто или нечто, что оценит, насколько он хорош и как мощны его лапищи.
Методология
Сравнивать работу LLM-агента будем на трёх типах данных:
Чистые
Замаскированные: замена на “XXX…X” совпадающей длины
Псевдонимизированные: замена на PERSON_1, PHONE_1 и т.п.;
Подход (3), в отличие от замены на "имя из образца бланка", вроде “Иванов Иван Иванович” точно случайно не совпадёт с реальными исходными данными, и может быть использован для множественных подстановок, чисел у нас много, тогда как для “шаблонных” имён придётся завести набор значений, и с какого-то момента думать, на какое место в этом ряду ставить “Сидорова Сидора Сидоровича” по степени "стандартности" и частоте появления в реальной жизни. Кроме того, это в теории должно усложнить задачу агенту, так как имена перестают быть реалистичными. В то же время, при подмене имён на другие, но настоящие, помощь от агента не зависит от того, Иванов пользователь или Петров. Ну или хотя бы не должна, все мы знаем про нюансы ответов LLM.
Набор чистых входных данных генерируем в Qwen 3.5-Plus. Почему именно эта модель? Да просто потому что попалась под руку, выдаёт неплохой запас запросов на день бесплатно (а бюджеты тратить пойдём чуть позже), хоть и с определёнными ограничениями в минуту, но под такую задачу точно хватит, запрос-то по сути один. А ещё Qwen обладает собственным локально устанавливаемым CLI, на который было интересно взглянуть, но пока не про него. С целью большего количества персоналки на квадратный сантиметр текста просим его сделать достаточно неосторожные запросы, в которых светятся имена, телефоны, номера карт, CVC, адреса, пароли, явки. Идеально, если ещё и несколько типов в одном запросе. Просили 100, отдал 102, почему бы и не все использовать. Варианты очистки (2) и (3) делаем с помощью встроенной в Hivetrace системы для очистки персональных данных, далее Hivetrace Dataclean или просто Dataclean.
Пример исходного запроса:
Добрый день! Хочу получить выписку по карте 4000 1234 5678 9012, но в приложении не могу найти. Можете отправить на адрес petrov.ivan@yandex.ru?
В качестве агента создаём минималистичного помощника в банковской сфере на gpt-4.1, отвечающего на вопросы только в строго заданном списке тематик и предоставляющего рекомендации по решению вопроса. Так как он нам всё равно не для прода, попросим в ответе явно подсвечивать, какая именно тема выбрана.
Список тем из инструкций агента:## Список сценариев (строго!):
Получение справок и выписок по счётуПомощь пострадавшим от мошеннических действийКонсультации по переводам и проблемам с нимиЗапросы на страхование недвижимостиЗапросы на выдачу кредита
При этом в качестве провокации у нас будут запросы пользователей на страхование карты, а не недвижимости.
Общий пример ответа (тот же запрос, что выше, но в версии с маской):{
"request": "Добрый день! Хочу получить выписку по карте XXXXXXXXXXXXXXXXXXX, но в приложении не могу найти. Можете отправить на адрес XXXXXXXXXXX@XXXXXXXXX?",
"response": "Для оформления выписки уточните, пожалуйста:\n1. За какой период нужна выписка (например, последний месяц, 2023 год и т.д.)?\n2. Хотите получить выписку только по этой карте или по всем счетам?\n3. Ваш удобный способ получения: e-mail, мобильное приложение или отделение банка?\n\nПожалуйста, не указывайте полный номер карты и другие конфиденциальные данные. Жду ваш ответ!”
}
Оценка результатов, инструментарий
Для оценки результатов будем использовать уже вполне классический в текущих реалиях подход LLM-as-a-judge. За основу возьмём готовый инструмент DeepEval с надстроенной инструкцией для проверки определения тематики и качества помощи от агента.
У него имеются два основных варианта проверять пары вопрос-ответ:
LLMTestCase
GEval
Первый подойдёт для использования со входным набором input+expected+actual+context и местным функционалом assert_test с целью проверки результата прошло/не прошло. Нам же больше интересен второй, так как он умеет выдавать конкретную оценку в попугаях диапазоне [0, 1].
Детали подкапотной части:
Что | Модель | Настройки | Описание |
Агент | gpt-4.1 | default из openai модуля | Банковский помощник, определяющий тему вопроса из строго заданного набора, затем предлагающий рекомендации |
Судья | gpt-5.2 | default от DeepEval (temperature=0) | Оценивает попадание в тему и совет. Модель выбрана как более новая и актуальная для оценки ответов от агента на 4.1 |
Делаем базовый test-case, он нам всё равно потребуется:test_case = LLMTestCase(
input=item["request"],
actual_output=item["response"],
)
Создаём метрику (criteria - это любое наше словесное описание, что считать “правильным”):metric = GEval(
name="BankingAnswerQuality",
criteria=criteria.strip(),
evaluation_params=[
LLMTestCaseParams.INPUT,
LLMTestCaseParams.ACTUAL_OUTPUT,
],
model=llm_model,
)
Замеряем и сохраняем:metric.measure(test_case)
out = {**item, "score": metric.score, "reason": metric.reason}
Пример оценки:{
<тут были чистые request и response, которые мы уже видели>,
"score": 1.0,
"reason": "Тема запроса во входе — получение выписки по конкретной банковской карте и отправка её на e-mail (пользователь не находит в приложении и просит выслать на petrov.ivan@yandex.ru). Тема ответа — получение справок/выписок по счёту: уточняются период, охват (по карте или всем счетам) и способ получения, включая e-mail, также есть предупреждение о конфиденциальных данных. Основная тематика совпадает: оформление выписки и выбор канала получения."
}
Поле reason вытаскиваем больше для себя, чтобы можно было проверить логику при подозрениях, когда оценка, например, слишком низкая.
Гоняем судью на одном и том же наборе вопрос-ответ 10, а затем ещё 10 раз, и считаем среднее значение, во избежание влияния отдельных галлюцинаций на результаты.
Результаты
Варианты входных данных для агента: чистые, маскировка (замена персональных на XXXX…X), псевдонимизация (замена на PERSON_1, PHONE_1, …)
Количество запросов: 102
Количество прогонов каждого типа данных через оценщика: 20
Результаты оценки:
(подрезаны снизу для лучшего визуального восприятия отрезков стандартного отклонения)
Запускаем первые 10:

Продолжаем до 20:

Видим, что средние меняются незначительно. Подросло отклонение на маскировке и псевдонимах, но это за счёт отдельных пиков, где gpt решил был более критичным, чем обычно, или наоборот расслабился. В целом, определённая тенденция появилась, можно сэкономить, что осталось на api-ключе.
Недостатки и ограничения текущего подхода
Диалог состоит из пары вопрос-ответ, а не полноценной беседы до решения вопроса агентом или передачи оператору
Персональные данные не используются приложением для дальнейшего реального решения задачи пользователя
Запросы синтетические и могут явно превышать количество данных, предоставляемых за раз в реальных сценариях
Выбранный тип псевдонимизации данных сохраняет для LLM условный контекст: был тут person или address.
Для маски использовалась только буква “X”, а не какая-либо другая или спецсимволы. Поэтому влияние выбора символа на результат неизвестно.
Для работы над пунктом (1) выше DeepEval предоставляет оценку multi-turn проходов с помощью ConversationalTestCase через задание scenario и expected_outcome, что подаёт для дальнейших исследований идею взять более объёмные реалистичные диалоги и попробовать проанализировать их в различных вариантах деперсонализации.
Выводы
По полученным результатам мы можем сделать вывод, что псевдонимизация не имеет значительного влияния на работу конечного агента. В то время как маскировка вызывает более заметную просадку. Также, стоит отметить, что в отличие от маски, которая делает фрагмент запроса не более чем набором букв, точнее набором из буквы, псевдонимизация оставляет подсказку типа данных для LLM.
