TL;DR
- Internal research tool: безопасная подача медицинских кейсов в LLM-помощника, нужно убрать ПД и идентификаторы.
- Стартовали с «LLM сама обезличивает» — получили дрейф ответов, утечки имени, неработоспособные плейсхолдеры.
- Переключились на детерминированный пайплайн распознавания + правила; LLM осталась только там, где она правда нужна. Результат — стабильно, проверяемо и в 2,5 раза быстрее ручной обезличивки.
Контекст
Это внутренний инструмент, а не клиентский кейс. Делал, чтобы безопасно отдавать медицинские истории врачу-консультанту через LLM-ассистента: нужно убрать ФИО, даты рождения, адреса, номера документов, не теряя клинического смысла. На вход — свободный текст истории болезни, на выход — обезличенный текст и таблица замен.
Проблема
Первый прототип «LLM анонимизирует целиком в одном вызове» выглядел красиво, но:
- Имена иногда оставлялись «по контексту» (модель решала, что это важно).
- Плейсхолдеры разбегались (
PERSON_1в одном месте,Пациент Ав другом). - При повторном прогоне ответы могли отличаться — невозможно ни проверить, ни откатить.
Для медицинского контекста это — стоп-фактор.
Подход
Поворот: модель там, где она нужна, код — там, где можно без неё.
- Распознавание сущностей детерминированно. NER-модель (spaCy + русский медицинский словарь) находит имена, даты, диагнозы, номера. Это воспроизводимый шаг.
- Правила и таблица замен в коде. Каждой сущности — стабильный плейсхолдер (
ИМЯ_1,ДАТА_2), таблица замен сохраняется отдельно, можно откатить. - LLM — только на «сложные» куски. Когда нужно перефразировать редкие диагнозы или мед-аббревиатуры. Узкий промпт, проверка на длину/совпадение терминов.
- Валидатор. Финальный проход: ищет в обезличенном тексте остатки ФИО/дат/номеров по словарям и регулярным выражениям. Если что-то нашлось — текст не уходит.
Результат
- Ручная обезличивка одной истории — 12–15 минут. Пайплайн — 5–6 минут (включая ручную проверку валидатора).
- −60 % времени на типовую историю.
- Стабильность 1:1 на повторном прогоне: тот же текст даёт тот же результат.
Что бы сделал иначе
Сразу бы построил валидатор первым, а не последним. Когда был только «LLM делает всё», именно отсутствие проверки на выходе и маскировало ошибки. Валидатор-сначала вынудил бы быстрее уйти в детерминированный путь.