diff --git a/vector_tasks.md b/vector_tasks.md index e1d0940..fa2ad27 100644 --- a/vector_tasks.md +++ b/vector_tasks.md @@ -661,9 +661,14 @@ sed -i 's/sar_mchs/vector_mchs/g; s#/root/sar-mchs#/root/vector#g' DEPLOY.md **Жёсткое правило:** ни ISRID, ни архив не дают права автоматически перезаписывать формулу весов — изменение коэффициентов проходит через ревью человека (см. B1 и «Жёсткие исключения» в начале файла). -B13 Структурирование проекта +## B13 — Структурирование «Вектора» в систему подсистем (видение-каркас) -1. Ядро «Вектора» +**Тип:** архитектура / реструктуризация, needs-context. Исключено из делегирования локальной модели: затрагивает контракты данных, схему БД, бизнес-логику SAR. + +**Цель:** ВЕКТОР — не один монолит, а система относительно независимых подсистем с чистыми границами. Упрощает разработку, тестирование и будущее подключение ATAK/Meshtastic. + +**Подсистемы:** +``` ВЕКТОР │ ├── Core @@ -716,33 +721,164 @@ B13 Структурирование проекта ├── вероятностная карта ├── группы └── статистика +``` -2. Технический +**Ключевые принципы (добавлены к изначальному скелету B13):** - PostgreSQL - │ - ├── PostGIS - │ - └── данные поисков +1. **Развязка движка.** Search Engine — чистая функция: исходные данные + данные поиска + параметры человека → `(ProbabilitySurface, SearchAreas, Recommendations)`. Он ничего не знает об ATAK, Meshtastic, веб-интерфейсе, БД, auth. Доставка (web/ATAK/Mesh) — отдельный слой, подписанный на результат. + +2. **Четыре слоя данных, не смешиваются:** + - **Слой 1 — исходные (неизменяемые):** OSM, DEM, гидрография, лес, дороги, здания, ISRID, исторические поиски. + - **Слой 2 — конкретный поиск:** исходная точка, время исчезновения, категория, возраст/пол/состояние, известный маршрут, погодные условия, доп. факторы. + - **Слой 3 — оперативные (во время операции):** Position, Observation, Track, Clue, Team, AreaChecked, WitnessReport, FoundPerson. + - **Слой 4 — результат модели:** ProbabilitySurface, SearchZone, Priority, Confidence, RecommendedRoute. **Результат не смешивается с исходными данными.** + +3. **Итеративная модель.** Не «Вектор один раз нарисовал круг», а постоянный пересчёт наиболее вероятного положения по мере поступления полевых данных: расчёт → поисковые группы → найдено/не найдено → новые данные → новый расчёт. Сюда же естественно ложатся ATAK + Meshtastic. + +4. **Очередь задач** для тяжёлых расчётов (terrain analysis, probability calculation, routing, recalculation) — сейчас `analyze_case` синхронно ждёт Overpass + Claude. + +**Технический контур:** +``` + PostgreSQL + │ + ├── PostGIS + │ + └── данные поисков + + ▲ + │ + ┌──────┴────────┐ + │ Vector Backend │ + ├────────────────┤ + │ Search Engine │ + │ GIS Engine │ + │ ISRID Engine │ + │ Routing │ + │ Event Engine │ + └──────┬─────────┘ + │ + ├── Web UI + ├── ATAK + └── Meshtastic +``` + +**Зависимости / блокеры:** +- **ISRID (B12) — внешний правовой блокер.** Итеративная модель (B18) строится на экспертных коэффициентах сейчас; ISRID подключается позже как источник априорных, когда вопрос правового статуса решён. +- **B1 (ядро скоринга) — коэффициенты под ревью человека.** Экстракция движка (B14) их НЕ меняет; любая калибровка — отдельно через B1. +- **Реал-тайм (B19) — фактически отдельный проект**, оформляется после ядра и Field Data. + +**Декомпозиция в фазы:** B14 → B15 → (B16, B17 параллельно) → B18 → B19. См. ниже. + +--- + +## B14 — Чистый SearchEngine (поведение-сохраняющий) +**Тип:** refactor / архитектура, needs-context. Фундамент всей развязки (B13-принцип 1). Низкий риск: поведение НЕ меняется, коэффициенты НЕ трогаются (B1-safe). + +**Что:** Вынести расчёт поисковой модели из `backend/routers/analyze.py` в отдельный модуль `services/search_engine.py` с чистой границей: +- `SearchInput` (pydantic): age, gender, terrain, weather, elapsed_hours, season, diagnosis_type, has_transport, psychotype_answers, profiles, lat/lon, loss_time, … — то, что сегодня собирается в `analyze_case` из payload + `db.get_case`. +- `SearchModel` (результат): max_distance_km, coefficients, time_of_day, psychotype (+modifiers +recommendations), weights, distance_multiplier, active_profiles, critical_warnings, urgency, primary_zones, search_radius_km, key_locations, behavioral_prediction, immediate_actions, summary, fallback_used. +- `build_search_model(input: SearchInput) -> SearchModel` — чистая функция, БЕЗ DB / auth / HTTP. Внутри вызывает `calculate_max_distance`, `claude_analyze`, `WeightedScorer`, `detect_psychotype` (вся логика из `analyze_case` переезжает сюда). +- `analyze.py::analyze_case` становится тонкой обёрткой: собрать `SearchInput` из payload + `db.get_case` → `build_search_model` → записать `analysis_log` → вернуть. Вся SAR-математика — в `search_engine.py`. + +**Границы (НЕ делать в B14):** +- НЕ менять коэффициенты (`distance_service`, `scoring_service`) — это B1. +- НЕ менять схему БД / миграции. +- НЕ добавлять Field Data / итеративность / PostGIS — это B15–B18. +- НЕ менять внешний контракт ответа `/analyze` (фронт B5 от него зависит) — состав полей тот же. + +**Критерий приёмки:** +- `analyze.py` не содержит расчётной логики (только сборка `SearchInput` + запись БД + возврат). +- Поведение `/analyze` идентично: bike 8yo 2h лес день → max_distance 5.4; РАС+bike → профили [РАС, велосипед]; psychotype-answers → detect. Регресс-кейсы из B5 проходят. +- `PYTHONPATH=/app python -m pytest backend/tests -q` — зелёный; добавлены юнит-тесты на `build_search_model` (моки на `claude_analyze`). + +--- + +## B15 — Четыре слоя данных: схема + миграция +**Тип:** schema / migration, needs-context. Исключено из делегирования: миграции БД. + +**Что:** Ввести слоистую модель данных (B13-принцип 2) через Alembic-миграцию: +- **Слой 2 (поиск):** `cases` — уже есть; оставить как карточку поиска (исходная точка, время, категория, параметры человека), вынеся поля других слоёв. +- **Слой 4 (результат):** `analysis_log` → явная таблица `search_models` (case_id, version, input_snapshot, model_json, created_at) с **версионированием** (несколько версий на случай — фундамент для B18). +- **Слой 3 (оперативные):** НОВЫЕ таблицы `field_observations` (position/observation/track/clue/witness), `search_teams`, `areas_checked`, `found_events` — пока схема + пустые + модели; ингестия в B17. +- **Слой 1 (исходные):** версионированный справочник `reference_priors` (source, category, value, units, provenance) — заглушка под B12 (ISRID) и экспертные значения; данные вносятся позже, сейчас структура. +- Бэкфилл существующих `cases`/`analysis_log` в новую схему без потери данных. +- API `/analyze` и `/cases` продолжают работать (адаптер поверх новой схемы). + +**Границы:** НЕ заполнять `reference_priors` (B12 заблокирован). НЕ реализовывать ингестию полевых данных (B17). Коэффициенты не трогать. + +**Критерий приёмки:** +- `alembic upgrade head` проходит чисто на пустой и на текущей БД; даун-миграция работает. +- Существующие кейсы и `analysis_log` доступны после миграции (бэкфилл проверен на тестовом наборе). +- `/analyze` и `/cases` отвечают тем же контрактом (фронт B5 не сломался). +- `pytest backend/tests` — зелёный. + +--- + +## B16 — GIS: PostGIS + локальный OSM (замена живого Overpass) +**Тип:** infra / architecture, needs-context. Крупная инфра-задача. + +**Мотив:** `services/geo_service.py` дёргает живой Overpass API (8 направлений × 4 дистанции = 32 запроса на анализ) — медленно и ненадёжно: при недоступности Overpass зоны получают одинаковые нейтральные оценки (см. тест B5 — все зоны с одинаковым скором). Локальный OSM в PostGIS даёт быстрые пространственные запросы, рельеф (DEM), гидрографию — фундамент для качественной вероятностной поверхности. + +**Что:** +- Подключить PostGIS к `vector-postgres` (расширение `postgis`, docker-compose). +- Импорт OSM-экстракта региона (Беларусь / округа) через `osm2pgsql` в таблицы PostGIS (roads, water, forest/wood, buildings, landuse). +- Переписать `geo_service.build_search_zones` на PostGIS-запросы (`ST_DWithin`, `ST_Intersects`, длина дорог в буфере, расстояние до ближайших water/settlement/forest) вместо Overpass. +- Опционально DEM / рельеф (уклон, проходимость) — можно вынести в B16b. +- Сохранить контракт `Zone` (forest_pct, road_density, water_distance_km, settlement_distance_km), чтобы `scoring_service` не менялся. + +**Границы:** Коэффициенты скоринга не трогать. DEM — опционально. ISRID — отдельно (B12). + +**Критерий приёмки:** +- `vector-postgres` имеет PostGIS; OSM-данные загружены. +- `build_search_zones(53.9, 27.56, case, 1.08)` отдаёт зоны с РЕАЛЬНЫМИ характеристиками (road_density/water/forest различаются между направлениями) без обращений во внешний интернет. +- Время анализа < 1с (без Overpass-латентности и без Claude fallback). +- `test_geo_service` адаптированы и зелёные. + +--- + +## B17 — Field Data: модель + ингестия операционных данных +**Тип:** new subsystem, needs-context. Зависит от B15 (оперативные таблицы). + +**Что:** Реализовать слой 3 (B13-принцип 2) — приём и хранение операционных данных во время поиска: +- Модели/таблицы: `field_observations` (тип: position/observation/track/clue/witness, geom, timestamp, team_id, confidence, raw), `search_teams` (id, name, members, role), `areas_checked` (geom-полигон, team_id, checked_at, result), `found_events`. +- API приёма: `POST /api/v1/cases/{id}/observations`, `…/teams`, `…/areas_checked`; `GET` для списка/карты. Auth: operator/field/admin. +- Валидация (тип наблюдения, геом в разумных пределах, таймстемп). +- События новых наблюдений — источник для B18 (перерасчёт). + +**Границы:** НЕ реализовывать сам пересчёт (B18). НЕ подключать ATAK/Meshtastic (B19) — только API-ингестия. Коэффициенты не трогать. + +**Критерий приёмки:** +- Создание/чтение наблюдений/групп/проверенных участков через API работает (тесты). +- Геометрии в PostGIS (если B16 готов) или в JSONB-заглушке (оговорить, если B16 ещё нет). +- Событие нового наблюдения попадает в очередь/лог для B18. + +--- + +## B18 — Итеративный пересчёт модели + очередь задач +**Тип:** architecture / core value, needs-context. Зависит от B14 (движок), B15 (версионирование), B17 (источник событий). Самая ценная фаза (B13-принцип 3). + +**Что:** +- Job-queue (arq / Celery / RQ — выбрать) для тяжёлых расчётов: probability, routing, recalculation. Сейчас `analyze_case` синхронный — не масштабируется на итерации. +- Event-driven пересчёт: поступление новых полевых данных (B17) → задача «пересчитать модель для case_id» → `build_search_model` с учётом оперативных данных (исключённые территории из `areas_checked`, уточнения из `observations`/`clues`) → новая версия `search_models` (B15). +- Версионирование: история моделей на случай; UI/ATAK показывают актуальную. +- Учёт «найдено/не найдено» — подтверждение/опровержение наблюдений корректирует апостериорную поверхность. +- Работает на экспертных коэффициентах; ISRID (B12) подключается как источник априорных, когда разблокируется. + +**Границы:** Коэффициенты — B1 (ревью). ATAK/Mesh — B19. Без ISRID-данных пока (экспертные априоры). + +**Критерий приёмки:** +- Поступление observation/area_checked ставит задачу пересчёта; новая версия модели сохраняется. +- Тяжёлый расчёт не блокирует HTTP-запрос (async через очередь). +- История версий модели доступна; можно откатиться/сравнить. +- Демонстрация: кейс → отметка «сектор N проверен, никого нет» → пересчёт снижает приоритет N, поднимает соседние. + +--- + +## B19 — «Контур» (ATAK + Meshtastic, реал-тайм) — отдельный проект, плейсхолдер +**Тип:** отдельный проект, пока не детализируется. Зависит от B17 (Field Data) и B18 (перерасчёт). + +**Состав:** интеграция позиций/событий полевых групп в реал-тайм → Field Data (B17) → триггерит пересчёт (B18). Протоколы: ATAK — CoT (Cursor on Target); Meshtastic — LoRa-mesh. Оформлять как самостоятельный workstream после того, как ядро (B14), слои данных (B15) и Field Data (B17) устоялись. - ▲ - │ -┌───────┴────────┐ -│ Vector Backend │ -├────────────────┤ -│ Search Engine │ -│ GIS Engine │ -│ ISRID Engine │ -│ Routing │ -│ Event Engine │ -└───────┬────────┘ - │ - ├──────── Web UI - │ - ├──────── ATAK - │ - └──────── Meshtastic - --- # Сводка @@ -752,5 +888,7 @@ B13 Структурирование проекта | Atomic (backend) | A1–A9 | локальной LLM | | Atomic (frontend) | A10–A16 | локальной LLM | | Needs-context | B1–B12 | сильной модели / человеку | +| Архитектура / рефакторинг | B13 (каркас), B14 (SearchEngine), B15 (слои данных), B16 (GIS/PostGIS), B17 (Field Data), B18 (итеративность + queue) | сильной модели / человеку | +| Отдельный проект | B19 «Контур» (ATAK/Meshtastic) | позже, плейсхолдер | -**Рекомендуемый порядок для локальной модели:** A8 и A7 первыми (чистка мёртвого кода и зелёный CI дают чистую базу для проверки остальных), затем backend A1–A6, затем docs A9, затем frontend A10–A16. После каждой backend-задачи прогонять `PYTHONPATH=/root/vector python -m pytest backend/tests -q`. +**Рекомендуемый порядок:** сначала B14 (чистый движок, поведение-сохраняющий — фундамент), затем B15 (слои данных), затем B16 (GIS) и B17 (Field Data) можно параллельно, затем B18 (итеративность + queue). B12 (ISRID) разблокируется внешне (правовой статус) и подключается как источник априорных к B18. B19 «Контур» — отдельным проектом после B17/B18. Коэффициенты (B1) — под ревью человека на каждом шаге.