Подвопрос внутри staged-output-fhir-storage и foundcontext-fhir-mapping: каждый item в foundContext[] (а возможно и в referenceContext[] / missingContext[]) описывает связь между anchor-параметром и preexisting patient-ресурсом. У этой связи есть тип ресурса (Observation/Condition/Medication — уже есть) и roleзачем этот ресурс упомянут (объясняет / подтверждает / искажает / контекстуализирует). Role в текущей schema нет; выводится косвенно по тому, в каком массиве item лежит. Вопрос — добавлять ли её explicit, и в каком виде.

Контекст

V2EvidenceItemOutput (определение в packages/analysis-core/src/types/parameter-analysis.types.ts:192) сейчас несёт type, name, rationale, quotes, source, personalizedRationale, unmatched. Неявно роль определена тем, в какой из массивов item попал (foundContext = что мы нашли релевантного, missingContext = чего не хватает, referenceContext = справочный материал). Но это типы массивов, а не типы связей — внутри foundContext могут лежать item’ы с принципиально разным отношением к anchor:

  • Condition T2DM → объясняет ↑HbA1c (causal)
  • Observation Glucose-fasting ↑ → подтверждает гипергликемию (corroborative)
  • Condition Hemolytic-anemia → делает HbA1c ложно ↓ (confounding)
  • Medication Metformin → клинический фон, влияет на интерпретацию baseline (contextual)

Все четыре — foundContext, но семантика связи разная, и эта разница информативна для downstream consumers (Writer’у — для prose-сборки; Doctor-validation — для корректной аргументации; FHIR-querier — для фильтрации).

Вопрос имеет два уровня:

  1. Architectural seam: где живёт ответственность за кодирование роли — в Reasoner (выходит из LLM с role) vs deterministic boundary (FHIR-builder выводит из контекста) vs не кодируем вовсе.
  2. Shape: если кодируем — какой набор values, единый или per resource-type, экстендируемый или закрытый.

Позиции

A. Не кодируем (status quo)

Role не добавляется. Семантика связи восстанавливается consumer’ом из контекста (массив + тип ресурса + content rationale).

За: schema не растёт, Reasoner не нужно учить новой ответственности. Меньше chance enum-collision при новых типах ресурсов.

Против: теряется sharp семантическая разметка — Writer, Doctor-validation и партнёры (см. foundcontext-fhir-mapping) вынуждены парсить role из prose rationale. Эта потеря уже наблюдается: builder пишет всё в плоский finding[] без различения, Writer’у на чтении приходится восстанавливать. См. open в staged-output-fhir-storage про per-test+one-extension вариант где это особенно остро.

B. Role generated by Reasoner (single enum, unified)

V2EvidenceItemOutput получает поле role: "explains" | "corroborates" | "confounds" | "contextualizes". Reasoner присваивает при генерации.

За: role появляется на этапе где есть полный контекст для суждения (LLM видит anchor + ресурс + interpretation). FHIR-builder mechanically мапит в native slots или extension. Один enum — простой парсер.

Против: не все 4 значения одинаково осмысленны для всех типов ресурсов (см. ось ниже). Нет выделенных значений для preanalytical / baseline / longitudinal-comparison кейсов — может потребовать расширения, но extension enum-а в schema = breaking change consumers.

C. Role generated by Reasoner (per-type valid set)

Тот же подход что B, но валидный набор role зависит от type:

  • type: "observation"corroborates | confounds | contextualizes (observation редко “explains” anchor — она скорее independent measurement)
  • type: "condition"explains | confounds | contextualizes (condition объясняет наблюдение или искажает интерпретацию)
  • type: "medication"explains | confounds | contextualizes (medication объясняет рост/падение или вносит preanalytical bias)

За: schema эксплицитно отражает то что LLM и так делает — некоторые комбинации семантически невалидны и discriminated union в schema их отрезает. Делает invalid states unrepresentable.

Против: complex schema (3 sub-shapes вместо одной), parser/builder сложнее. Нужно тестирование per-type.

D. Role derived at FHIR boundary

Reasoner не присваивает role. FHIR-builder при сборке CI деривирует role из других полей и контекста (тип ресурса, направление biological effect, presence в anchor’s biomarker chain). Role появляется как Reference-level extension bloodgpt-edge-role на конкретной References внутри slot’а.

За: Reasoner schema не меняется (B/C — менется). LLM-output остаётся минимальным. Один централизованный builder отвечает за derivation.

Против: derivation может быть невозможен без доступа к LLM-знанию (например, “Metformin contextualizes vs explains” зависит от того, как препарат влияет на anchor — это medical knowledge, не deterministic mapping). Boundary derivation работает только для cases где есть structural signal.

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

  • Хватает ли 4 значений? В chain-discussion (1021e0fe, 2026-05-05) обсуждалось что baseline и preanalytical не покрываются 4-enum’ом. Если добавлять 5-й и 6-й — какие именно, и есть ли клинический use-case где они различимы для Writer/Doctor.
  • Per-type vs unified set (различает B и C). Зависит от того, можно ли сформулировать смысл explains/corroborates/confounds/contextualizes так чтобы все 4 были валидны для всех типов. Если “Observation explains” принципиально не имеет смысла, C обоснован; если просто маловероятен — B проще.
  • Где живёт mapping role → FHIR slot. В варианте B/C спред по native slots problem/supportingInfo/investigation (см. foundcontext-fhir-mapping) может быть выведен из role напрямую: confoundssupportingInfo (background, нарушает измерение), explainsproblem (relevant existing condition), corroboratesinvestigation.item (concurrent measurement). Это упрощает builder, но связывает решения двух страниц.
  • Backward compatibility evidence-without-role. Если schema добавляет role как required, ранее сохранённые items без role становятся invalid. Migration path — сделать role optional с defaultRule (например unknown), либо backfill через replay Reasoner’а на исторических analyses.
  • LLM reliability на role-assignment. Известно что LLM путаются на разделении explains vs contextualizes (causal vs background). Нужен eval на размеченном датасете до того как схема пойдёт в prod.

Что нужно для разрешения

  • Решение по foundcontext-fhir-mapping (positions A vs B). Если spread-по-natives — role на edge даёт чёткий mapping в slot, и B/C становятся почти обязательны. Если всё в supportingInfo[] — role можно положить как extension без structural impact, варианты B/C/D одинаково применимы.
  • Demand сигнал от Writer / Doctor-validation pipeline: реально ли downstream использует role различение, или достаточно prose rationale. Если не использует — A разумен.
  • Eval LLM на размеченном датасете для понимания насколько reliable role-assignment Reasoner’ом.

Следствия

  • Если выбираем B/C — bloodgpt-evidence-item extension получает поле role, parser/builder обновляются, Reasoner prompt explicitly требует role-классификацию.
  • Если выбираем D — пишется otherwise-thin bloodgpt-edge-role extension на References, builder централизованно деривирует, Reasoner не трогается.
  • Если A — фиксируем явно в этой странице как “не кодируем — обоснование…“. Текущая прозрачность zero, парсер должен учитывать что role implicit.

Связано

Источники