Шлях лікаря-розробника від небезпечного рецепту до продакшн медичного інструменту.

Кожен клініцист знає цей момент. Переглядаєш список медикаментів пацієнта — шість, сім, іноді дванадцять препаратів — і відчуваєш тихий неспокій: чи безпечно їх поєднувати?
Я відчув це одного дня, дивлячись на пацієнта, який приймав варфарин, аспірин і новий НПЗП, призначений колегою. Три препарати. Три різних механізми дії. Одна потенційно небезпечна для життя комбінація.
Проблема, яку жоден застосунок не вирішував
Помилки при призначенні медикаментів є однією з провідних причин запобіжної шкоди в охороні здоров’я. Взаємодії між препаратами, протипоказання, перехресні алергії — це не рідкісні крайні випадки. Це повсякденна клінічна реальність, особливо серед літніх пацієнтів із кількома хронічними захворюваннями.
Існуючі інструменти мали два типи провалів. Або вони були розроблені для фармацевтів і переповнені клінічною термінологією, яку пацієнти не могли зрозуміти, або були зручними для споживача, але настільки поверхневими, що майже не мали реальної клінічної цінності. Нічого не знаходилось посередині — доступного, чесного і справді корисного.
Тому я і побудував цю золоту середину.
Що MedCheck насправді робить
MedCheck — це веб-застосунок для перевірки сумісності препаратів із багаторівневим аналітичним рушієм:
Рівень 1: Пошук у структурованій базі даних. Ми проіндексували понад 2 мільйони пар взаємодій між препаратами з фармакологічних баз даних. Коли користувач вводить комбінацію, спочатку виконується точний пошук — швидкий, детермінований і надійний.
Рівень 2: Векторний RAG (Retrieval-Augmented Generation). Для взаємодій, які не охоплені структурованими даними, ми використовуємо семантичний пошук по 27 000+ текстових фрагментах інструкцій до препаратів та клінічних настанов. Ембеддинги, згенеровані моделлю paraphrase-multilingual-mpnet-base-v2, дозволяють знаходити релевантний контекст навіть при варіативності термінології.
Рівень 3: Синтез за допомогою LLM. Модель llama-3.3-70b-versatile від Groq отримує весь знайдений контекст і генерує відповідь, адаптовану до ролі користувача — зрозумілою мовою для пацієнтів, з клінічними деталями для лікарів. Різні ролі, різні промпти, одні й ті самі дані.
Окрім взаємодій між препаратами, MedCheck також перевіряє:
- Перехресні алергії (117 відомих пар алерген-препарат із перехресними реакціями)
- Протипоказання на основі діагнозів МКБ-10
- Вікові та дозові обмеження
Архітектура, яка забезпечила результат
Стек навмисно простий — і це перевага, а не недолік.
Бекенд: FastAPI на Python 3.11. Швидкий, типізований, асинхронний. Індекс взаємодій між препаратами (134 МБ пар) завантажується в оперативну пам’ять при старті, тому кожен пошук займає менше мілісекунди.
База даних: Supabase (PostgreSQL + pgvector). Використовуємо pgvector для семантичного пошуку і стандартні реляційні запити для всього іншого. Одна платформа, дві парадигми.
Нормалізація назв препаратів: Виявилась складнішою, ніж очікувалось. Українські та російські торгові назви не відповідають однозначно міжнародним INN-назвам. Ми побудували власну функцію нечіткого пошуку в PostgreSQL, яка надає пріоритет точному збігу префіксів над показниками схожості — невелика зміна, що драматично покращила якість ідентифікації препаратів.
Деплой: Два Docker-контейнери за nginx на VPS. Бекенд на порту 8001, фронтенд на 3001. Просто, відтворювано, легко в обслуговуванні.
Продуктове рішення, яке змінило все
Ранні версії мали один інтерфейс. Одну мову. Одну роль.
Користувачі одразу почали скаржитись. 60-річний пацієнт у Києві та лікарняний фармацевт у Львові мають принципово різні потреби від одного й того самого запиту. Фармацевт хоче знати механізм взаємодії, ступінь серйозності, альтернативні варіанти. Пацієнт хоче знати: чи варто хвилюватись, і що сказати лікарю?
Ми вирішили це за допомогою рольово-орієнтованих промптів. Та сама пара препаратів, ті самі вихідні дані — але LLM отримує різні інструкції залежно від того, чи визначив себе користувач як пацієнт або як клініцист. Різниця у якості відповідей виявилась разючою.
Ми також додали три мови — українську, російську, англійську — оскільки наша цільова аудиторія охоплює регіон, де мова не є однорідним припущенням.
Вбудовування медичного інструменту в медичний сайт
Після того як MedCheck запрацював на власному домені, логічним наступним кроком стала інтеграція. Ми вбудували віджет у наявний сайт неврологічної клініки через чистий API-підхід — без iframe, без редиректів. Лише кастомний HTML/JS-віджет, що звертається напряму до наших FastAPI-ендпоінтів.
Технічна складність виявилась несподіваною: WordPress агресивно видаляє вбудовані теги <script> з кастомних HTML-блоків. Рішенням став WPCode — плагін, який вставляє JavaScript через підвал сторінки, поза фільтрами контенту WordPress.
Ми також додали форму згоди — модальний оверлей при першому відвідуванні, який вимагає від користувача підтвердити, що MedCheck є інформаційним інструментом, а не замінником медичної консультації. Це не лише юридична необхідність. Це правильно з етичної точки зору.
Що показали дані після запуску
Після логування кожного API-запиту — email, IP, джерело, запитувані препарати, статус відповіді, затримка — закономірності проявились швидко.
Найпоширенішими запитами були серцево-судинні комбінації: антикоагулянти з антиагрегантами, статини з антиаритміками. Це також комбінації з найвищими клінічними ризиками. Користувачі не ставились до MedCheck як до цікавинки. Вони використовували його для реальних рішень.
Середній час відповіді — близько 10 секунд від початку до кінця, переважно через виклик LLM. Для інструменту, де точність важливіша за швидкість, це прийнятно. Користувачі не очікують миттєвих результатів, коли питання звучить як «чи можна безпечно приймати ці препарати разом?»
Що я зробив би інакше
Починати з нормалізації. Проблема зіставлення назв препаратів забрала більше часу, ніж будь-який інший компонент. Нечіткий пошук, багатомовна еквівалентність, зіставлення торгових назв з INN — саме тут медичні AI-проєкти зупиняються. Будуйте це правильно з самого початку.
Розділяти відповідальності раніше. Початковий agent.py виріс і взяв на себе забагато відповідальності — нормалізація, пошук взаємодій, RAG, промптинг LLM, форматування відповіді. Розподіл на окремі сервіси раніше прискорив би ітерації.
OTP-аутентифікація була правильним рішенням. Жодних паролів для керування, жодних потоків відновлення паролю, мінімальний бар’єр входу. Для медичного інструменту, де довіра має значення, верифікація через email відчувається відповідально серйозною без зайвого навантаження.
Ширший контекст
MedCheck не намагається замінити клінічне судження. Це не мета і не повинно нею бути.
Мета — дати людям — пацієнтам, які переглядають список своїх ліків перед прийомом, лікарям, що роблять швидку повторну перевірку, медсестрам, що оновлюють виписні інструкції — швидку, чесну, багатомовну відповідь на питання, яке раніше вимагало або клінічного фармаколога, або обґрунтованого здогаду.
У нашій базі 9 500 препаратів. Понад 2 мільйони проіндексованих пар взаємодій. Виявлення перехресних алергій. Картування протипоказань за МКБ-10. Багатомовні відповіді LLM. OTP-аутентифікація. Адмін-дашборд для моніторингу використання та архівування даних.
Все це працює на одному VPS із 8 ГБ оперативної пам’яті.
Поріг для створення реальних медичних інструментів нижчий, ніж думає більшість розробників. Поріг для створення надійних — вищий, ніж більшість усвідомлює.
Саме в цьому розриві відбувається справжня робота.
MedCheck доступний на pharm.esemi.org та вбудований на neurolikar.com.ua/medcheck. Проєкт активно розвивається.
