Status: active direction. Конвергенция трёх векторов к snapshot-модели:

  • Со стороны UX (unified-upload-flow): единый drop-zone + генерация только последнего отчёта = Уровень 1–2 vision’а на практике.
  • Со стороны generation (biomarker-analysis-pipeline): Diagnostic + Retriever + Generator обращается к patient FHIR record через generic FHIR-tools, а не к конкретному BloodTest. Pipeline уже patient-centric.
  • Со стороны продукта — Вася на heads sync 2026-04-27 формализовал «Health Status» как финальную страницу пациента: aggregate всей истории, обновляется при добавлении новых файлов, сохраняется история изменений для diff-навигации. Продуктовая поверхность для этого vision’а — my-health.

Парадигмальный переход охватывает общий engine распознавания — он стоит и за fhir-gravity (B2B-бренд), и за B2C BloodGPT, поэтому не разделение «legacy здесь, новое там», а единая эволюция. Текущая (тест-центричная) реализация описана в fhir-modeling-ai-content — Variant C.

Gap между vision и pipeline: vision предлагает 3-resource pattern (ClinicalImpression + Composition + CarePlan) для хранения output, но generation-pipeline на Apr 23 не имеет зафиксированного решения куда писать (Артур: «А не было кстати сходу идей куда в fhir все это сохранять? Не думал»). Это open вопрос на пересечении двух страниц.

Контекст и идея

Сейчас вся медицинская работа BloodGPT привязана к тесту: загрузил PDF/HL7/JSON → отдельная сущность BloodTest → AI-overview прикрепляется к этому тесту. В session 065369a0 (Mar 14-19 2026) Ильдар сформулировал альтернативную рамку: то что мы генерируем — это не “комментарий к тесту”, а point-in-time snapshot здоровья пациента.

Триггер snapshot’а может быть любым:

  • Новый тест (как сейчас)
  • Новый диагноз добавлен
  • Изменилось лекарство
  • Прошло время — “пора пересмотреть”
  • За неделю было 3 теста — общий snapshot

Snapshot привязан к пациенту + дате, а не к тесту. Тест становится частным случаем триггера.

Ильдар (065369a0, 12:27): «получается, что это описание здоровья человека, хотя он привязан к тесту сейчас. я представляю себе будущее, в котором он не будет привязан. он будет привязан скорее к дате к чему-то такому»

Ильдар (цитата коллегам, 14:11): «Если уйти от тест-центричного подхода к документ-центричному или fhir-центричному, тогда тесты станут способом представления данных как угодно полученных. А может и не тесты это будут, а например недели. Вот за эту неделю были такие наблюдения — в том числе 3 теста — вот такие выводы»

В session 34e93701 (Apr 24 2026) этот концепт развит со стороны триггеров: разные события (test upload / questionnaire / diary entry / monthly cron / classifier-detected accumulation) → разные типы generation (full report / digest / suggestion / recommendation). То есть snapshot-vision говорит что генерим (point-in-time view), triggers paradigm — когда и от чего. Возможно это две стороны одного направления.

Carry-over: связь snapshot-vision с use-cases (Lab / Doctor / B2C) — не установлена. Открытый вопрос: snapshot един для всех audiences или разный per audience. Пока не думали.

Carry-over: если разделять generation на Reasoner (truth) + Writer (форма), возникает open question — как хранить в FHIR обе части. Сейчас не думали вообще, на будущее. См. fhir-clinical-impression где patient-voice уже зарезервирован как option для будущего.

Что в FHIR уже поддерживает паттерн

  • Composition.subject = Patient — Composition уже привязан к пациенту, не к DR. Связь с DR опциональна (через section.entry).
  • Composition.event — поле для триггера (“что вызвало этот документ”). Можно положить ссылку на DR / Condition / MedicationStatement / time-event.
  • ClinicalImpression.previous — built-in цепочка assessments во времени. Каждый snapshot ссылается на предыдущий → история доступна как linked list.

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

Три уровня изменений (по сложности)

Структура из обсуждения 065369a0 (17:32):

Уровень 1 — Snapshot отвязан от теста

Минимальная правка. Что нужно:

  • CarePlan.supportingInfo → Composition вместо → DiagnosticReport
  • Composition.event — extension хранит тип триггера ({ trigger: "blood-test", ref: "DiagnosticReport/5" })
  • В Postgres: fhirReportOverviewId / fhirFollowUpId уже абстрагированы от теста (через сделанную миграцию 20260316003527)

Можно сделать “хоть завтра”. Не ломает существующее, только подготавливает архитектуру.

Уровень 2 — Несколько триггеров → один snapshot

“За неделю было 3 теста — общий snapshot.” Что нужно:

  • Новый pipeline — не “тест завершён → snapshot”, а “собрать события за период → snapshot”
  • Scheduler/cron вместо event-driven (или: тест завершён → проверь недавние события → сгенерируй общий snapshot)
  • Composition builder принимает массив триггеров, не один DR
  • LLM-промпты переписать под мульти-источниковый контекст
  • UI — портал и PDF показывают snapshot, а не тест. Навигация: timeline вместо списка тестов

Сложность: месяцы.

Уровень 3 — Новые типы событий (полная экспансия)

Опросы, трекеры (wearables/CGM), ручной ввод. Что нужно:

  • Новые входные pipelines:
    • API для ручного ввода диагнозов / лекарств → Condition / MedicationStatement
    • Wearables (Fitbit, Apple Health, Garmin) → Observation (vital-signs, activity)
    • CGM → Observation (glucose continuous)
    • Опросник → QuestionnaireResponseObservation
  • HealthEvent таблица в Postgres — обобщение BloodTest:
    HealthEvent
    ├── id, type (blood_test | document | survey | device_sync | manual)
    ├── date, patientId, organizationId
    ├── fhirTriggerId (DR, Condition, QuestionnaireResponse, ...)
    ├── fhirHealthSnapshotId → Composition
    └── fhirFollowUpId → CarePlan
    
  • Trigger router — “пришло событие → обнови State → решение: генерировать snapshot или нет”
  • AI промпты переписать под мульти-источниковый контекст

Concrete implementation patterns (уже существуют)

Часть Level 3 строительных блоков уже реализована (sessions Mar-Apr 2026 в worktree feat/mastra-agents):

  • 5 smart FHIR write tools (V0.5): recordSymptom, recordMedication, recordAllergy, recordProcedure, recordFamilyHistory. Дедупликация по name (V0.5) или code (V1, через $expand). Enriched schemas (clinicalStatus, severity, onset, bodySite). См. fhir-meta-tagging + clinical-record-reconciliation.
  • Source attribution через fhir-provenance + meta.tag — ready для multi-source (chat / survey / document-import).
  • Survey/MedicalContext agent (medical-context-survey) — Mastra multi-turn agent для опросника пациента, output JSON MedicalProfile. Bridge LLM step к FHIR-write через 5 tools — V1.5 (планируется).
  • Health-chat agent — Mastra-агент для chat-интерфейса на dashboard, использует те же 5 write tools напрямую.
  • TextToFHIR TS port — анализ Python-сервиса сделан в session 7ff79368, реальный port — отдельная сессия. Будет использовать те же 5 write tools.
  • Inngest single step normalizeAndHarmonizeStep заменил 2 Python-сервиса (нормализация + LOINC), под feature flag USE_LOCAL_NORMALIZATION. См. loinc. Это infrastructure для blood-test branch Level 3 events.

Что осталось до полного Level 3:

  • HealthEvent обобщающая таблица в Postgres (сейчас всё на BloodTest schema)
  • Trigger router orchestration (event-driven snapshot generation)
  • Wearables / CGM integrations (полная новая infrastructure)

3-resource snapshot architecture (вариант реализации)

Из обсуждения с Ильдаром в 065369a0 (21:02), draft модель данных:

HealthSnapshot (или пока BloodTest)
├── id, patientId, date, status
├── fhirAssessmentId        → ClinicalImpression (structured assessment + цепочка previous)
├── fhirPresentationId      → Composition (тексты для пациента + ссылки на данные)
├── fhirFollowUpId          → CarePlan (план)
└── (optional) triggerId    → что вызвало snapshot

Идея: разделить что найдено (ClinicalImpression — structured findings, prognosis) от как это рассказано пациенту (Composition — texts) и от что делать (CarePlan).

Блокер: ClinicalImpression.assessor — только Practitioner/PractitionerRole, не Organization. Verified в session c9560637 ([Н7] в digest). Это значит для AI-author нужен другой ход — либо использовать note[].authorReference для атрибуции, либо ждать R5 (где может расширили), либо смириться с extension. Не решено.

В качестве промежуточного шага в 065369a0 уже сделан Уровень 1.5: добавлены dedicated columns fhirAssessmentId (Composition) и fhirFollowUpId (CarePlan) — naming “Assessment” выбран сознательно, как stepping stone к ClinicalImpression. См. zero-extensions-fhir и fhir-modeling-ai-content Pipeline.

Связано (входные звенья цепочки)

  • patient-summary — описывает retrieval-звено: как собрать FHIR-контекст для AI (relevance scoring, IPS Bundle, $summary operation). Vision описывает что с ним делать дальше (генерировать snapshot). Цепочка:
    Patient Summary (RFC-019) → input для LLM
    Vision: input → snapshot (Composition + ClinicalImpression + CarePlan)
    
  • fhir-modeling-ai-content — текущая (тест-центричная) реализация
  • ai-enrichment-separate-step — pattern “create skeleton → enrich post-create” уже generalizable, лёг бы в snapshot-центрик
  • fhir-meta-tagging — multi-source attribution готов (origin / source / security слои)
  • agent-vs-workflow — applicable для structured-LLM шагов (LOINC, etc); для open-ended conversation (survey, chat) — не подходит, см. agent-pattern
  • fhir-composition — уже поддерживает subject=Patient + event для триггера
  • fhir-provenance — source-tracking enabler
  • medical-context-survey — Mastra-survey agent (BG-1050, реализован 2026-03-25)
  • (TBD entity) domain/fhir-clinical-impression — для structured assessment с previous цепочкой
  • (TBD entity) domain/fhir-questionnaire-response — для опросов
  • (TBD entity) domain/fhir-condition, domain/fhir-medication-statement, domain/fhir-allergy-intolerance — для “live patient state” что не привязан к тесту

Связанные направления

CQL (Clinical Quality Language)

В session 065369a0 discovered как future direction. CDS Hooks, FHIR Clinical Reasoning module, AHRQ CDS tools, CQFramework, Google CQL Engine. Public rules library существует в текстовом формате. Это путь для evidence-based decision support вместо LLM-only generation. Ильдар: «про CQL это отдельная тема я вижу. давай как-то зафиксируем». Не реализовано, не start point — отдельный major direction для рассмотрения.

Eval system in fhir-services worktree

Ильдар упомянул: «кажется код для eval есть в fhir-services». 3-agent pipeline для тестирования генерации parameter analysis с agentic-loop pattern. Может быть портирован в analysis-worker как Inngest function для замены 5 отдельных промптов одним адаптивным агентом — Ильдар планировал попробовать в отдельной worktree-сессии.

Health Status v1

Минимальный v1 переиспользует существующие компоненты pipeline’а:

  • Prompt: существующий test-overview prompt в роли writer’а. Отдельный Health Status writer не создаётся.
  • Substrate: abnormals за последнюю дату всех тестов пациента (простой union, не полноценный cross-test aggregator).
  • Follow-ups: собираются из уже сгенерированных данных, отдельный механизм не вводится.
  • TTL биомаркеров: out-of-date параметры не передаются в retrieval, подсвечиваются в UI с пометкой. См. biomarker-actuality-thresholds.
  • Dual-version generation: обе версии (doctor + patient) формируются одновременно поверх identical substrate, см. doctor-patient-prompts-not-fork.
  • Endpoint — отдельный route, изменения изолированы на frontend.

Вне v1 (отложено на постепенное усложнение):

  • Cross-test aggregator (полный union по истории, не только abnormals)
  • Отдельный Health Status writer (отделённый от test-overview prompt)
  • Highlights generator (top-N приоритетных)
  • Versioning / история изменений Health Status
  • Pre-result опросник как substitute-канал контекста
  • Lazy generation

Открытые вопросы

  • [О1] Найти и ingest сессию с диаграммой триггеров. Ильдар упомянул что обсуждал тему trigger taxonomy в другой сессии и рисовал диаграмму. На 2026-04-26 — конкретная сессия не идентифицирована (grep дал 10+ кандидатов: e677dcbe Apr 20 main worktree, 1e52c32d/2b8ce115/6df35bba Feb early, 3763f542 Mar 12, 4a380012/9993d3f0/1efbee45 Apr — все non-digested). Action: ingest нужной сессии для context, не just pointer.
  • [О2] Naming. Vocabulary alignment в работе (chain 6bfe41d1, 2026-05-17). Working terms: Snapshot (curated patient context на as-of date — input в pipeline), Report (AI artifact на выходе — ClinicalImpression + Composition + CarePlan), Insights (themed cross-cutting sections в Report), Biomarker Analyses (per-biomarker structured rich-output, atomic). Pipeline naming + phase/step naming + section names — under discussion в health-report-vocabulary. Old column name fhirAssessmentId — оставлен для back-compat (легаси-читатели), не противоречит, но в новой vocabulary misleading.
  • [О3] ClinicalImpression — implementation landed, design под review. BG-1323 (session d9de9416) shipped: один CI per patient (identifier = fhirPatientId, PUT-replaced; run history через FHIR _history), per-biomarker rich-output в repeating extension bloodgpt-parameter-analysis (parameterName, value, unit, clinicalInterpretation, foundContext, citations, testDate). Workaround assessor: Practitioner-only — через Provenance.agent.who: Device/bloodgpt-pipeline-vN + note[].authorReference. Mapping см. в fhir-clinical-impression. Под вопросом правильная ли это shape долгосрочно — literal ClinicalImpression.finding[] vs custom extensions, singleton vs multiple CIs (run-history first-class), unused summary field. Re-design не запланирован, но shape под review.
  • [О4] Composition ↔ CI relationship. Composition остаётся как documentation-уровень (type=patient-summary, narrative sections), CI отдельно как assessment-уровень (per-biomarker rich-output). По плану BG-1323: Composition.section[].entry → Reference(ClinicalImpression) для join’а. На 2026-05-17: оба ресурса written per patient, но literal section.entry → CI references не написаны (carry-over из BG-1409). Reader matches Composition ↔ CI через meta.tag analysisRunId (см. фикс e025b436).
  • [О5] CQL incorporation — где CQL ложится в pipeline (между retrieval и generation? замена generation?). Слишком рано.
  • [О6] Migration path. Сейчас все Composition с subject=Patient но event не используется — backfill не нужен, добавлять postupно.

Источники (дополнительные)

Источники

Источники: 1 2 3.

Сноски

  1. FHIR R4 ClinicalImpression, accessed 2026-05-17, https://hl7.org/fhir/R4/clinicalimpression.html.

  2. FHIR R4 Composition, accessed 2026-05-17, https://hl7.org/fhir/R4/composition.html.

  3. FHIR Clinical Reasoning module, accessed 2026-05-17, https://hl7.org/fhir/R4/clinicalreasoning-module.html.