Контекст

В pipeline процесса анализа крови есть фаза AI analysis, которая генерирует:

  • parameterDetails (текст-комментарий на каждый параметр)
  • interpretation (H/L/N через AI, не только формула value vs range)

Эти данные должны попасть в fhir-observation как note[] + interpretation. Вопрос: когда именно они туда записываются — при initial create или отдельным update.

Рассматривали

  • Вариант A: поле aiNote?: string на ObservationData interface, заполняется до save-fhir-resources step, попадает в Observation сразу при create. Один write — никакого update. Проблема: AI analysis в текущем pipeline запускается после save-fhir-resources. Observation уже создан до того как AI сгенерировал parameterDetails. Поле было бы пустым; либо нужно переставлять весь pipeline.
  • Вариант B: отдельный step update-fhir-with-ai-analysis — запускается после AI analysis, делает PUT Observation/{id} с добавленными note[] + interpretation. Двухшаговая запись (create → update), но не требует ломать pipeline.
  • Вариант C: переструктурировать pipeline — сначала AI analysis на сыром recognition output, потом одним bundle save в FHIR. Сложно: разные шаги пайплайна, между ними нужно где-то держать промежуточное состояние, а Cloud SQL для PHI как раз убираем (см. phi-in-fhir-not-sql). Один write, но требует что AI analysis работает без FHIR Observation как input — неконсистентно с тем, что часть AI-логики читает Observations.

Выбрали: B — отдельный update-step

Pipeline:

  1. save-fhir-resources создаёт Observations с базовыми полями (valueQuantity, code, referenceRange).
  2. AI analysis читает Observations через FHIR (по IDs которые вернул шаг 1).
  3. update-fhir-with-ai-analysis Inngest function делает PUT Observation/{id} для каждого, добавляя note[] + interpretation.

Почему

  • Не требует переставлять pipeline. AI analysis уже работает после save-fhir-resources в текущей архитектуре.
  • Согласуется с тем что FHIR store становится shared state между Inngest steps (см. inngest и phi-in-fhir-not-sql). AI analysis читает Observations из FHIR, обновляет туда же — никакого in-memory передавания тяжёлых JSON.
  • PUT идемпотентен по FHIR-семантике — повторные запуски enrichment не ломают данные.
  • Ясная attribution: при create Observation автор — лаборатория (или null); при update — note[].authorReference указывает на BloodGPT (см. authorship-organization-not-device).

Claude (признание initial mistake): «Запихивать всё в одно aiNote — неправильно. И ещё проблема: AI analysis запускается ПОСЛЕ save-fhir-resources в Process pipeline»

Следствия

  • Реализация — Inngest function enrichFhirObservations (enrich-fhir-observations.function.ts). Рядом в той же папке enrichment/ живёт целая семья post-AI шагов: overview-generation, panel-overview-generation, follow-up-generation, trends-generation, product-recommendation-generation, save-fhir-ai-resources.
  • Observation существует в двух состояниях: post-create (без AI-полей) и post-enrichment (с note[] + interpretation). Любые downstream-читатели должны быть готовы к обоим.
  • При rebuild AI analysis (поменяли pipeline или prompt) — re-run только enrichment step, не весь pipeline.
  • B2B API GET endpoints, читающие Observations между save-fhir-resources и enrichFhirObservations, увидят их без note[] + interpretation. Edge case (клиент опрашивает прямо во время процессинга, до генерации) — если нужна гарантированная консистентность, ждать enrichment.completed event.

Связано

Источники

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

Сноски

  1. Реализация, accessed 2026-05-17, https://github.com/Realai-plus/bloodgpt-for-business/blob/main/apps/analysis-worker/src/inngest/functions/enrichment/enrich-fhir-observations.function.ts.

  2. Worker registration, accessed 2026-05-17, https://github.com/Realai-plus/bloodgpt-for-business/blob/main/apps/analysis-worker/src/inngest/worker.ts.