Опросник пациента для сбора medical context (диагнозы, лекарства, аллергии, симптомы, семейный анамнез, образ жизни). Реализован как multi-turn Mastra-агент в TypeScript (BG-1050, портирован из Python в session 7d777edb, 2026-03-25). Подключён к patient-portal на странице /dashboard через /api/survey route.
Использование в BloodGPT
- Источник patient-reported данных для FHIR — наряду с health-chat и document-import. См. health-report-vision (Level 3 multi-source).
- MedicalProfile output (текущее) — JSON свободного формата. Поля сгруппированы по доменам (medications, conditions, allergies, lifestyle).
- Будущий FHIR output — после bridge-step (LLM нормализация → 5 smart write tools
recordSymptom/recordMedication/recordAllergy/recordProcedure/recordFamilyHistory). Реализация V1.5. См. open question ниже. - Substitute channel для отфильтрованных полей — после Apr 27 heads sync (patient-content-filter) пациентская версия Health Status не показывает diagnoses / conditions / medications из FHIR. Опросник остаётся каналом, через который пациент сам сообщает эту информацию для интерпретации, при этом она не отображается обратно как «факт из медкарты».
Архитектура (multi-turn Mastra agent)
Не stateless wrapper и не generateObject. Каждый ход:
- Static prompt в
instructions— language requirement, update mode rules, mandatory checklist, sub-question hints, fatigue/digestive example options - History через messages — Mastra-агент держит conversation как массив; не template-injection в prompt
presentNextSteptool — единственный output mechanism. Агент вызывает с{question: "...", choiceConfig: {options: [...]}}. Frontend рендерит кнопки изoptions. В Mastra Studio тестируется через текстовый чат + JSON tool call.
patient enters dashboard → /api/survey?action=start
↓
Mastra agent
├─ instructions (static)
└─ messages (history grows)
↓
presentNextStep tool call
↓
{ question, choiceConfig: { options[] } }
↓
frontend → buttons
↓
patient selects → /api/survey?action=answer
↓
(loop until done)
Ключевые design decisions (из 7d777edb)
Multi-turn agent, не stateless
Ильдар: «ДА! я и говорю что это агент по сути сделанный специфично»
Initial Claude proposal: stateless wrapper с template variables ({language}, {history}, {gathered_data_json}). Ильдар отверг — конверсация natural-fit для multi-turn agent через messages. История пациента растёт через conversation-state, не через prompt injection.
Запись неуверенных упоминаний
Ильдар: «откуда статус может быть confirmed?» → решено через инструкции в промпте
Если пациент говорит “врач что-то про сахар упоминал” — записывается close-to-text:
{
"item": "Возможно повышенный сахар",
"details": "со слов пациента: врач упоминал, точный диагноз неизвестен"
}Не теряем информацию через “не додумывай”; не добавляем verificationStatus поле (всё patient-reported по определению — confirmed бессмысленен). Решение на уровне промпта, не схемы.
presentNextStep как единственный UI-pattern
Не “каждый вариант ответа = отдельный tool” (вырывает control-flow из агента). Не ask_user (это фича Claude Code, не Mastra). presentNextStep со structured choiceConfig.options — frontend рендерит UI, агент содержит логику вопросов.
Восстановленный full prompt (~original)
Initial port был ~120 строк (50% от оригинала). Ильдар провёл gap analysis, попросил восстановить:
- Language requirement (LLM забывают переводить options)
- Update Mode фразы-примеры
- Mandatory Checklist
- Sub-questions для fatigue/digestive
- Field mapping rules
Ильдар: «давай подумаем что вернуть»
Накопленные знания из итераций с реальными пациентами — эти детали не упрощать.
История 3 опросников в проекте
В session 7d777edb раскопали:
| Опросник | Автор | Период | Архитектура | Статус |
|---|---|---|---|---|
doctor_visit_preparation | Артур | лето 2025 | Planner + Executor (двухагентная), гипотезы по анализам | Не портирован |
diet_plan_generation | Артур | лето 2025 | Один агент | Не портирован |
medical_context | Никита | декабрь 2025 | Один агент, Q1-Q9 | Портирован в BG-1050 |
doctor_visit_preparation — сложнее (динамические вопросы, two-agent), отдельная фича. Не в скоупе текущего портирования.
Gotchas
reasoning_effort: "low"для cascade fallback на gpt-5.2 — экспериментальный параметр, не финализирован.- Languages: тестировался Russian + tested-paths. Hungarian, English — TODO. Существует риск что LLM забывает переводить
choiceConfig.optionsбез явной language reminder в каждом prompt-step. - Returning user mode в промпте есть (Update Mode rules), но не протестирован — нужен FHIR read для “что уже знаем о пациенте”.
gpt-5.2для всех Mastra-агентов сейчас (см. loinc про production model config — там Gemini для production LOINC; для survey-agent только gpt-5.2 пока).
Open question — Survey → FHIR bridge (V1.5)
SurveyAgent сейчас собирает MedicalProfile JSON (свободный формат, поля сгруппированы по доменам — medications, conditions, allergies, lifestyle). FHIR-output — V1.5, через bridge-step:
SurveyAgent (V0.5) Bridge step (V1.5, TBD) Mastra write tools (V0.5 уже есть)
───────────────── ─────────────────────── ──────────────────────────────────
MedicalProfile JSON → LLM нормализация → forEach(record):
{ (term → English, role recordSymptom(...)
conditions: [...], → which tool to call) recordMedication(...)
medications: [...], recordAllergy(...)
allergies: [...], recordProcedure(...)
lifestyle: {...} recordFamilyHistory(...)
}
Открытые design-вопросы для bridge:
- Где живёт bridge step — отдельный LLM call после survey complete? Внутри SurveyAgent как последний step? Отдельная background job?
- Granularity нормализации — одна LLM call на весь MedicalProfile JSON (batch translate + classify) vs per-entity calls?
- Error handling — что делать если term не нормализуется в SNOMED? Сохранять text-only с
meta.tagили skip? - Reconciliation — если у пациента уже есть Condition с тем же кодом из chat / previous survey — update existing или create new? См. clinical-record-reconciliation.
- Idempotency survey re-runs — пациент проходит survey второй раз; bridge step должен detect уже existing FHIR resources и не дублировать.
Эти решения зависят от того что мы будем наблюдать на реальных пациентских проходах survey — отложены до V1.5.
Связано
- health-report-vision — Level 3 (multi-source events): survey как один из триггеров для snapshot
- my-health — поверхность пациента, для которой опросник работает substitute-каналом
- patient-content-filter — почему пациенту нужен substitute-канал (regulatory)
- fhir-meta-tagging — survey-output идёт с
meta.tag source: surveyдля filtering - llm-numeric-codes-policy — почему LLM в survey возвращает English term, не SNOMED код напрямую
- clinical-record-reconciliation — что делать когда пациент повторно сообщает то же condition
- agent-vs-workflow — survey-agent — case где decomposed pattern НЕ подходит (open-ended conversation требует agentic loop)
- fhir-provenance — будущий source attribution survey-данных
- fhir-organization —
Organization/bloodgptкак author для survey-generated FHIR
Источники
Источники: 1.
Сноски
-
Linear BG-1050, accessed 2026-05-17, https://linear.app/realai/issue/BG-1050 — issue tracking. ↩