Подвопрос внутри 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 — для фильтрации).
Вопрос имеет два уровня:
- Architectural seam: где живёт ответственность за кодирование роли — в Reasoner (выходит из LLM с
role) vs deterministic boundary (FHIR-builder выводит из контекста) vs не кодируем вовсе. - 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 напрямую:confounds→supportingInfo(background, нарушает измерение),explains→problem(relevant existing condition),corroborates→investigation.item(concurrent measurement). Это упрощает builder, но связывает решения двух страниц. - Backward compatibility evidence-without-role. Если schema добавляет
roleкак required, ранее сохранённые items без role становятся invalid. Migration path — сделатьroleoptional с defaultRule (напримерunknown), либо backfill через replay Reasoner’а на исторических analyses. - LLM reliability на role-assignment. Известно что LLM путаются на разделении
explainsvscontextualizes(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-itemextension получает полеrole, parser/builder обновляются, Reasoner prompt explicitly требует role-классификацию. - Если выбираем D — пишется otherwise-thin
bloodgpt-edge-roleextension на References, builder централизованно деривирует, Reasoner не трогается. - Если A — фиксируем явно в этой странице как “не кодируем — обоснование…“. Текущая прозрачность zero, парсер должен учитывать что role implicit.
Связано
- staged-output-fhir-storage — parent-вопрос (host/granularity/distribution), edge-role это часть оси 3 (distribution внутри CI)
- foundcontext-fhir-mapping — split-per-type vs unified slot; role-encoding влияет на mapping
- fhir-clinical-impression — какие native slots доступны и какая spec-семантика у каждого
- fhir-modeling-ai-content — общие принципы маппинга AI-output