Status: draft
Кратко: для timeline v1 закрываем минимум gap’ов — category нужно для иконок. Полный coverage всех ресурсов — на v2. Какие именно gap’ы закрываем сейчас — ниже.
Контекст
Аудит builder’ов в packages/analysis-core/src/services/narrative-to-fhir/fhir-builders/*.ts + lib/fhir-*.ts + apps/analysis-worker/.../save-fhir-resources.function.ts (см. [[../domain/fhir-resource-categories]] раздел «Что мы пишем сейчас») показал:
| Ресурс | Стандартный код | Custom параллельно | Статус |
|---|---|---|---|
| Observation (lab) | laboratory | + custom panel-categories | ✅ ок |
| Observation (narrative) | LLM enum 7/9 R4 кодов | — | ✅ ок |
| DiagnosticReport | только LAB hardcoded | — | ⚠️ gap (нет RAD/IMG/EC) |
| Condition | problem-list-item hardcoded | — | ✅ ок (с rationale про US-Core query) |
| AllergyIntolerance | LLM enum (все 4 R4) | — | ✅ ок |
| MedicationStatement | ❌ не set | — | ❌ gap |
| Procedure | ❌ не set | — | ❌ gap |
| CarePlan | — | custom bloodgpt.com/careplan-category | ⚠️ только custom, std нет |
| Composition (top-level) | ❌ не set | section codes — custom | ❌ gap |
| Immunization | (resource не emit’ится) | — | ❌ gap (нет builder) |
Для Timeline иконок нужно чтобы у каждого ресурса был хоть какой-то category — иначе иконка fallback’ается на resource type, теряется гранулярность (например все procedure’ы выглядят одинаково, surgical vs diagnostic не различимы).
Рассматривали
A. Закрыть всё сразу
Добавить category всем ресурсам где gap (5 мест), плюс заменить custom CarePlan system на standard.
Плюс: консистентность, полная картина для timeline. Минус: больше touch-points в коде, больше тестов, дольше до v1 timeline. Часть gap’ов (Immunization builder) — большая работа.
B. Закрыть только то что нужно для visible иконок v1
Что реально появится на timeline v1:
- Observation lab → ✅ есть
laboratory - Observation vital-signs → ✅ есть
vital-signs - Condition → ✅ есть
problem-list-item - AllergyIntolerance → ✅ есть category из LLM enum
- MedicationStatement → ❌ gap — добавить
- Procedure → ❌ gap — добавить
- DiagnosticReport → ⚠️ только LAB — расширение зависит от
document_typeкоторое LLM сейчас не возвращает (см.[[uploaded-document-types-supported]]); отложить до этой фичи - Composition / CarePlan → AI-генерации, не на clinical timeline (см.
[[timeline-page-design]]про двухтрековое разделение). Иконка нужна только на system timeline, можно использовать какой угодно код или fallback на resource type - Immunization → не emit’ится сейчас, не v1
Плюс: минимум изменений для unblock timeline v1.
Минус: оставляет gap’ы которые могут confuse в будущем, plus в FHIR search через ?category= некоторые ресурсы не filtable.
C. Не закрывать ничего, fallback на resource type
Иконку на timeline всегда брать из FHIR resource type, не из category. Procedure → ⚙️, MedicationStatement → 💊, etc. Простая 1-1 mapping table.
Плюс: zero code changes.
Минус: теряется гранулярность — Observation laboratory vs Observation vital-signs будут одинаковыми иконками, хотя сейчас они различимы. Откат гранулярности.
Выбрали: B (close-the-visible-gaps)
Почему:
- Timeline v1 unblock — главная цель сейчас. A слишком много touch-points.
- C жертвует уже работающей гранулярностью Observation lab vs vital-signs. Не приемлемо — это ровно то ради чего category вообще нужна.
- B минимальная работа: 2 builder’а добавить category выставление + плюс minor updates для Composition/CarePlan если нужно.
Конкретные изменения для v1
MedicationStatement — добавить patientspecified
В narrative-to-fhir/fhir-builders/medication-statement.ts, после resource construction, добавить:
resource.category = [
{
coding: [
{
system: "http://terminology.hl7.org/CodeSystem/medication-statement-category",
code: "patientspecified",
display: "Patient Specified",
},
],
},
];Default patientspecified обоснован тем что наши narrative MedicationStatement в основном из выписок или со слов пациента — semantically это и есть «patient-specified». В будущем (если появится integration с pharmacy) можно расширить enum в LLM extracted schema чтобы LLM сам выбирал между community / patientspecified.
Procedure — добавить SNOMED-based category из LLM
В extract-entities.ts schema добавить поле category к ExtractedProcedureSchema:
export const ProcedureCategoryEnum = z.enum([
"surgical", // SNOMED 387713003
"diagnostic", // SNOMED 103693007
"counselling", // SNOMED 409063005
"education", // SNOMED 409073007
"social-service", // SNOMED 410606002
"other",
]);
ExtractedProcedureSchema = z.object({
...,
category: ProcedureCategoryEnum,
});В narrative-to-fhir/fhir-builders/procedure.ts mapping enum → SNOMED code. Default fallback (other или missing) → 103693007 Diagnostic procedure (большинство наших процедур из narrative — это диагностика).
Composition (top-level) — добавить assess-plan
В fhir-composition-builder.ts, при construction Composition, добавить:
composition.category = [
{
coding: [
{
system: "http://hl7.org/fhir/composition-category", // example
code: "assess-plan",
display: "Assessment Plan",
},
],
},
];Это для system timeline иконки — Composition будет различим от других resources в feed’е.
CarePlan — добавить standard assess-plan parallel к existing custom
В fhir-careplan-builder.ts:164, расширить existing custom bloodgpt.com/careplan-category parallel’но добавить standard:
category: [
{
coding: [
{
system: `${BLOODGPT_SYSTEM}/careplan-category`,
code: "follow-up-recommendations",
display: "Follow-up Recommendations",
},
{
system: "http://terminology.hl7.org/CodeSystem/care-plan-category",
code: "assess-plan",
display: "Assessment Plan",
},
],
},
],Custom system остаётся (clear-cut «это наш AI-flow»), но standard рядом для interoperability и search filterability.
Что НЕ делаем в v1
- DiagnosticReport — оставляем
LABonly. Расширение требуетdocument_typeextraction от LLM (см.[[uploaded-document-types-supported]]) — это отдельная задача. - Immunization builder — narrative LLM иногда возвращает
content_type: immunization, но dedicated Immunization resource builder не существует. Контент сейчас попадает в Procedure/Observation. Отдельная задача (новый builder + schema). Не критично для v1. - Расширять Observation enum до 9 кодов R4 —
therapyиactivityв LLM enum не extracted. Не блокер для v1, но в будущем стоит расширить если появятся reference cases. health-concerncategory для Condition (US-Core) — для self-reported concerns. Не v1; зависит от того, появится ли patient-driven data entry workflow.
Следствия
- 4 файла нужно тронуть:
medication-statement.ts,procedure.ts(+ schema),fhir-composition-builder.ts,fhir-careplan-builder.ts. - LLM схема
ExtractedProcedureSchemaрасширяется новым enum field — нужен update промптаnarrative_to_entitiesчтобы LLM возвращал классификацию. - Иконка-mapping таблица для timeline берёт
categoryкоды как ключи. Mapping table (отдельная UX задача) должна покрыть:laboratory,vital-signs,imaging,social-history,survey,exam,procedure(Observation);food/medication/environment/biologic(AllergyIntolerance);problem-list-item(Condition);LAB(DR);patientspecified(MS);surgical/diagnostic/counselling(Procedure);assess-plan(Composition / CarePlan). - FHIR search через
?category=...для clinical history queries будет работать на 5 ранее-не-фильтрабельных ресурсах (MS / Procedure / Composition / CarePlan / DR).
Открытые вопросы
- Procedure LLM-классификация надёжная? — surgical vs diagnostic line иногда размыта (например биопсия — surgical с diagnostic intent). Может потребоваться fallback на «other» с дополнительным SNOMED через
code.coding[]. - MedicationStatement default
patientspecifiedvscommunity— если в будущем появится separate pharmacy integration где medications приходят по официальному каналу, нужно будет различать. Решим тогда. - Immunization builder timeline — когда добавлять? Зависит от того, насколько часто immunization data появляется в narrative.
Связано
[[../domain/fhir-resource-categories]]— survey стандартных ValueSets + audit-таблица + gaps (источник для этого решения)[[../domain/fhir-medication-statement]]— entity-page с описанием gap[[../domain/fhir-procedure]]— entity-page с описанием gap[[../domain/fhir-composition]]— entity-page; gap в top-level category[[../domain/fhir-careplan]]— entity-page; custom-only system gap[[timeline-page-design]]— потребитель этого решения[[uploaded-document-types-supported]]— связан с DR.category расширением