Контекст
Блок «Main now» в Health Report (Phase 3 Highlights Insight) не может показать 20-50 абнормальных биомаркеров — это убивает смысл «main». Из всего набора отклонений нужно выбрать то подмножество, на которое опытный врач посмотрел бы первым. Решение должно быть воспроизводимым, прослеживаемым, дешёвым (без LLM в момент принятия решения) и калибруемым через врача-в-цикле.
Решение фиксирует какие сигналы участвуют, как они комбинируются, что значат исходы и какие альтернативы отвергнуты. Алгоритм закодирован в отдельном GitHub-репозитории main-biomarkers-detection (Артур, 2026-05-11 v0.1 1).
Решение не про диагностику и не про степень тяжести. Это про приоритет показа — кому показывать первым в My Health / Health Report.
Что нужно от ответа на выходе:
- Бинарная метка на каждый биомаркер:
mainлибоnot_main, без третьей корзины. - Детерминированный источник срабатывания: либо граф (biomarker-graph), либо patient FHIR через Ретривер, либо предыдущие измерения. Никаких порогов «из головы», кроме одного откалиброванного параметра.
- Аудит-след на каждую классификацию: какой сигнал сработал, цитаты (обоснование / quotes / source из графа), версии prompt’a / правил / графа.
- Один и тот же вердикт для пациента и врача: распределение по аудиториям — работа downstream-writer’ов, не sieve.
- Полностью воспроизводимый результат для одного и того же входа и версий: без LLM в момент принятия решения, без случайной выборки.
Рассматривали
Восемь сигналов-кандидатов: три из них вошли в выбранную комбинацию через OR; пять отвергнуты как самостоятельные сигналы.
S1 — фенотип-кластер (вошёл в комбинацию)
Биомаркер abnormal И ≥ N = 2 его relatedParameters-сиблингов (из графа, предварительно резолвнутых Ретривером) тоже abnormal в bundle’е. Мысленная модель — «синдром не пара, а кластер»: три сходящихся находки начинают рассказывать историю; два — низший защищаемый минимум, прежде чем шум забивает сигнал.
Плюсы:
- Захватывает синдромную структуру: даже без контекста пациента видно, что отклонения сходятся на одной системе.
- Источник цитат — граф; на каждый сработавший сигнал есть
quotes/sourceдля аудита. - Калибровка через одну рукоятку (
N).
Минусы:
- Зависит от полноты
relatedParametersв графе; редкий или необычно сгруппированный синдром может промахнуться. N = 2стартовая гипотеза, не доказанная — нужна валидация через цикл с врачом на ~50 doctor-reviewed отчётов.
S2 — patient-context match (вошёл в комбинацию)
Биомаркер abnormal И Ретривер вернул ≥ 1 typed match против patient FHIR — реальный диагноз/состояние из matchedConditions либо лекарство из matchedMedications. Чистый поиск, без LLM. Каждое совпадение несёт quotes + source из графа через Ретривера.
Плюсы:
- Учитывает индивидуальный контекст: отклонение, связанное с конкретным активным диагнозом или лекарством пациента, выходит вперёд.
- Аудит-след прозрачен — typed match даёт citations.
- Дёшево, детерминистично, граф уже несёт связи.
Минусы:
- Зависит от полноты
relatedConditions/relatedMedications. - Не различает «condition в активной фазе» и «condition в анамнезе» — это делает FHIR на стороне Ретривера, а не sieve.
S3 — сигнал тренда (вошёл в комбинацию)
Биомаркер abnormal И один из паттернов по предыдущим измерениям: первый раз abnormal / усугубление / разворот направления. Стабильно хроническое и улучшающееся не продвигают (пациент и врач знают; уже под контролем).
Плюсы:
- Захватывает новизну и развитие: первый раз abnormal или усугубление — это сигнал, который кластер и контекст пропустят.
- Сравнение между лабами через структурно-позиционную нормализацию, см. biomarker-observations-comparability.
Минусы:
- Требует подачи предыдущих измерений, которые Phase 1 health-report-pipeline сейчас отбрасывает (open).
- Веса паттернов (какой из трёх privilege’нее) — эмпирический вопрос для цикла с врачом.
Q1 — список панических значений / критических порогов (отвергнуто)
Ручной список «критических» значений на каждый биомаркер — если последнее измерение за порогом, биомаркер автоматически main.
Плюсы:
- Определённость по абсолютным порогам.
- Интуитивно безопасный.
Минусы:
- Бремя сопровождения — пороги дрейфуют по гайдлайнам, нужен врач-в-цикле на каждое обновление.
- Дублирует интерпретацию, которую уже даёт refRange.
- Не учитывает контекст пациента — пациент с компенсированным хроническим состоянием получает ложный «main» при каждом тесте.
Почему отвергнуто: сопровождение не масштабируется без постоянного ритма работы с врачом; граф биомаркеров уже несёт ту же информацию через related_conditions, обновляется централизованно. SPEC § 3 фиксирует отказ 2026-05-12; docs/doctors/q1_panic_values.md остался замороженным артефактом, не потребляется.
Q2 — бинарная привязка к условию (заменено на S2)
«Если в FHIR пациента есть любое из условий, связанных с биомаркером в графе → main».
Плюсы:
- Дёшево, детерминистично.
- Граф уже несёт связи.
Минусы:
- Теряет аудит-след на каждое совпадение:
quotes/source/rationaleиз графа не доходят до выхода. - Не различает «condition в активной фазе» и «condition в анамнезе».
Почему отвергнуто: строго перекрывается S2 — та же бинарная проверка плюс метка типа совпадения (condition / medication / age-shifted / pregnancy-shifted) и цитаты в trace. Никаких регрессий, больше функциональности.
Q3 — объяснимость через контекст лекарств / диагнозов (отвергнуто)
«Если abnormal-биомаркер можно объяснить активным лекарством или диагнозом → понизить приоритет (не main)».
Плюсы:
- Интуитивно — «у пациента низкий калий, но он на диуретике, это известный механизм, не критично».
Минусы:
- Это диагностика. Чтобы честно решить «объяснимо или нет», нужно весить вклад каждой активной причины, оценивать компенсацию, ранжировать причины.
- Работа врача с историей, не функция-поиск.
Почему отвергнуто: разрастание области в диагностику. Удалено 2026-05-08 явно в SPEC § 3. Доктор делает интерпретацию вниз по потоку — наш модуль ему говорит «вот биомаркер X, и у пациента есть причина Y, которая связана с ним по графу» (это S2). Врач сам решает, объяснён ли он.
Reflex / follow-up логика (отвергнуто)
«Биомаркер, требующий дополнительного теста для интерпретации → main, потому что заслуживает внимания».
Плюсы:
- Ловит «ну, тут надо пересдать / уточнить».
Минусы:
- Биомаркер, требующий доп-теста, менее информативен на одном замере, не более.
- «Main now» — про текущее состояние; «надо пересдать» — про следующий цикл измерений.
Почему отвергнуто: воспроизводит проблему списка панических значений — список нужно поддерживать отдельно от графа. И смысл другой: reflex-флаг — это «уверенность в этом значении ниже», а main — «внимание выше». Reflex нужен, но это отдельный workstream, не сигнал для main.
Числовые шкалы тяжести (отвергнуто)
«Назначим биомаркеру числовой балл (severity / urgency / clinical_weight), top-K = main».
Плюсы:
- Непрерывная шкала, удобно для ранжирования.
- Можно сразу тюнить.
Минусы:
- Субъективность спрятана в весах, нечитаема для читателя аудит-логов.
- Без калибровки врачом веса — шум.
- Порог для top-K произволен и зависит от того, сколько биомаркеров было abnormal.
Почему отвергнуто: бинарный main / not_main плюс аудит-след даёт тот же приоритет вниз по потоку без ложной объективности. Если когда-нибудь понадобится ранжирование внутри main (urgent vs routine) — это работа downstream-writer’a.
LOINC-panel pattern matching как основной сигнал (отвергнуто)
«Если abnormal-биомаркеры группируются в известный panel (lipid panel, basic metabolic panel) → весь panel = main».
Плюсы:
- На стандартах, LOINC уже поддерживает panel definitions.
Минусы:
- Panel-определения часто не совпадают с тем, как реальный лаб режет тесты.
- Нестандартные panel’и от регионального лаба не матчатся.
- Биомаркеры внутри одного panel могут иметь совершенно разные основания для main (Hb низкий vs MCV высокий — разные паттерны, в одном CBC panel’е).
Почему отвергнуто: S1 (фенотип-кластер через relatedParameters из графа) делает то же самое, но точнее — сиблинги определяются клинически, не лабораторной группировкой. Panel definitions могут быть подсказкой для нормализации, не сигналом для main.
Третья корзина — «Possible error / Pay attention» (отвергнуто)
«Добавить третью метку для unit / preanalytic / sanity-check fails».
Плюсы:
- Даёт UI явное место для «не уверены».
Минусы:
- Засоряет пространство меток.
- Смешивает «биомаркер не main» (медицинский вердикт) и «не можем классифицировать» (проблема качества данных).
Почему отвергнуто: sanity-fails эмитим как not_main + явный reason_code в trace (sanity:missing-ref-range / sanity:latest-normal). Аудит-лог видит проблему качества данных отдельно от медицинского решения; пользовательская метка остаётся бинарной.
Выбрали — комбинация S1 + S2 + S3 через OR поверх двух sanity-проверок
Биомаркер main если:
- Последнее измерение abnormal (не sanity:latest-normal), И refRange присутствует (не sanity:missing-ref-range), И
- Сработал хотя бы один из трёх сигналов: S1 (фенотип-кластер), S2 (patient-context match), S3 (тренд).
OR-комбинация: код причины в trace фиксирует какой именно сигнал сработал (один или несколько); цитаты из графа протаскиваются. N = 2 для S1 — единственный калибруемый параметр.
За
- Три независимых основания для «main». Кластер (синдром не пара), контекст (реальная активная причина в картине пациента), тренд (новизна или развитие). AND-комбинация over-filter’ит случаи, где врач реально посмотрел бы; OR — слабее, но это правильная слабость.
- Все границы — refRange или цитаты из графа. Никаких порогов «из головы», кроме калибруемой
N=2. - Sanity отдельно от вердикта. Проблемы качества данных уходят в
reason_codeв trace, не в отдельную метку. Пространство меток не засоряется data-quality-сигналами. - Без LLM в момент принятия решения. Sieve поверх retrieved-блока — чистая функция; LLM остаётся в upstream-агентах (Диагностик / Ретривер), которые готовят retrieved.
Следствия
- Подача предыдущих измерений для S3. health-report-pipeline Phase 1 сейчас делает
latest-per-biomarkerсвёртку и отбрасывает предыдущие. Для рабочего S3 нужно либо подгружать их параллельно, либо переделать свёртку — open-вопрос интеграции, см. main-biomarkers-detection § Открытые вопросы. - Сравнимость предыдущих измерений между лабами. Структурно-позиционная нормализация
(value − upper_ref) / (upper_ref − lower_ref)— отдельная decision-страница biomarker-observations-comparability. - Ритм калибровки привязан к врачу-в-цикле.
N=2ждёт ~50 doctor-reviewed отчётов от Кати через общий рабочий цикл. До этого fixture-driven (38 synthetic patient bundles в репозитории Артура). - Каталог sanity reason codes — отдельный. Помимо
sanity:missing-ref-range/sanity:latest-normalсо временем понадобятся unit / preanalytic / unknown-biomarker reason codes. Каталог пока не финализирован (SPEC § 6 open-вопрос). - Конвертер Retriever → classifier input на границе. Наш Ретривер выдаёт
relatedFound*/relatedMissing*на каждый биомаркер; classifier ждётretrievedBlockSchemaсо своими именами полей. Конвертер в Phase 1 — open-вопрос реализации, см. main-biomarkers-detection. - Один и тот же вердикт для пациента и врача — не пересматриваем. Распределение по аудиториям идёт вниз по потоку, в writer’ах health-facts-as-generation-substrate.
Открытые вопросы
- Калибровка
N=2. Стартовая гипотеза. Валидация на ~50 doctor-reviewed отчётов через цикл с врачом; ждёт полноты графа. Если over-flags → 3; если under-flags → 1. - Веса паттернов S3. Какие паттерны (первый раз abnormal / усугубление / разворот) врачи реально выделяют на практике — эмпирический вопрос для рабочего цикла с врачом, не SPEC.
- Граничные случаи формулы сравнимости между лабами. Одностороннее refRange (только upper для troponin), pediatric (детские) диапазоны, pregnancy-shifted — biomarker-observations-comparability § Open.
- Каталог sanity reason codes — unit / preanalytic / unknown-biomarker reason codes не финализированы.
- Pediatric / pregnancy охват. v0.1 adult-only. Расширение — отдельное развёртывание, требует pediatric диапазонов в графе и pediatric-specific S2-записей.
Связано
- main-biomarkers-detection — concept-страница модуля: контракт
classify(), поток управления, что НЕ делает, состояние интеграции в Health Report Pipeline - biomarker-observations-comparability — почему предыдущие измерения сравниваются через
(value − upper) / (upper − lower), а не через конвертацию единиц; альтернативы и компромиссы - health-report-pipeline — Phase 3 Highlights Insight (куда подаётся
main-подмножество) и Phase 1 свёртка (откуда уходят предыдущие измерения) - medical-expert-loop — общий механизм работы с врачом, через который идёт калибровка
Nи весов паттернов S3 - Ретривер — источник retrieved-блока на каждое последнее измерение
- biomarker-graph — single source of truth для
relatedParameters/relatedConditions/relatedMedications - Диагностик — собирает diagnostic plan; верх по цепочке от Ретривера
- biomarker-actuality-service — родственный детерминистический-поверх-LLM-фактов сервис; другой вопрос («можно ли опираться на это значение сейчас» vs «main-биомаркер для Highlights»)
- health-facts-as-generation-substrate — Variant D Reasoner/Writer split; вниз по потоку от sieve, audience-specific формулировки
- health-report-vocabulary — словарь Snapshot / Insights / Biomarker Analysis
Сноски
-
SPEC v0.1 (draft 2026-05-11),
docs/SPEC.md, accessed 2026-05-19. https://github.com/Realai-plus/main-biomarkers-detection/blob/main/docs/SPEC.md ↩