Как спроектировать систему, где LLM должна работать с конфиденциальными данными (медицина, финансы)?

Краткий тезис

Проектирование LLM-системы для работы с конфиденциальными данными требует компромисса между функциональностью и безопасностью. Ключевое решение — self-hosted (развёртывание всех компонентов на собственной инфраструктуре) для исключения утечек через API третьих лиц. Дополнительно необходимы: шифрование на всех этапах (включая TEE для памяти), удаление PII перед индексацией, детальный аудит действий и строгое разграничение доступа на уровне документов. Без self-hosted в таких доменах не обойтись — это вопрос compliance.

1. Терминология и контекст

Конфиденциальные данные (sensitive data) — информация, разглашение которой может нанести вред субъекту данных (пациенту, клиенту банка). Включает PHI (Protected Health Information) в медицине и PII (Personally Identifiable Information) в финансах.

Compliance — соответствие требованиям регулирующих органов: HIPAA (Health Insurance Portability and Accountability Act) для США, GDPR (General Data Protection Regulation) для ЕС, 152-ФЗ для РФ.

Self-hosted — развёртывание модели, векторной БД и всего пайплайна на собственных серверах или в приватном облаке, без передачи данных внешним API (OpenAI, Anthropic).

TEE (Trusted Execution Environment) — аппаратная изоляция памяти (Intel SGX, AMD SEV), защищающая данные даже от администратора ОС.

2. Self-hosted как фундамент

Без self-hosted любая передача данных облачному провайдеру (через API) нарушает compliance. Даже если провайдер обещает не сохранять данные, юридически это риск.

Компоненты для self-hosted

КомпонентВариантыПримечание
LLMLlama 3, Mistral, Qwen (open-weight)Нужна модель, которую можно запустить локально
Векторная БДQdrant, Weaviate, MilvusДолжна поддерживать шифрование и RBAC
ОркестраторLangChain, Haystack, LlamaIndexЗапускается на том же сервере
ИнференсvLLM, TGI, OllamaОптимизирует работу LLM на GPU

Почему нельзя использовать API

  • Данные могут быть использованы для дообучения модели провайдера
  • Нет контроля над логированием запросов
  • Юридически сложно доказать, что данные не были переданы третьим лицам

3. Шифрование: на диске и в памяти

Шифрование на диске]] (at rest):

  • Векторная БД должна шифровать индексы (AES-256)
  • Документы в объектном хранилище (S3, MinIO) — шифрование на стороне клиента
  • Ключи управления — в HSM (Hardware Security Module) или Vault (HashiCorp)

Шифрование в памяти (in use):

  • Использование TEE (Intel SGX, AMD SEV) для изоляции памяти LLM
  • Данные расшифровываются только внутри защищённого анклава
  • Даже root-пользователь сервера не видит содержимое памяти

Шифрование в транзите (in transit):

  • Все соединения через TLS 1.3
  • Внутренние микросервисы — mTLS (взаимная аутентификация)

4. PII удаление перед индексацией

Перед тем как документ попадает в векторную БД, из него нужно удалить или замаскировать PII (имена, номера паспортов, телефоны, адреса).

Подходы

  1. NER-модель (Named Entity Recognition): fine-tuned BERT или spaCy для поиска сущностей
  2. Регулярные выражения: для номеров карт, СНИЛС, ИНН
  3. Маскировка: замена на плейсхолдеры (например, [ИМЯ], [ТЕЛЕФОН])
  4. Псевдонимизация: замена на хэш (но необратимо, чтобы нельзя было восстановить)

Пример кода (маскировка PII):

import re
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine

analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()

def mask_pii(text: str) -> str:
    results = analyzer.analyze(text=text, language='ru')
    anonymized = anonymizer.anonymize(text=text, analyzer_results=results)
    return anonymized.text

document = "Пациент Иванов Иван, телефон +7-999-123-45-67"
masked = mask_pii(document)
# Результат: "Пациент <PERSON>, телефон <PHONE_NUMBER>"

Важно маскировка должна быть необратимой. Если нужно сохранить связь с исходным документом — используйте отдельную защищённую таблицу с доступом только для администраторов.

5. Аудит действий

Каждое действие пользователя и системы должно логироваться для расследования инцидентов.

Что логировать

  • Запрос пользователя (в замаскированном виде)
  • Какие документы были найдены retrieval'ом
  • Какой ответ сгенерировала LLM
  • Время, IP, роль пользователя
  • Изменения в конфигурации системы

Требования к логам

  • Неизменяемость (append-only, запись в WORM-хранилище)
  • Шифрование логов
  • Доступ к логам только у compliance-отдела
  • Хранение минимум 1 год (по требованиям HIPAA/GDPR)

Пример структуры лога

{
  "timestamp": "2024-03-15T10:30:00Z",
  "user_id": "a1b2c3",
  "role": "doctor",
  "query_hash": "sha256(masked_query)",
  "retrieved_docs": ["doc_123", "doc_456"],
  "response_hash": "sha256(masked_response)",
  "action": "query"
}

6. Fine-tuning на синтетических или обезличенных данных

Если нужно дообучить модель под специфику домена (медицинские термины, финансовые формулы), нельзя использовать реальные конфиденциальные данные.

Подходы

  1. Синтетические данные: генерация реалистичных, но вымышленных примеров с помощью самой LLM (self-instruct)
  2. Обезличенные данные: удаление PII из реальных данных, затем fine-tuning
  3. Дифференциальная приватность (Differential Privacy): добавление шума в градиенты, чтобы модель не запомнила конкретные записи

Пример генерации синтетических данных

prompt = """
Сгенерируй 5 примеров медицинских записей о пациентах с гипертонией.
Все имена, даты и номера должны быть вымышленными.
Формат: JSON с полями symptoms, diagnosis, treatment.
"""
# Используем LLM для генерации
synthetic_data = llm.generate(prompt)

Важно даже синтетические данные могут случайно напоминать реальные — проверяйте через NER перед использованием.

7. Разграничение доступа (RBAC)

Пользователь должен видеть только те документы, к которым у него есть доступ. Это критично в медицине (врач видит только своих пациентов) и финансах (менеджер видит только свои сделки).

Реализация

  1. Метаданные документов: каждый документ помечается тегами (отдел, пациент, уровень доступа)
  2. Фильтрация на этапе retrieval: перед поиском добавляется фильтр по правам пользователя
  3. Post-retrieval фильтрация: если фильтр встроен в векторную БД (Qdrant, Weaviate поддерживают)

Пример фильтрации в Qdrant

from qdrant_client import QdrantClient

client = QdrantClient(url="localhost")

# Документ помечен тегом department: cardiology
# Пользователь имеет доступ только к кардиологии
results = client.search(
    collection_name="medical_docs",
    query_vector=query_embedding,
    query_filter=models.Filter(
        must=[
            models.FieldCondition(
                key="department",
                match=models.MatchValue(value="cardiology")
            )
        ]
    )
)

Важно фильтрация должна быть на стороне сервера, а не клиента — иначе пользователь может обойти ограничения.

8. Дополнительные меры безопасности

Rate limiting ограничение числа запросов от одного пользователя (предотвращение извлечения данных через множество запросов).

Prompt injection защита проверка, что пользователь не пытается заставить LLM выдать конфиденциальные данные через манипуляции с промптом.

Валидация ответов перед отправкой пользователю проверять, что ответ не содержит PII (если маскировка не сработала).

Резервное копирование шифрованные бэкапы с контролем доступа.

Пет-проект для закрепления

Задача Спроектировать и реализовать прототип RAG-системы для медицинской клиники, где врачи могут задавать вопросы по историям болезней, но видят только своих пациентов.

Инструменты

Шаги:

  1. Развернуть Qdrant и Ollama локально (Docker Compose)
  2. Написать скрипт для загрузки документов с маскировкой PII через Presidio
  3. Реализовать API с аутентификацией через Keycloak
  4. При поиске добавлять фильтр по department пользователя
  5. Логировать все запросы в файл с WORM-доступом
  6. Написать тесты: попытка запроса от пользователя без прав должна возвращать пустой результат

Ожидаемый результат

  • Работающий локальный сервис, где врач-кардиолог видит только кардиологические записи
  • Логи всех запросов в защищённом хранилище
  • Документы в БД без PII (имена заменены на плейсхолдеры)
  • Демонстрация, что self-hosted работает без интернета

Связь с другими вопросами

ВопросТема
5Оценка качества retrieval в RAG
7Уменьшение latency RAG-системы
9Обновление документов в RAG
10Self-RAG и его использование
84Обработка запросов без ответа в документах
85Мультимодальные RAG-системы

Навигация