[bot][A] Движок диалога: конвейер scrub → safety → FSM → мониторинг, согласие, возраст, кризис #10

Closed
opened 2026-10-03 14:38:54 +03:00 by Eleboba · 0 comments
Owner

Описание

Ядро диалога, общее для всех трёх направлений. Реализует DialogEngine из app/dialog/contracts.py (ADR-004). Поток SPEC §3: scrub() → safety.check() (+ risk_classify как доп. сигнал) → FSM по состоянию → сохранение (app.db.repo) → MonitoringService.after_data_change() → исполнение диалоговых действий.

Желаемый результат

  • app/dialog/engine.py: Engine.handle(token, event); состояние в dialog_state (repo); сообщения в messages только scrubbed.
  • Каркас FSM, расширяемый сценариями (регистрация шагов по модулям), чтобы ветки ветерана / семьи / депрессии добавлялись отдельными файлами app/dialog/flows/<module>.py без правки движка.
  • Состояния ядра: new → consent → age_gate → (flow модуля) → idle, crisis (выход только после ack врача — флаг из repo), consent_revoked (бот замолкает, данные помечены к удалению).
  • Возраст < 18 → детский телефон доверия, без обработки.
  • ACUTE → safety.crisis_reply() дословно, LLM не вызывается, повторно скрипт в том же кризисе не шлётся; POSSIBLE → cssrs_screen.
  • Исполнение диалоговых Action (schedule_scale, med_info, reach_out, show_safety_plan, suggest_visit) → реплики.
  • Тесты tests/test_dialog_engine.py: согласие, отказ, < 18, кризис без LLM, possible → C-SSRS, повторный кризис.

Зона и владение файлами

Lane A (бот, Егор) — владеет: app/dialog/**, app/channels/**, app/llm/**, config/texts/**, tests/test_dialog_*.py, tests/test_telegram*.py.

Замороженные контракты (менять только отдельным мелким PR с пингом всех): app/core/types.py, app/dialog/contracts.py, app/api/schemas.py, app/integrations/base.py, app/services/contracts.py, app/main.py, app/config.py, config/modules.yaml, config/rules.yaml, config/scales/*, pyproject.toml/uv.lock, docker-compose.yml, Makefile.


Лог выполнения

2026-10-03

2026-10-03

Что сделано

  • app/dialog/engine.py: конвейер scrub → safety (сначала лексикон, при ACUTE LLM не вызывается) → FSM → сохранение → after_data_change → диалоговые действия; register_engine(Engine).
  • Каркас FSM app/dialog/fsm.py (@state, @on_enter, @trigger), автозагрузка app/dialog/flows/*; flows/core.py: new → consent → age_gate → <module>.onboarding.start → idle, crisis, consent_revoked, minor, refused.
  • app/dialog/scale_session.py — переиспользуемая сессия шкалы; app/dialog/actions.py — исполнение действий мониторинга; app/dialog/crisis.py.
  • config/texts/dialog.yaml — согласие (152-ФЗ), возраст, меню, кризис, план безопасности (черновик, утверждает психиатр #8).
  • 23 теста в tests/test_dialog_engine.py.

Результат

  • get_engine() возвращает настоящий движок; make lint test зелёные.

Заметки/решения

  • Без согласия (до него, после отказа или отзыва, для несовершеннолетних) LLM не вызывается и ничего не сохраняется; на острый кризис бот всё равно отвечает, но без фразы «врач уведомлён».
  • Если БД недоступна, движок отвечает текстом о временной недоступности, а на кризисное сообщение — кризисным скриптом без фразы «врач уведомлён».
  • Пока работает NoopMonitoring, выйти из crisis можно только по doctor_ack.
  • Для lane B (#38) в dialog_state.data пишутся факты safety_events, crisis, contact_requests, delete_requested; формат описан в PR.
  • Исходящие сообщения перед сохранением тоже проходят через scrub (план безопасности может содержать имена).
## Описание Ядро диалога, общее для всех трёх направлений. Реализует `DialogEngine` из `app/dialog/contracts.py` (ADR-004). Поток SPEC §3: `scrub()` → `safety.check()` (+ `risk_classify` как доп. сигнал) → FSM по состоянию → сохранение (`app.db.repo`) → `MonitoringService.after_data_change()` → исполнение диалоговых действий. ## Желаемый результат - `app/dialog/engine.py`: `Engine.handle(token, event)`; состояние в `dialog_state` (repo); сообщения в `messages` только scrubbed. - Каркас FSM, расширяемый сценариями (регистрация шагов по модулям), чтобы ветки ветерана / семьи / депрессии добавлялись отдельными файлами `app/dialog/flows/<module>.py` без правки движка. - Состояния ядра: `new → consent → age_gate → (flow модуля) → idle`, `crisis` (выход только после ack врача — флаг из repo), `consent_revoked` (бот замолкает, данные помечены к удалению). - Возраст < 18 → детский телефон доверия, без обработки. - `ACUTE` → `safety.crisis_reply()` дословно, LLM не вызывается, повторно скрипт в том же кризисе не шлётся; `POSSIBLE` → `cssrs_screen`. - Исполнение диалоговых `Action` (schedule_scale, med_info, reach_out, show_safety_plan, suggest_visit) → реплики. - Тесты `tests/test_dialog_engine.py`: согласие, отказ, < 18, кризис без LLM, possible → C-SSRS, повторный кризис. ## Зона и владение файлами **Lane A (бот, Егор)** — владеет: `app/dialog/**`, `app/channels/**`, `app/llm/**`, `config/texts/**`, `tests/test_dialog_*.py`, `tests/test_telegram*.py`. **Замороженные контракты** (менять только отдельным мелким PR с пингом всех): `app/core/types.py`, `app/dialog/contracts.py`, `app/api/schemas.py`, `app/integrations/base.py`, `app/services/contracts.py`, `app/main.py`, `app/config.py`, `config/modules.yaml`, `config/rules.yaml`, `config/scales/*`, `pyproject.toml`/`uv.lock`, `docker-compose.yml`, `Makefile`. --- ## Лог выполнения ### 2026-10-03 ### 2026-10-03 **Что сделано** - `app/dialog/engine.py`: конвейер scrub → safety (сначала лексикон, при ACUTE LLM не вызывается) → FSM → сохранение → `after_data_change` → диалоговые действия; `register_engine(Engine)`. - Каркас FSM `app/dialog/fsm.py` (`@state`, `@on_enter`, `@trigger`), автозагрузка `app/dialog/flows/*`; `flows/core.py`: new → consent → age_gate → `<module>.onboarding.start` → idle, crisis, consent_revoked, minor, refused. - `app/dialog/scale_session.py` — переиспользуемая сессия шкалы; `app/dialog/actions.py` — исполнение действий мониторинга; `app/dialog/crisis.py`. - `config/texts/dialog.yaml` — согласие (152-ФЗ), возраст, меню, кризис, план безопасности (черновик, утверждает психиатр #8). - 23 теста в `tests/test_dialog_engine.py`. **Результат** - `get_engine()` возвращает настоящий движок; `make lint test` зелёные. **Заметки/решения** - Без согласия (до него, после отказа или отзыва, для несовершеннолетних) LLM не вызывается и ничего не сохраняется; на острый кризис бот всё равно отвечает, но без фразы «врач уведомлён». - Если БД недоступна, движок отвечает текстом о временной недоступности, а на кризисное сообщение — кризисным скриптом без фразы «врач уведомлён». - Пока работает `NoopMonitoring`, выйти из `crisis` можно только по `doctor_ack`. - Для lane B (#38) в `dialog_state.data` пишутся факты `safety_events`, `crisis`, `contact_requests`, `delete_requested`; формат описан в PR. - Исходящие сообщения перед сохранением тоже проходят через scrub (план безопасности может содержать имена).
Eleboba added this to the Этап 1 · Ветераны СВО milestone 2026-10-03 14:38:54 +03:00
Eleboba added the P0safetysize/2botfeature labels 2026-10-03 14:38:54 +03:00
Eleboba self-assigned this 2026-10-03 14:38:54 +03:00
Eleboba added a new dependency 2026-10-03 14:38:59 +03:00
Eleboba changed title from [bot] Диалоговый движок (FSM, канал-независимый): согласие → скрининг/триаж → чек-ины → шкалы → кризис to [bot][A] Движок диалога: конвейер scrub → safety → FSM → мониторинг, согласие, возраст, кризис 2026-10-03 15:16:25 +03:00
Eleboba added the lane/Astage/veterans labels 2026-10-03 15:16:26 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Blocks Depends on
Reference: MedITron/PTSRaiAgent#10