Files
PTSRaiAgent/docs/DECISIONS.md
T
Arxip222andClaude Opus 5.5 290b0d6151 feat(web): two-stage questionnaires, screening result and diary by result
- Sign-in: centred ЕМИАС card without the sidebar; anonymous entry asks «Депрессия / ПТСР».
- Stage 1 is the same for everyone and follows the fixed order of the MVP questionnaire:
  PHQ-2 → trauma filter/TSQ → RUS-AUDIT-S → C-SSRS → context; stage 2 only where stage 1
  is positive. Starts without a profile.
- After stage 1 the patient sees a preliminary result (screening, not a diagnosis), stored
  with the record; it switches on the diary modules (depression / PTSD).
- Stage 2 diary: morning (sleep, sleep quality, alcohol) and evening (mood, medication,
  3 PHQ-9 items in rotation, short PCL-5); C-SSRS since last contact only under risk;
  flags on weekly means against the personal norm; scheduled full scales with change
  (remission, response, worsening).
- Questionnaire log: GET /me/{token}/results and the «Мои результаты» screen.
- Questionnaires centred without the sidebar and without scale names; «Продолжить позже»
  goes home; confirmations are toasts; free-text comments are optional.
- Demo policy config/questionnaire_demo.yaml (INTAKE_DEMO only, not a clinical approval).

ADR-012.

Refs #77

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:14:09 +03:00

67 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Архитектурные решения (ADR)
> Живой журнал: **что** решили и **почему**. Новые записи — сверху. Одна запись = одно решение.
> При конфликте с [`docs/info/SPEC.md`](info/SPEC.md) действует ADR.
## 2026-10-04 — ADR-012: Два этапа опросов — общий скрининг, предварительный результат пациенту, дневник по результату
**Контекст:** PDF «Опросники для MVP» (этап 1) и «Этап 2: наблюдение после диагноза». Пациент должен видеть, что у него всё в порядке или есть признаки депрессии/ПТСР, и дальше вести дневник под свой результат. Клиническая политика (`config/questionnaire.yaml`) закрыта до решений врача C01–C09, поэтому в демо опрос всегда заканчивался остановкой «нужна оценка специалиста».
**Решение:** этап 1 общий для всех и идёт в фиксированном порядке документа: PHQ-2 → фильтр травмы/TSQ → RUS-AUDIT-S → C-SSRS → общие вопросы, затем этап 2 только там, где этап 1 положительный. Пункты внутри шкал не переставляются. Стоп-правила риска срабатывают после каждого ответа. После этапа 1 правила (`app/core/interpretation.py`, тексты в `config/texts/results.yaml`) показывают пациенту предварительный результат: «не выявлено», «предположение депрессии» (PHQ-9 ≥10), «склонность к ПТСР» (PCL-5 ≥31), алкоголь, ЧМТ, маршрут. Это всегда «результат скрининга, не диагноз». Результат включает модули дневника: `depression` и `veteran` (ПТСР). Без врача их определяет скрининг, модули врача сохраняются и дополняются. Дневник этапа 2 состоит из двух частей: утро (часы сна, качество сна, порции алкоголя) и вечер (настроение, лекарства + модули: 3 пункта PHQ-9 в день по кругу, короткая PCL-5 из 4 пунктов). C-SSRS «с последнего контакта» задаётся каждый вечер только при суицидальном риске. Флаги считаются по недельным средним против личной нормы за первые 14 дней и выносят полную шкалу раньше срока. Каждая запись хранит интерпретацию, показанную при завершении; журнал отдаёт `/me/{token}/results`. Демо (`INTAKE_DEMO=1` + демо-часы) читает отдельный `config/questionnaire_demo.yaml`, помеченный «не клиническое согласование», а реальная политика не меняется.
**Причина:** пациенту нужен понятный итог и путь дальше, а не тупик; решения о риске и маршруте при этом остаются детерминированными (ADR-007).
**Последствия:** отступления от присланного PDF сохранены по `docs/clinical/README.md`: TSQ вместо PC-PTSD-5 (v2 PDF), RUS-AUDIT-S — пункты 3/9/10 (ВОЗ). Открыто: номера 4 пунктов короткой PCL-5 (Price et al., 2016), официальная форма C-SSRS Since Last Contact (сейчас — тексты Recent с периодом «с последнего ответа»), пороги «роста» модулей дневника — согласовать с врачом.
## 2026-10-04 — ADR-011: База знаний (RAG) — лексический поиск BM25 и экстрактивные ответы с цитатами
**Контекст:** агенту нужны ответы по клиническим рекомендациям (КР ПТСР 2023, КР Депрессия 2024) со ссылкой на источник (#60). Эмбеддинги требуют либо сети (GigaChat Embeddings), либо тяжёлых зависимостей (torch, модель на сотни МБ). При этом демо должно работать офлайн (ADR-004), а `pyproject.toml` заморожен (#34).
**Решение:** PDF переводятся в txt один раз, офлайн (`docs/source/rag/pdf_to_txt.py`: таблицы → markdown, маркеры страниц, текст блок-схем перенесён вручную). Корпус лежит в `config/rag/`. Поиск — BM25 по основам слов (свой стеммер Snowball) с синонимами и переранжированием по самому плотному отрывку. Ответ **экстрактивный**: показываются только предложения из найденного фрагмента с цитатой «[КР ПТСР 2023, стр. N]». LLM может лишь упростить их для пациента, а перефразировка с новым содержанием отбраковывается. Если отрывки покрывают слишком малую долю вопроса (`min_coverage`), ответ — «ответа нет, уточните у врача». Пациенту не показываются предложения о назначении и дозах. Внутренние документы команды (`audiences: [team]`) не доходят ни до пациента, ни до врача.
**Причина:** работоспособность офлайн и детерминизм важнее recall на перефразированных вопросах. Каждое показанное утверждение можно проверить по странице КР.
**Последствия:** разговорные формулировки без общих слов с КР находятся хуже — это лечится словарём `synonyms` в `config/rag/sources.yaml`. Качество поиска контролирует набор контрольных вопросов в `tests/test_llm_rag.py` (hit@3 ≥ 15/18). Гибридный поиск с эмбеддингами можно добавить за тем же интерфейсом `BM25Index.search`.
## 2026-10-03 — ADR-010: Фокус — ветераны СВО; разработчики делят слои, а не направления
**Контекст:** три направления (ветераны, семьи, общая популяция с депрессией) и три разработчика. Если каждый ведёт своё направление, все трое правят одни и те же файлы: движок, API, дашборд.
**Решение:** на хакатоне делаем только ветеранов (эпик #35); семьи (#40) и общая популяция (#43) — опциональные направления на будущее, без срока. Разработчики делят **слои**: A — бот (диалог, каналы, LLM), B — backend (API, сервисы, шедулер, адаптеры, БД, ядро), C — дашборд. Между слоями — замороженные контракты (#34) с заглушками, поэтому все трое стартуют одновременно. Направления подключаются как `app/dialog/flows/<module>.py` + `config/modules.yaml`.
**Причина:** ноль коллизий по файлам при параллельной работе; общее ядро переиспользуется направлениями, как и задумано в ADR-001. Главный герой демо — Андрей (решение #9).
**Последствия:** изменения контрактов — только мелкими PR `[contract]` с пингом всех. Владение файлами — в `docs/dev/WORKFLOW.md`.
## 2026-10-03 — ADR-009: Процесс — регламент TenVex в облегчённом режиме хакатона
**Контекст:** 3 дня, сдача в пн 05.10 в 13:00 МСК. Два разработчика и Claude Code-агенты работают параллельно. Опыт TenVex/vobot: без issue и изоляции агенты конфликтуют.
**Решение:** берём ядро регламента vobot: задача = issue, одна зона на issue, worktree + ветка от `origin/dev`, PR в `dev`, Conventional Commits, лог в теле issue, ADR сюда. Облегчения: PR в `dev` можно мержить самому после зелёного `make lint test`, без обязательного ревью; contract-first/orval нет; `main` = сдаваемая версия, промоушн `dev → main` делает человек.
**Последствия:** регламент — [`docs/dev/WORKFLOW.md`](dev/WORKFLOW.md).
## 2026-10-03 — ADR-008: Конфиги в `config/`, данные в `data/`
**Контекст:** требования к оформлению: «конфигурационные файлы — в отдельной папке». В SPEC пороги лежали в `data/scales`.
**Решение:** `config/` — шкалы (пункты, диапазоны, градации), расписания модулей, правила алертов, лексикон риска, тексты для пациента (YAML, вычитывает психиатр). `data/` — синтетика, выдержки ГРЛС, eval-разметка.
**Причина:** соответствие чек-листу жюри; психиатр правит только `config/`, не трогая код.
## 2026-10-03 — ADR-007: «Действия агента» — это выход движка правил, а не tool-calling LLM
**Контекст:** в демо-сценарии действия записаны как вызовы инструментов (`raise_alert`, `notify_doctor`, `crisis_protocol`, `book_visit`). Это выглядит так, будто их решает LLM, а это противоречит правилу «алерты детерминированы».
**Решение:** правила (`config/rules.yaml`) по структурированным данным возвращают список действий; оркестратор их исполняет. LLM только формулирует реплику вокруг уже принятого действия.
**Причина:** юридическая атрибуция ответственности: пороги задаёт врач, ИИ решений не принимает. Это ключевой аргумент для жюри и для специалистов.
## 2026-10-03 — ADR-006: Цвета в UI ↔ уровни алертов
**Решение:** 🔴 = `acute` (кризис, п.9 ≥ 1, C-SSRS с намерением/планом, доступ к оружию при риске); 🟡 = `high` + `moderate` (врачу ≤ 24 ч / флаг к визиту); 🟢 = нет открытых алертов; `info` (молчание, нет ответа) — серый флаг. В БД хранится уровень, цвет вычисляется.
## 2026-10-03 — ADR-005: Демо-часы (виртуальное время)
**Контекст:** демо — 13 дней терапии за 3 минуты. Шедулер на реальном времени это показать не даст.
**Решение:** всё время в системе берётся из `app.core.clock` (реальное или виртуальное, `DEMO_CLOCK=true`). В демо-консоли есть кнопка «следующий день». Шедулер срабатывает по сдвигу часов. Правила считают окна (3 дня подряд, 7 дней) от виртуального «сейчас».
**Последствия:** нигде не вызывать `datetime.now()` напрямую. Проверяется в ревью.
## 2026-10-03 — ADR-004: Канал-независимый диалоговый движок + веб-демо-консоль + офлайн-режим
**Контекст:** решение должно запускаться на технике организатора без нестандартного ПО. Telegram и GigaChat на площадке могут быть недоступны.
**Решение:** диалог — чистая функция `engine.handle(token, event) -> list[Reply]` поверх FSM. Адаптеры: Telegram (aiogram) и веб-чат в демо-консоли (Streamlit). LLM за протоколом с двумя провайдерами: `gigachat` и `mock` (детерминированные ответы для демо-реплик). `LLM_PROVIDER=mock` по умолчанию в `docker compose up`.
**Причина:** главный критерий жюри — работоспособность. Демо не должно зависеть от сети.
## 2026-10-03 — ADR-003: Один Postgres, две базы; без Redis и Alembic в MVP
**Контекст:** в SPEC два контейнера Postgres и Redis.
**Решение:** один сервис `postgres` с двумя базами (`identity`, `clinical`; `audit_log` лежит в `clinical`) и двумя ролями: роль клинического модуля не имеет прав на `identity`. Связь — HMAC-токен. Схема создаётся через `create_all` при старте. FSM-состояние хранится в таблице `dialog_state`.
**Причина:** изоляция сохраняется (разные БД и разные роли), а операционной сложности меньше. Alembic для трёхдневного прототипа — лишнее трение. В проде — отдельные кластеры и миграции.
## 2026-10-03 — ADR-002: Ежедневный чек-ин = настроение + приём + модульный вопрос; шкалы — по расписанию
**Контекст:** в SPEC ежедневно собираются 2–3 пункта PHQ-9 в 14-дневном окне. В демо-персоне и в модулях — ежедневно настроение 0–10, приём, сон, алкоголь, а шкалы целиком по расписанию (PHQ-9 еженедельно/раз в 2 недели) и внепланово по триггеру.
**Решение:** берём модель из модулей. Ежедневно: настроение 0–10, приём препарата, один модульный вопрос (сон/кошмары у ветерана, алкоголь при флаге AUDIT-C) и свободный текст (`extract` → побочки, намерение бросить, алкоголь). Полные шкалы — по расписанию модуля и внепланово (например, настроение ≤ 3 три дня → внеплановый PHQ-9). П.9 PHQ-9 — только кнопками.
**Последствия:** новая таблица `daily_checkins` (mood, med_taken, sleep_bucket, alcohol_bucket, flags).
## 2026-10-03 — ADR-001: Общее ядро + три модуля, включаемые скринингом
**Контекст:** три источника требований (SPEC, демо-персона, концепция/CJM) по-разному ставят фокус: ветераны, депрессия, семьи.
**Решение:** общее ядро (PHQ-9 → C-SSRS, ежедневный чек-ин, молчание как сигнал) и модули `veteran`, `depression`, `family` ([`CLINICAL_MODULES.md`](info/CLINICAL_MODULES.md)). Модули включает стартовый скрининг и/или врач при подключении пациента. Шкалы, расписания и триггеры модулей описаны декларативно в `config/modules.yaml`.
**Приоритет на хакатон:** ядро, `veteran` и `depression` — P0; `family` — P1 (вход «Я близкий», чек-лист, кнопка «сообщить врачу»).
**Кейс:** закрываем сценарии «мониторинг после обращения» и «управление приёмом лекарств». Скрининг/триаж — входная точка, а не отдельный третий сценарий.