
MedStore, прототип интерфейса запросов к медицинскому хранилищу данных. Он сделан намеренно раньше самого хранилища: сначала договориться, как люди будут доставать данные, и только потом строить инфраструктуру.
Почему так
Медицинские хранилища чаще всего проваливаются не на инженерии, а на использовании: данные собраны, витрины построены, а врач или аналитик не может получить нужную выборку без разработчика. Прототип интерфейса стоит несопоставимо дешевле, чем переделка готового хранилища под реальные сценарии.
Что показывает прототип
- конструктор запросов и выборок без написания SQL;
- формирование когорт пациентов по набору критериев;
- предпросмотр результата до запуска тяжёлого запроса;
- ролевую логику доступа к чувствительным полям.


Проблема номер один: агрегация данных из разных источников
В клинике данные о пациенте никогда не лежат в одном месте. Структурированные поля и шаблоны осмотров живут в МИС. Выписки, протоколы операций и эпикризы хранятся как HTML или бинарные документы. Лаборатория отдаёт PDF. Пациент приносит сканы из других клиник. За десять лет это десятки миллионов документов и несколько тысяч новых каждый день.
Собрать это в одно хранилище значит решить несколько задач, которые обычно недооценивают:
- Сопоставление пациента. Один человек записан в разных системах с опечатками в фамилии, старым телефоном, без отчества. Сопоставление (MPI, master patient index) делается по детерминированным и вероятностным правилам и обязательно до деперсонализации, внутри доверенной зоны: после неё сравнивать уже нечего.
- Единые справочники. Диагнозы приводятся к МКБ-10, анализы к LOINC, препараты к ATC, единицы измерения к одной системе. Без этого запрос «UACR от 200 мг/г» молча пропустит половину пациентов, у которых показатель записан в мг/ммоль.
- Версии шаблонов и документов. Шаблоны осмотров меняются годами, документы исправляются задним числом. Хранилище должно помнить версию и не считать исправление новым случаем.
- Текст из PDF и сканов. OCR и разметка разделов документа («анамнез», «заключение», «рекомендации») нужны до поиска, иначе совпадение в анамнезе будет выглядеть как диагноз.
- Происхождение каждого факта. Любая строка выборки ссылается на документ-источник. Врач не поверит когорте, которую нельзя проверить глазами.
Анонимизация и деперсонализация
Ключевой принцип архитектуры: хранилище, по которому ищут, вообще не содержит персональных данных. Имя пациента не нужно ни для когорты, ни для статистики, ни для семантического поиска. Оно нужно только лечащему врачу в конкретный момент, и этот момент должен быть отдельным, контролируемым действием.
Термины часто путают, поэтому коротко. Анонимизация необратима: из данных нельзя восстановить человека никаким способом. Псевдонимизация обратима при наличии ключа: вместо ФИО стоит псевдоним, а таблица соответствия хранится отдельно. С точки зрения GDPR и украинского закона о защите персональных данных псевдонимизированные данные остаются персональными, поэтому ключ охраняется как самый ценный актив системы. Деперсонализация здесь общий термин для всего конвейера.
Прямые идентификаторы
ФИО, телефон, адрес, e-mail, номер карты, РНОКПП, номера документов удаляются из структурированных полей и заменяются псевдонимом вида MS-452184. Ключ соответствия живёт в отдельном сервисе с отдельными учётными записями и не реплицируется в хранилище. Ориентир по перечню полей: 18 идентификаторов метода Safe Harbor из HIPAA, дополненные украинскими реалиями.
Свободный текст
Самое опасное место. Врач пишет в выписке «со слов жены, Марии Петровны» или «направлен из больницы в Мукачево», и никакое удаление полей это не поймает. Текст проходит через NER-модель, словари (имена сотрудников, названия населённых пунктов) и регулярные выражения для телефонов и номеров документов. Найденное заменяется типизированными метками вроде [ИМЯ] или [НАСЕЛЁННЫЙ_ПУНКТ]. Качество очистки измеряется на размеченной выборке, и остаточный риск учитывается в правах доступа к полному тексту.
Косвенные идентификаторы и k-анонимность
Даже без имени человека можно узнать по сочетанию признаков: возраст, пол, район и редкий диагноз. Поэтому возраст обобщается до групп, даты сдвигаются на постоянное для каждого пациента смещение (интервалы между событиями сохраняются, календарные даты нет), редкие значения укрупняются. Выгрузки проверяются на k-анонимность: каждое сочетание косвенных признаков должно встречаться минимум у k пациентов. В агрегатах маленькие ячейки (например, меньше пяти человек) скрываются.
Уровни доступа и аудит
- Аналитик видит агрегаты и псевдонимизированные выборки, может выгружать их в утверждённых форматах.
- Врач-исследователь работает с когортами и документами, но выгрузка ему закрыта по политике.
- Reverse lookup, то есть переход от псевдонима к карте пациента в МИС, доступен только ролям с правом идентификации, с указанием причины.
- Журнал аудита фиксирует, кто, когда, какой запрос выполнил, сколько записей получил и какие документы открыл. Журнал только дописывается, правок в нём нет.

Поиск по неструктурированным данным через локальный RAG
Самые ценные медицинские факты записаны не в полях, а в тексте: «узел 10 мм в S6 правого лёгкого», «стент в проксимальном сегменте», «толщина ткани 0.42 см». Полнотекстовый поиск находит слова, но не смысл: он не знает, что «новообразование» и «узел» могут означать одно и то же. Для таких вопросов нужен семантический поиск и языковая модель, которая соберёт ответ из найденных фрагментов. Это и есть RAG, retrieval-augmented generation.
Очевидное решение, отправить выписки в облачную LLM, для медицинских данных не подходит. Текст покидает периметр клиники, попадает в логи чужого провайдера, и никакой договор не вернёт его обратно. Поэтому весь RAG-контур работает локально.
Как устроен конвейер
- Индексируется только уже деперсонализированный текст. Документы режутся на фрагменты по разделам, у каждого фрагмента есть document_id, тип документа, дата и псевдоним пациента. Имён в индексе нет изначально, а не «фильтруются потом».
- Эмбеддинги считает локальная модель на собственном GPU-сервере клиники. Подходят открытые многоязычные модели, которые понимают украинский и медицинскую лексику. Эмбеддинги тоже считаются чувствительными: по ним можно частично восстановить исходный текст, поэтому индекс не выходит за периметр.
- Векторный индекс (например, pgvector, Qdrant или OpenSearch) хранит фрагменты с метаданными. Поиск гибридный: векторная близость плюс полнотекстовые совпадения, чтобы не терять точные числа и коды. Фильтры по ролям применяются на этапе выборки, до того как фрагменты увидит модель.
- Локальная LLM с открытыми весами работает на своём сервере инференса без выхода в интернет. Она отвечает только по найденным фрагментам и ссылается на документы-источники. Если в найденном нет ответа, модель так и говорит.
Контроль утечек в ответах
- Сетевая политика запрещает исходящие соединения из RAG-контура, это проверяется технически, а не на честном слове.
- Ответ модели проходит через тот же детектор персональных данных, что и документы. Если в ответе всплыло имя или номер, ответ блокируется и событие уходит в аудит.
- Текст документов считается данными, а не инструкциями: защита от prompt injection, спрятанной в тексте выписки.
- Каждый RAG-запрос журналируется: кто спросил, что спросил, какие фрагменты были подняты и что ушло пользователю.
- Промпты и ответы не отправляются во внешние сервисы телеметрии и не используются для обучения чужих моделей.

Архитектура целевой системы
Схема ниже показывает путь данных от источников до экрана пользователя. Всё, что внутри пунктирной рамки, работает в контуре клиники. Деперсонализация стоит до хранилища, поэтому ни витрины, ни полнотекстовый индекс, ни RAG никогда не видят прямых идентификаторов. Обратно к личности пациента ведёт единственная дверь, reverse lookup, и она под аудитом.
Что уже есть в прототипе, а что относится к целевой системе
Прототип работает на синтетических данных, и мы честно разделяем сделанное и спроектированное.
Реализовано в прототипе:
- вход под тремя демо-ролями: медицинский аналитик, врач-исследователь, администратор доступа;
- четыре типа запросов: когорта, сводка по пациенту, семантический поиск по тексту документов, агрегированная аналитика с группировкой по возрастным группам для Power BI;
- псевдонимы пациентов MS-id и отдельный идентификатор для reverse lookup вместо имён в выдаче;
- таблица результатов с фильтрами и страницами, найденные фрагменты текста с подсветкой и уверенностью, ссылки на документы-источники, график;
- запрет выгрузки для роли врача-исследователя с записью события в журнал аудита;
- распознавание запросов без медицинских критериев: система просит уточнить и не запускает поиск;
- нормализация похожих формулировок в один запрос, чтобы одинаковые вопросы давали одинаковый ответ;
- контракт будущего API: в ответе описан план выполнения, например гибридный поиск по текстовому индексу и эмбеддингам.
Спроектировано для целевой системы: коннекторы к МИС и архивам, OCR, нормализация справочников и MPI, сервис псевдонимизации с отдельным ключом, NER-очистка текста, проверка k-анонимности выгрузок, локальные эмбеддинги, векторный индекс, локальная LLM с фильтром ответов, серверный неизменяемый журнал аудита. Необязательный AI-режим прототипа генерирует демо-ответы только по синтетическим данным; в целевой системе этот шаг выполняет исключительно локальная модель.
Важное ограничение
Это прототип согласования UX. Он не подключён к реальным медицинским данным и не содержит персональной информации пациентов: на нём проверяют логику работы, а не лечат.
Следующий шаг
После согласования интерфейса определяется модель данных, слой доступа, аудит обращений к чувствительным полям и правила деперсонализации выгрузок. Затем пилот на ограниченном срезе данных: один источник, одна группа документов, замер качества очистки текста и точности семантического поиска на размеченной выборке.
Схема данных
Из чего состоит база проекта и как связаны сущности.
Контур системы
Из каких частей собран проект.
Схема интерфейса
Как устроен рабочий экран.
Где посмотреть
Публичной ссылки у проекта нет: прототип развёрнут на закрытом адресе с авторизацией и наружу не выкладывается. Показываем его на демонстрации, по записи.