Процесс: как структурированный AI-output BloodGPT (интерпретация анализов, рекомендации, follow-up) ложится на стандартный FHIR R4.

Текущее состояние — подвижное. Раньше страница утверждала «ложится почти полностью, один custom extension». На практике это держится только для deployed-минимума (Observation/DiagnosticReport/Composition/CarePlan + requiresDoctorPreparation). Как только спускаемся в rich-output последнего многостадийного анализа биомаркеров (там тоже пока нет определённости с формой output’а), появляются открытые вопросы и contested-маппинги (foundContext → investigation vs problem vs supportingInfo; missingContext → note vs extension; CI как зонт vs размазать по стандартным ресурсам). Конкретно этот «куда складывать staged-analysis rich-output» — отдельная contested decision-страница staged-output-fhir-storage (первичный источник, эта страница — синтез вокруг). Набор «нужных» custom extensions переоценивался уже несколько раз — финального списка нет. См. zero-extensions-fhir для принципа, секции Trade-off и Открытые вопросы ниже — для актуальных развилок.

Визуальная картина: 5 слоёв

AI-output BloodGPT раскладывается на 5 концептуальных слоёв. Сначала “где сидит ответственность” — потом детали маппинга в секциях ниже.

СлойСемантикаFHIR-ресурсыStatus
0. Source documents (ingest)оригиналы файлов до парсинга — PDF лаб-отчёты, HL7-сообщения, выпискиfhir-document-referenceresearch (сейчас оригиналы в GCS без FHIR-обёртки)
1. Patient ground dataфакты о пациенте, без AI — выходят из ingest-парсингаfhir-observation (lab values), Condition, MedicationRequest/Statement, AllergyIntolerancedeployed
2. AI assessment per biomarkerсинтез поверх ground data — clinical view per-параметрfhir-clinical-impressionexploratory
3. Recommendations / actionsпредлагаемые действияfhir-service-request (additionalWorkup), fhir-careplan (follow-up)SR exploratory, CarePlan deployed
4. Document aggregationконтейнеры для рендера и rollup AI-output’аfhir-composition (clinical document), DiagnosticReport (test-level)deployed
5. Audit / provenanceкто/когда/каким AI создалfhir-provenancedeployed

L0 (DocumentReference) — отдельный обсуждаемый трек: трекать ли incoming documents как первоклассные FHIR-ресурсы, или оставлять их вне FHIR (как сейчас — оригиналы в GCS, в FHIR живут только распарсенные Observation/Condition). Direction — внедрить, без timeline. Подробности и аргументы на entity-странице.

L4 (Composition) — наш AI-output документ (клиническое narrative + sections). НЕ путать с L0 — это разные сущности: L0 = incoming raw, L4 = generated output.

flowchart TB
    subgraph L1["L1 — Patient ground data (pure, no AI)"]
        OBS[Observation<br/>biomarker value + LOINC]
        COND[Condition]
        MED[MedicationRequest]
    end

    subgraph L2["L2 — AI assessment per biomarker"]
        CI[ClinicalImpression<br/>summary, investigation,<br/>finding, note, extensions]
    end

    subgraph L3["L3 — Recommendations"]
        SR[ServiceRequest<br/>proposed tests<br/>intent: proposal]
        CP[CarePlan<br/>follow-up schedule]
    end

    subgraph L4["L4 — Document aggregation"]
        COMP[Composition<br/>section per overview]
        DR[DiagnosticReport<br/>conclusion + conclusionCode]
    end

    subgraph L5["L5 — Audit / provenance"]
        PROV[Provenance<br/>agent.who Organization/bloodgpt<br/>extension bloodgpt-llm]
    end

    CI -->|focus| OBS
    CI -.->|investigation/supportingInfo<br/>CONTESTED| OBS
    CI -.->|problem<br/>CONTESTED| COND
    CI -.->|supportingInfo| MED
    SR -->|reasonReference| CI
    DR -->|result| OBS
    COMP -->|section.entry| CI
    COMP -->|section.entry| CP
    PROV -->|target| CI
    PROV -->|target| SR

Легенда стрелок: сплошная — settled FHIR reference; пунктир + CONTESTED — открытые вопросы (см. foundcontext-fhir-mapping).

Output многостадийного анализа → FHIR (condensed)

Срез mapping’а по полям, которые генерирует многостадийный анализ биомаркеров (форма output’а сама ещё подвижная, см. ту страницу). Status в правой колонке относится к FHIR-маппингу, не к стабильности самого поля.

ПолеСлойFHIR slotStatus
clinicalInterpretation2CI.summarysettled
reasoning2CI.note[authorString="diagnostician"]settled
whatAdditionalDataWouldClarify2CI.note[authorString="personalizer"]settled
foundContext[]2CI.investigation / problem / supportingInfocontested (foundcontext-fhir-mapping)
missingContext[]2CI.note[] или extensionopen
referenceContext[]2CI.supportingInfo[]settled
additionalWorkup[]3отдельный ServiceRequest (intent: proposal)settled
triage2CI.extension/bloodgpt-triage (parameter-triage-codes)settled
citations[]2CI.extension/bloodgpt-citation* (repeating)settled

Per-resource подробности — fhir-clinical-impression. Boundary с Observation и RiskAssessment (что НЕ относится к CI) — там же. Custom extensions namespace http://bloodgpt.com/fhir/StructureDefinition/..., перечень — секция Какие custom extensions реально нужны ниже.

Маппинг — DB-таблицы → FHIR (легаси-карта)

Эта таблица — другой срез того же mapping’а: с точки зрения наших Postgres-таблиц (это привычная нам ментальная модель — “что лежит в БД, и куда оно поедет в FHIR”). Парная с condensed-таблицей выше — там срез “по полям pipeline-output’а” (что генерирует pipeline, в какой FHIR-slot ложится). Обе нужны: верхняя таблица помогает понимать новый shape AI-output, эта — траекторию миграции каждой существующей DB-сущности.

Наши таблицы / поляFHIR-ресурс
TestOverview (narrativeSummary + sections)fhir-composition (с nested section[])
PanelOverviewsub-sections в той же Composition
FollowUpRecommendations (расписание + срочные тесты)fhir-careplan (activity[].detail + LOINC + priority)
ParameterAnalysis (isHealthy, parameterDetails, reasoning)fhir-observation (interpretation + note[])
ParameterTrends (исторические значения)Observation с _sort=date запросом по LOINC
ParameterTrendAnalysis (AI-текст про тренд)Composition Section “Trends” → sub-section per parameter (065369a0)
AI-комментарии (любой note[])fhir-annotation как data type
Авторство всего AI-контентаfhir-organizationOrganization/bloodgpt (с Feb 17 2026, fhir-device initial superseded)
requiresDoctorPreparationextension на Composition (единственный custom)

Конкретные поля, JSON-примеры, github-ссылки на builder’ы — в самих entity-страницах. Эта таблица — навигационная.

Ключевые решения

  • zero-extensions-fhir — стандартные FHIR-поля везде где они есть. Custom extension только когда стандартного аналога нет. Конкретный decision-framework “когда extension оправдан” — gap в текущем decision-page (см. [О4]).
  • ai-enrichment-separate-step — AI-добавки (note[] + interpretation) пишутся отдельным PUT-step после fhir-resource-creation, не inline на create.
  • authorship-organization-not-deviceOrganization/bloodgpt как author (Device был initial выбор, заменён после Healthcare API limitation).

Направление: FHIR-only storage (confirmed Ильдаром 2026-04-26: «точно стоит переехать, тут нет вопросов, мы всё храним в FHIR»). Rich-output многостадийного анализа сейчас в Postgres parameterAnalysis columns + UI читает оттуда. Cutover на FHIR-only — pending. Proposed FHIR target — fhir-clinical-impression (per-parameter CI), explored в session d9de9416 (BG-1323). Builder + parser в worktree, не deployed.

См. также где-этот-pipeline-живёт-в-коде — open вопрос «куда сохранять facts/reasoning в FHIR» из generation-pipeline относится сюда: ClinicalImpression — основной кандидат для output этого pipeline.

Принципы FHIR-моделирования AI-контента

При выборе где хранить новые AI-генерируемые данные в FHIR — придерживаемся правил:

  • Native FHIR-поля > custom extensions. Если есть стандартное поле под смысл (finding[] для diagnostic conclusions, note[] для prose, investigation[].item для evidence) — используем его. Custom extension оправдан только когда стандартного аналога нет.
  • Не сливать структурированные данные в свободный текст. Если есть массив items (found-context / missing-context / additional-workup) — кладём в native list-поле, не в summary как «mush».
  • Сохранять структуру какой она была сгенерирована — не упрощать ради удобства одной consumer-странички.
  • Append-only история. Старые AI-ассессменты не переписываются при поступлении новых данных — это исторические записи «что мы думали тогда», нужны для медицинского audit.

Конкретный пример калибровки (session d9de9416): builder для CI изначально клал structural evidence в плоские extensions (bloodgpt-found-context, bloodgpt-missing-context); после ревью переписан на native поля. Это иллюстрирует правило «native > extensions».

Какие custom extensions нужны — рабочий список

Список пересматривался несколько раз и пока не закрыт. Текущий минимум для rich-output многостадийного анализа биомаркеров, где стандартного FHIR-аналога заведомо нет:

  • bloodgpt-triage — наша product-specific taxonomy (urgent_doctor / routine_doctor / monitor / ok_in_context, см. parameter-triage-codes).
  • bloodgpt-citation — AI traceability (title / url / quote / source).
  • bloodgpt-llm — model / prompt version / latency / tokens (на Provenance, single per Provenance).

Что может ещё добавиться — зависит от того, как разрешатся contested-маппинги (foundContext, missingContext, см. condensed-таблицу выше и foundcontext-fhir-mapping). Что может уйти — если выберем вариант B из trade-off ниже («размазать по стандартным ресурсам»), часть из этих трёх может оказаться лишней.

Остальные поля анализа гипотетически укладываются в native FHIR-поля или существующие ресурсы (детали — mapping-v25-ci-где-plan-файл-расходится-со-spec), но это не settled до cutover.

Trade-off: ClinicalImpression-зонт vs комбинация стандартных ресурсов

Полное обсуждение с осями и аргументами — на отдельной decision-странице staged-output-fhir-storage (contested). Ниже — короткая версия двух полюсов, чтобы не открывать ту страницу для оглавления.

Два архитектурных варианта для rich-output многостадийного анализа, оба обсуждаются:

Вариант A — ClinicalImpression как зонт per-параметр

Один CI на параметр. CI собирает в себя clinicalInterpretation, reasoning, evidence references, triage и пр. Подробно — fhir-clinical-impression. Форма самого pipeline-output’а, который сюда укладывается, ещё подвижна — см. biomarker-analysis-pipeline.

Плюсы:

  • AI-output сгруппирован per-параметр в одном объекте — один GET ClinicalImpression?subject=X отдаёт всё AI на пациенте.
  • Provenance / audit chain прозрачно ссылается на отдельные CI.
  • Concept «AI assessment» как FHIR-сущность — удобно для downstream consumers.

Минусы:

  • ClinicalImpression в R4 — Trial Use, менее стабильный.
  • assessor: только Practitioner — блокер для AI-author, нужен workaround через Provenance.agent.who: Device.
  • Несколько custom extensions нужно именно на CI, плюс vocabulary около него.

Вариант B — комбинация стандартных ресурсов без ClinicalImpression

Размазать output многостадийного анализа по существующим FHIR-ресурсам:

ПолеКуда
clinicalInterpretation (prose)Observation.note[] на самом параметре (где предыдущая версия уже живёт)
likely-diagnoses (если выделим)Condition с verificationStatus=provisional, attached к patient
foundContext[] evidenceНе отдельный ресурс — Observations доступны через subject=Patient
additionalWorkup[]ServiceRequest (intent: proposal)
referenceContext[]Observation.referenceRange[] — стандартное место
Test overviewComposition (как сейчас) с section[].entry → Observation/Condition/ServiceRequest
triageextension на Composition или на Condition
AI metadataProvenance на каждом resource

Плюсы:

  • Всё native FHIR. Никаких новых ресурсов. Меньше custom extensions.
  • Используем Trial-Use ресурсы по минимуму.
  • Каждый resource по прямому назначению (Condition для диагнозов, ServiceRequest для tests).

Минусы:

  • AI-consumer должен делать _include-запрос чтобы собрать всё AI на пациенте — нет одного-go-to ресурса «дайте мне AI assessment».
  • Concept «AI assessment per параметр» теряется как объект — размазан по нескольким resources.

Финально не выбрано. Plan-файл и текущий builder идут по варианту A; вариант B — альтернатива для рассмотрения, особенно если CI окажется неудобным в production (Trial Use, vendor compat).

Workflow при новом тесте — что регенерируется

Inngest-pipeline должен быть идемпотентным и append-only — это вытекает из принципа append-only истории.

  1. Recognition создаёт новые Observation для новых параметров.
  2. Diagnostic + Retriever + Generator запускается только для новых аномальных параметров. Старые Observations не трогаются — pipeline skip уже-processed.
  3. Создаются новые CI / Conditions / ServiceRequest для новых параметров. Старые остаются как есть — медицинский audit-friendly, никогда не переписываем.
  4. Test overview Composition — новый per тест, ссылается на новые Observations (+ опционально исторические для контекста). Старые Composition не апдейтятся.
  5. CarePlan (follow-up) — новый, зависит от текущего состояния пациента (все active Conditions + только что найденное). Старые CarePlan остаются.
  6. Trends — рассчитываются on-read через все Observations с одинаковым code и subject=Patient. Не предгенерируются, не хранятся как ресурс.
  7. При snapshot-модели (health-report-vision): patient-level snapshot создаётся новый, ClinicalImpression.previous chain (или его аналог) соединяет с предыдущим — timeline доступен как linked list.

Ключевые свойства которые pipeline должен соблюдать:

  • Идемпотентность — для уже-processed Observation pipeline не создаёт дубликатов AI-output.
  • Append-only — старые ассессменты не пересматриваются, новые добавляются.
  • Patient-state-driven follow-up — CarePlan смотрит на текущее состояние, не на «что было в этом тесте».
  • On-read trends — timeline не предгенерируется.

Связанные exploratory (часть BG-1323, FHIR-cutover для многостадийного анализа):

Открытые decisions:

  • TBD decisions/ai-versioning-in-fhir — model version + prompt version сейчас в Postgres (aiModel/promptVersion поля). При FHIR-only cutover — multi-agent Provenance chain (fhir-provenance) с Device/bloodgpt-pipeline-vN через Provenance.agent.who хранит эту информацию. Не резкая необходимость до compliance audit.
  • TBD decisions/interpretation-source-priority — multi-source interpretation. Production deployed (3-tier): seed CodeableConcept → LLM code → deterministic (BG-1207 + BG-1258, session 731a3f60, см. fhir-observation). Экспериментально (4-tier): ветка bg-1297-lab-interp (commit 05846324, не merged) добавляет top-rung labInterpretationCode через POST API + HL7 OBX-8. Frontend display при divergence — open.

BloodTest.overallStatus — legacy unused field (calibration по Ильдаром, 2026-04-26)

Поле BloodTest.overallStatusустаревшее, неиспользуемое, на текущую систему не влияет. Per-parameter interpretation (через fhir-observation standard ValueSet) и parameter_range_type — actual sources. overallStatus остаётся в schema как legacy, безопасно игнорируется.

(Замечание: в первичном digest 731a3f60 [Н2] было неправильное предположение про “overview-LLM который выставляет overallStatus” — это была misinterpretation Ильдара’s instruction “не гадай, посмотри кто”, не архитектурное утверждение.)

Pipeline — где это всё происходит во времени

Порядок Inngest-функций (по domain-группам из worker.ts, упрощённо, без error-paths):

  1. Inputinput-pdf-recognition / input-hl7-parsing / input-json-parsing → распознанные параметры
  2. Normalizationnormalization-loincnormalization-parametersnormalization-ranges
  3. FHIR creationfhir-resource-creation → Patient + DiagnosticReport + Observations с базовыми полями (valueQuantity, code, referenceRange)
  4. InterpretationanalysisPipeline (interpretation-analysis.function.ts) → LLM читает Observations из FHIR, генерирует AI-content (interpretation, parameterDetails, panel overviews, narrative, follow-ups)
  5. EnrichmentenrichFhirObservations + family (overviewGeneration / panelOverviewGeneration / followUpGeneration / trendsGeneration / productRecommendationGeneration) + saveFhirAiResources → PUT’ы на Observations с note[] + interpretation; создание Composition (включая Section “Trends” с trendAnalysis per parameter, добавлено в 065369a0) + CarePlan; запись fhirAssessmentId + fhirFollowUpId колонок в BloodTest (065369a0)
  6. Doctor Validation (HITL)doctor-review-gate (опциональный gate)
  7. Outputoutput-pdf-generation

Между шагами FHIR store работает как shared state (replaces legacy BloodTest.rawRecognitionResult JSON). Detailed orchestration → inngest.

Что попадает в FHIR store

Структурный snapshot после полного прогона одного теста (placeholder’ы вместо конкретных биомаркеров). Справа — на каком pipeline-шаге элемент появляется или дополняется:

Patient/{patientId}                                          ← fhir-resource-creation
 └─ DiagnosticReport/{reportId}                              ← fhir-resource-creation
      │   (status: final, performer: лаб, resultsInterpreter: bloodgpt-com)
      │
      └─ result[]
           ├─ Observation/{panel}                            ← fhir-resource-creation
           │    │   (panel = группирующий Observation: code + hasMember[], без value)
           │    │
           │    └─ hasMember[] → Observation/{biomarker}     ← fhir-resource-creation
           │                     │
           │                     ├─ valueQuantity + referenceRange         ← fhir-resource-creation
           │                     ├─ interpretation (H/L/N)                 ← enrichFhirObservations (PUT)
           │                     └─ note[] (parameterDetails, AI-комментарий) ← enrichFhirObservations (PUT)
           │
           └─ ... другие panel'ы

Composition/{compId}                                         ← saveFhirAiResources
 │   (type: Assessment, author: Organization/bloodgpt)
 │   (subject: Patient — не DR; event = trigger reference, опционально)
 │   (extension: requiresDoctorPreparation — единственный custom)
 │
 ├─ section "Interpretation"     (text.div + entry → DiagnosticReport)
 ├─ section "Patterns"           (sub-sections per identified pattern)
 ├─ section "Health Considerations"
 ├─ section "Pay Attention"      (recommendations с category)
 └─ section "Trends"             ← saveFhirAiResources (065369a0)
      └─ sub-sections per biomarker (text.div = trendAnalysis от AI)

CarePlan/{planId}                                            ← saveFhirAiResources
 │   (intent: proposal, author: Organization/bloodgpt)
 ├─ supportingInfo  → DiagnosticReport
 └─ activity[]      → ServiceRequest (LOINC + scheduledPeriod + priority)

Organization/bloodgpt                                    ← создаётся один раз при provisioning

Что это показывает:

  • Граф растёт layer’ами, а не переписывается. fhir-resource-creation строит skeleton (Patient + Report + Observations с raw values). Enrichment достраивает существующие Observations (PUT с дополнительными полями, не replace) и добавляет новые ресурсы (Composition / CarePlan).
  • Атомарность Observation = биомаркер. Panel — группирующий wrapper, у него только code + hasMember[], без value. Это стандартный FHIR-паттерн, не наша придумка.
  • Composition и CarePlan связаны с DiagnosticReport, не дублируют его. Через entry/supportingInfo references — DiagnosticReport остаётся single source of truth для лабораторных значений.
  • Authorship чёткое. Лаб-данные — без author. AI-генерации — Organization/bloodgpt. Фильтр по author даёт ответ “только det” / “только AI” (см. [О5]).

Следствия для BloodGPT

(Черновик — не согласовано с Ильдаром.)

  • Минимизация custom extensions = страховка для B2B integration. У нас не ноль (requiresDoctorPreparation есть), но один extension — приемлемо. Партнёр-лаб / EMR читает FHIR-ресурсы стандартным парсером, interpretation (H/L/N) и note[].text отрисовываются в любом FHIR-клиенте.
  • Patient/$everything используется в apps/b2b-api/src/utils/fhir-response-builder.ts, но с defensive fallback: Composition и CarePlan фетчатся отдельно — Google Healthcare API не гарантирует их возврат в $everything bundle. То есть готов “почти” out-of-the-box, с одним workaround. Не custom export endpoint.
  • requiresDoctorPreparation — мониторим как single point of vendor lock-in. Один extension хорошо изолирован. Если custom extensions начнут расти — это сигнал что концепт не fits R4. R5 как fallback неочевиден — vendor support под вопросом (см. [О3]); возможные пути — IPS extensions, SNOMED concepts, или переоценить как поле моделится через стандартный механизм.
  • AI enrichment как отдельный step — generalizable pattern. Если завтра появится HITL doctor-review (врач добавляет свой note), архитектура готова — doctor-review-gate.function.ts уже существует как gate, расширение под note-write встанет в ту же модель post-create update.
  • Тест-центричная модель — текущая, не финальная. В session 065369a0 обсуждался paradigm-shift на snapshot-центричную модель (Composition.subject = Patient, триггеры — тест / новый диагноз / лекарство / прошедшее время). Текущая страница описывает тест-центричную реализацию; exploratory direction зафиксирован в health-report-vision.

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

[О1] B2B API read path — миграция с Prisma include на FHIR queries

GET-endpoints в b2b-api пока ходят в Prisma include. Должны читать из FHIR (GET Composition?subject=Patient/{id} + parsing nested sections). Требует pagination strategy, caching consideration (latency), и frontend mapping (UI ждёт ту же shape что Prisma давал).

Ильдар: «они по сути все должны использовать fhir как источник истины»

[О2] ParameterTrends — как frontend получает trend chart

Маппинг “ParameterTrends → Observation с derivedFrom” зафиксирован, но как именно frontend агрегирует значения за период (одиночный _revinclude запрос? batch-fetch? отдельный custom endpoint?) не разобрано.

[О3] FHIR R4 vs R5 — vendor support

Stored как R4. R5 опубликован (2023) и стабилен, имеет обновлённый Annotation, новые value types. Главный блокер — vendor support: Google Healthcare API на 2026-04 R5 не в GA (нужен periodic re-check). HAPI R5 поддерживает. Поэтому R5 для нас не tactical fallback — sticking with R4 на 12+ мес., через год переоценить vendor landscape.

[О4] Decision-framework “когда extension оправдан”

decisions/zero-extensions-fhir фиксирует принцип (“standard где можно, extension когда нет”), но не процесс как это решать в конкретный момент. Tactical практика подсказывает шорткат: Composition Section с custom code предпочитается extension’у с typed value — secвенно проверено в 065369a0 на Trends section. Хороший process-snippet: (1) поискать стандартное FHIR-место, (2) если нет — попробовать Composition.section с custom section-type коде, (3) extension как последний резорт.

[О5] Разделение детерминированного и AI-генерированного контента — ongoing

Technical путь найден (но не финал) через fhir-meta-tagging (session 7ff79368, Mar 30):

  • meta.tag.code = "document-import" — лабораторные данные из загруженных документов
  • meta.tag.code = "health-chat" — patient-reported через chat с AI-агентом
  • meta.tag.code = "survey" — patient-reported через опросник
  • authorReference = Organization/bloodgpt дополнительно: лаб-данные без author, AI с author

Это filter-механизм на читающей стороне, не полное решение разделения. Открыто на более широком уровне:

  • Pipeline-уровень: должны ли det и AI идти разными pipeline-шагами? Сейчас в analysis-worker они переплетены (FHIR-resource-creation создаёт лаб-Observations и затем enrich-fhir-observations добавляет AI note[]/interpretation на те же ресурсы).
  • Storage-уровень: разделять ли хранилища? Сейчас всё в одном FHIR store, разделение только через теги.
  • API surfaces: должен ли B2B API отдавать только-det и только-AI как разные эндпоинты? Сейчас один path возвращает всё.
  • Source-of-truth ownership: для перезаписей (re-process одного теста) — что считается truth-set? Det-данные иммутабельны (от лаборатории), AI-данные могут пересоздаваться. Это влияет на dedup и versioning.
  • Governance: какие use-cases требуют det-only view? Какие — only-AI? Это продуктовое решение, не technical.

Это была одна из важных итераций архитектурного поиска (065369a0, 7ff79368). Tag-based filter — необходимый, но не достаточный шаг.

[О6] AI-content audit / маркировка для compliance — partial

Базовый audit готов через fhir-meta-tagging + fhir-provenance (session 7ff79368) для V0.5 write tools:

  • Кто (Provenance.agent.who = Organization/bloodgpt)
  • Когда (Provenance.recorded timestamp)
  • Из чего (Provenance.entity.what → DocumentReference, для document-import)
  • Source class (meta.tag.code = "health-chat" | "survey" | "document-import")

Что не покрыто (поэтому partial, не closed):

  • AI model version + prompt version — хранятся в BloodTest Postgres (aiModel, promptVersion), не в FHIR. Если regulatory потребует full provenance в FHIR — нужно решить как (TBD decisions/ai-versioning-in-fhir).
  • Audit для analysis-worker pipeline (test-flow): save-fhir-resources создаёт Composition + CarePlan без Provenance. План описан (~30-40 строк кода), реализация отложена в session 7ff79368.
  • Audit trail для re-process — если пациент перезагружает тот же тест и AI генерирует другую интерпретацию, как это связать с предыдущей версией. Не зафиксировано.
  • Audit на read-side: кто читал PHI, когда, через какой endpoint. Это уже Cloud Audit Logs / WAF-уровень, не FHIR-data; см. multi-tenant-fhir-storage [О3] HIPAA compliance.

[С1] Drift между FHIR builders и frontend

fhir-composition-builder.ts создаёт structure { Section "Interpretation" → Sub-sections → text.div }. Frontend парсит её обратно в narrativeSummary + identifiedPatterns[]. Если builder поменяется без updates frontend → silent breakage. Не решено: shared TypeScript types? Schema validation? Contract test?

Связано

Источники

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

Сноски

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

  2. v3-ObservationInterpretation ValueSet, accessed 2026-05-17, https://terminology.hl7.org/CodeSystem-v3-ObservationInterpretation.html.

  3. Реализация (enrichment/ family), accessed 2026-05-17, https://github.com/Realai-plus/bloodgpt-for-business/tree/main/apps/analysis-worker/src/inngest/functions/enrichment.