Предложение, обсуждается в chain 6bfe41d1, 2026-05-10).

Контекст

Лабораторные документы часто несут текст, привязанный к конкретному результату, а не ко всему отчёту: inline-ремарка (“slightly elevated, repeat advised”), сноска по маркеру (*, (1)), блок conditional-ranges (normal: <150, borderline: 150-200, high: >200), пояснение метода. Это не само значение, не врачебный вердикт — это аннотация к измерению.

Легаси-распознавание (recognize_gpt, Langfuse prompt) это поддерживает: per-parameter поле inline_interpretation (“any explanatory text, comments, or complex reference value structures located directly [рядом с параметром]”), per-parameter footnote_ids (number[]) → root-level массив footnotes[] ({id, symbol, text, scope}, scope ∈ parameter_interpretation | general_note), плюс секция “Footnote and Comment Processing” в промпте с правилами inline vs referenced.

Новый recognize_image_to_fhir (in-repo, recognize-image-to-fhir.schema.ts) это потерял: per-parameter поля — только param_name / original_value / value / unit / original_range / loinc_code / fhir_value / is_allergen / page / test_date. Комментарии/сноски могут попасть только в document-level narrative_blocks[] (content_type ∈ recommendation | other), не привязанные ни к какому параметру. Следствие: комментарий к конкретному результату либо теряется, либо всплывает несвязанным narrative-блоком — а иногда (как “Continue current lifestyle measures” в тесте teqzg42…) неудачно превращается в отдельный social-history Observation с valueString, что вдобавок ломает enrich-fhir-observations.

Связанное, но другое: лабовский H/L/↑/↓ флаг (не текст) — это original_status (RFC-013, proposed; см. recognition) → Observation.interpretation. Этот документ — про текст-комментарий, не про флаг.

Предложение

Вернуть в recognize-image-to-fhir схему/промпт:

  • per-parameter inline_interpretation: string | null — дословный inline-текст, привязанный к параметру; промпт классифицирует «комментарий vs блок conditional-ranges» как в легаси (если это conditional-ranges — original_range зануляется, текст идёт сюда);
  • per-parameter footnote_ids: number[];
  • root-level footnotes: [{ id, symbol, text, scope }], scope ∈ parameter_interpretation | general_note.

Маппинг в FHIR (без новых extension — см. zero-extensions-fhir):

  • свободный текст inline_interpretationObservation.note[] ({ authorString: lab_name | authorReference: Organization лабы, time: test_date, text }); автор — лаба, отличается от AI-ноты (authorReference: Organization/bloodgpt, добавляется enrich-fhir-observations — он сохраняет существующие note[], конфликта нет);
  • блок conditional-ranges из inline_interpretationObservation.referenceRange[].text (v1 — дословно; structured referenceRange[] с type / appliesTo — later, см. multi-tier-range-storage);
  • футноут со scope=parameter_interpretation, референснутый параметром → Observation.note[] с символом-префиксом ("* <text>");
  • футноут со scope=general_noteDiagnosticReport.note[] (это про весь отчёт, не про параметр).

Не делаем: парсинг “slightly elevated” → interpretation: H (распознавание — OCR, не врач; interpretation ставит шаг анализа, см. recognition и lab-status-vs-derived-interpretation); складывать комментарий в valueString / в дочерний Observation; новые custom extensions.

Следствия

  • Часть случаев, когда recommendation-текст лаб-коммента всплывает несвязанным Observation’ом (типа “Continue current lifestyle measures”), снимается: текст привязывается к нужному результату как note[], а не материализуется фейковым social-history Observation. (Где должен жить чисто-рекомендательный текст без привязки к параметру — CarePlan / DiagnosticReport.note[] / drop — см. lifestyle-vs-clinical-data, medical-as-instrument-not-recommendation, uploaded-document-types.)
  • Выбор «conditional-ranges → referenceRange[].text» де-факто частично разрешает multi-tier-range-storage в сторону Variant B (FHIR-native referenceRange[]), хотя там это пока contested.
  • Паритет нового image-pipeline с легаси recognize_gpt по comment/footnote-данным — снимает один из cutover-блокеров (см. fact-based-recognition).
  • narrative-to-fhir / save-fhir-* шаги получают новый маппинг (inline_interpretation / footnotesObservation.note[] / referenceRange[].text / DiagnosticReport.note[]).

Observation.note[] — только lab-originated

Важное уточнение: Observation.note[] зарезервирован под аннотации из документа (этот inline_interpretation / footnotes, author = лаба). Сгенерированный нами контент туда НЕ идёт — в V2.5 пер-параметровый AI-разбор живёт в ClinicalImpression.extension[bloodgpt-parameter-analysis], на уровне пациента — в Composition/CarePlan. Раздельность «оригинальное vs сгенерированное» — чтобы можно было достать одно без другого (?_security:not=AIAST для trusted lab data; ?_tag=origin|user-uploaded); tagging mechanism — fhir-meta-tagging; lifecycle (immutability raw Observation, легаси enrich-fhir-observations кладёт AI parameterDetails в Observation.note[] с author=Organization/bloodgpt — артефакт тест-scoped эры, помечен на удаление) — fhir-resource-origin-and-lifecycle. То есть после этого decision’а в Observation.note[] живёт лаб-текст; наш текст — отдельно, в своих ресурсах.

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

  • authorString: lab_name vs создавать/референсить Organization для лабы (есть lab_name из test_info) — v1 строкой, потом, возможно, ресурс.
  • Дедуп: если тот же inline-текст подтверждён несколькими VLM-проходами / уже есть в note[] — не дублировать (как enrich-fhir-observations делает notes = [...(obs.note||[])]).
  • Нужно ли отдельное поле под conditional-ranges в схеме (conditional_ranges?) вместо того чтобы они приезжали внутри inline_interpretation и классифицировались эвристикой в narrative→FHIR.
  • Перенос обновлённого recognize_image_to_fhir промпта/схемы в Langfuse (сейчас in-repo).

Связано

Источники

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

Сноски

  1. FHIR R4 Observation.note / Observation.referenceRange, accessed 2026-05-17, https://hl7.org/fhir/R4/observation.html.