VEU.WathBody/Архитектура_LLM_планировани...

22 KiB
Raw Permalink Blame History

Дизайн: создание и корректировка программы тренировок с помощью LLM

Дата составления: 17.10.2026 Привязка к проекту: «Феникс 6X» (см. «Техническое задание.md»). Архитектура: телефон + часы Garmin Fenix 6 + личный сервер (FastAPI + PostgreSQL), данные из Garmin Connect в .fit.


1. Главная идея: LLM — планировщик, код — источник истины

Ключевое архитектурное решение: LLM никогда не является хранилищем программы и не имеет прямого доступа к данным.

  • Программа живёт в БД сервера как версионированный JSON-документ строгой схемы.
  • LLM только предлагает программу или изменение — в виде структурного JSON (structured output / function calling), не свободного текста.
  • Детерминированный валидатор проверяет предложение по схеме и правилам спортивной методологии. Прошёл — новая версия программы. Не прошёл — повторный запрос LLM с текстом ошибки (ограниченное число попыток), затем fallback на правила.

Почему так:

Риск «LLM всем управляет» Как снимается
Галлюцинации (несуществующие упражнения, веса 500 кг) Жёсткая JSON-схема + справочник ID упражнений + валидатор
Небезопасная прогрессия (резкий скачок нагрузки) Детерминированные правила-ограничители (разд. 6)
Непредсказуемость, «откатить нельзя» Неизменяемые версии программы + журнал причин изменений
Дорого и медленно при каждом чихе Гибрид: микроправки после тренировки делает простой код, LLM вызывается редко (разд. 3)
Зависимость от интернета/провайдера Офлайн-fallback: чисто правила (разд. 7)

2. Представление программы: версионированный JSON

Формат хранения — свой JSON (внутренний формат из документа «Форматы_тренировок_и_рекомендации.md»). Иерархия:

Программа (Program, версия неизменяема)
 └─ Неделя (Week / мезоцикл: целевой объём, тонус)
     └─ Тренировка (Session: день, фокус — верх/низ/тяни-толкай)
         └─ Упражнение (Exercise: ссылка на ID из библиотеки)
             └─ Подход-план (SetPlan: вес, повторения, RPE-цель, отдых)

Пример фрагмента:

{
  "program_id": "prg_2026_10",
  "version": 3,
  "based_on_version": 2,
  "status": "active",              // draft | active | archived
  "author": "llm",                 // llm | rules | user
  "created_at": "2026-10-17T18:00:00Z",
  "weeks": [
    {
      "week": 1,
      "goal": "гипертрофия, объёмная",
      "sessions": [
        {
          "day": "monday",
          "focus": "push",
          "exercises": [
            {
              "exercise_id": "bench_press",   // только ID из библиотеки!
              "sets": [
                {"weight_kg": 80, "reps": 8, "target_rpe": 7, "rest_sec": 150}
              ]
            }
          ]
        }
      ]
    }
  ],
  "rationale": "Увеличил вес в жиме на 2.5 кг: 3 тренировки подряд все подходы закрыты при RPE<=7. Объём недельный не менял."
}

Важные свойства:

  • Упражнения — только по ID из библиотеки упражнений приложения (иначе сломаются голосовой ввод, статистика и сопоставление с .fit).
  • Каждая версия неизменяема. Корректировка = создание новой версии + запись program_changes (что, почему, решение валидатора, принял ли пользователь).
  • Выполненные тренировки ссылаются на конкретную версию — история не «поплывёт» после правок.
  • Из этой же структуры при необходимости можно собрать экспорт (CSV/JSON/FIT workout).

3. Два контура и три частоты корректировки

Контур A — планирование (редко, LLM)

Когда: холодный старт, смена цели, начало нового мезоцикла (каждые 4–6 недель), явная просьба пользователя.

Что делает LLM: строит программу целиком (или следующий мезоцикл) из профиля, цели, инвентаря, ограничений (травмы) и сводки истории.

Контур B — корректировка (три частоты, гибрид «правила + LLM»)

Частота Кто решает Что корректирует Пример
После каждой тренировки Код (правила), без LLM Вес/повторы следующего занятия по прогрессии «Все подходы закрыты при RPE≤7 → +2.5 кг»; «не дотянул 2 раза → −5%»
Раз в неделю LLM Следующая неделя: объём, порядок, замена упражнений, лёгкая неделя «Сон плохой, пульс покоя +5 → снизить объём на 15%»
Раз в мезоцикл (4–6 нед.) LLM Перепланирование: делоад, новые упражнения, смена акцентов «Плато в жиме 3 недели → сменить на жим гантелей, добавить паузы»

Смысл гибрида: 90% решений — тривиальная прогрессия, которую надёжнее и дешевле считает обычный код. LLM подключается там, где нужна интерпретация нетривиальных сигналов (усталость, плато, пропуск тренировок), — редко, обдуманно и с полным контекстом недели.


4. Конвейер данных: из .fit — в признаки для LLM

LLM не видит сырые .fit и пульсотограммы. Сервер сам считает метрики и отдаёт компактную сводку.

Garmin Connect (.fit)
   → парсер (Session / Lap / Record / Set)   [уже есть в ТЗ]
   → каноническое JSON-представление тренировки
   → MetricsEngine: детерминированный расчёт признаков
   → хранилище фактов (PostgreSQL)
   → сборка промпта для LLM (сводка недели/мезоцикла)

Метрики, которые считает MetricsEngine (детерминированно, без LLM):

  • Выполнение плана: доля закрытых подходов/тренировок (adherence).
  • Тоннаж по упражнению и по мышечным группам (план vs факт).
  • Оценочный 1ПМ (формулы Эпли/Бжицки) и его динамика по неделям.
  • Фактический RPE — из голосового ввода («Подход 120 на 8» → если план был 120×6 при RPE 8, фактически RPE ниже) и/или короткого опроса после тренировки.
  • Кардионагрузка: средний/пиковый пульс за подход, время восстановления пульса между подходами (из Lap/Record .fit — это как раз данные часов).
  • Сигналы усталости: падение рабочего веса при том же RPE, рост времени отдыха, падение числа повторений в последних подходах (кластеры «до отказа»).
  • Субъективный фидбек из мини-опроса в приложении после тренировки (1 вопрос: «Как прошла? 1–10» + опционально сон/самочувствие) — стоит дёшево и резко повышает качество корректировок.

5. Контракт с LLM: промпт и структурный вывод

Состав запроса

  1. Системный промпт — роль и методология (правила прогрессии, пределы изменений, приоритет безопасности), требование вернуть строго JSON по схеме.
  2. Профиль: пол, возраст, вес, цель, стаж, инвентарь, травмы/ограничения.
  3. Текущая версия программы (JSON).
  4. Сводка фактов от MetricsEngine: последняя неделя (или мезоцикл) в сжатом виде — adherence, тоннаж по группам, динамика e1PM, пульс-метрики, субъективный фидбек. Сырую историю — не давать.
  5. Задача: «предложи план на следующую неделю/мезоцикл» или «создай программу с нуля».

Ответ

Только JSON по схеме: новая версия (или дельта-изменения) + обязательное поле rationale («почему») — оно попадает в журнал изменений и показывается пользователю («Феникс повысил вес: ты закрыл все подходы»).

Технически: API провайдера с structured output / function calling + JSON Schema; при неудаче валидации — повтор с текстом ошибки, максимум 2–3 попытки.

Дополнительно (при желании, этап 2)

Двухпроходная схема: первый вызов генерирует план, второй («ревьюер») проверяет его по чек-листу методологии и возвращает замечания. Дёшево и ловит логические ошибки, которые схема не видит (например, два тяжёлых дня на одну группу подряд).


6. Слой валидации и безопасности (детерминированный)

После LLM предложение проходит обязательные проверки. Это обычный код, не LLM:

Схема: все ID упражнений существуют в библиотеке; веса/повторы/отдых в допустимых диапазонах.

Методологические лимиты (пример, настраиваются):

  • Рост рабочего веса ≤ +5% (или ≤ +2.5 кг на базовых) в неделю на упражнение.
  • Изменение недельного объёма ≤ ±20% (кроме запланированного делаода).
  • Не поднимать одновременно объём и интенсивность (одна переменная за раз).
  • Делоад — не реже чем каждые 6 недель (объём −40–50%).
  • Не более 2 «тяжёлых» (RPE≥8) тренировок на одну мышечную группу в неделю.
  • Замена упражнения — только в пределах той же двигательной группы (жим → жим, тяга → тяга); список групп — атрибут библиотеки упражнений.
  • Лимит общего числа подходов за тренировку (например, ≤ 25).

Человеко-машинный контур: предложение показывается в приложении в виде диффа («Вес в приседе: 100 → 102.5, потому что…») с кнопками «Принять / Изменить / Отклонить». По мере доверия настраивается «бюджет автобонуса»: например, микроправки весов ±5% применяются сами, смена упражнений и делоады — только по подтверждению.


7. Офлайн-fallback и отказоустойчивость

Требование автономности из ТЗ (разд. 5) распространяется и на планирование:

  • Если LLM недоступен (нет сети, лимит API, ошибки) — работает контур правил: прогрессия по формулам + делоад по счётчику недель. Программа продолжает корректироваться безопасно, просто без «умных» интерпретаций.
  • Правила-микроправки (после каждой тренировки) вообще не зависят от LLM — это базовый режим, LLM — надстройка.
  • Корректировки — фоновая задача (cron после синхронизации тренировки / вечером воскресенья), не в реальном времени, поэтому очереди и ретраи из ТЗ подходят без изменений.

8. Компоненты сервера и схема данных

┌────────────┐   .fit   ┌──────────────┐  факты  ┌───────────────┐
│ Garmin     │─────────▶│ FitParser    │────────▶│ PostgreSQL    │
│ Connect API│          │ (из ТЗ)      │         │  факты, БД    │
└────────────┘          └──────────────┘         └──────┬────────┘
                                                        │ сводка
                        ┌───────────────┐   план/дельта │        ▲
                        │ LLM Planner   │◀──────────────┘        │
                        │ (промпт+JSON) │                        │
                        └──────┬────────┘                 ┌──────┴────────┐
                               ▼                          │ MetricsEngine │
                        ┌───────────────┐                 │ (правила,     │
                        │ Validator     │──✗ ошибка→ретрай│  прогрессия)  │
                        └──────┬────────┘                 └───────────────┘
                               ▼ версия
                        ┌───────────────┐   дифф «принять?»   ┌──────────┐
                        │ ProgramStore  │────────────────────▶│ Телефон  │
                        │ (версии JSON) │◀────────────────────│ (UI)     │
                        └───────────────┘   подтверждение    └──────────┘

Таблицы (минимум):

  • programs — id, версия, статус, author, created_at, JSON-тело.
  • program_changes — from/to версия, rationale, отчёт валидатора, решение пользователя (auto/accepted/rejected/edited).
  • workout_facts — факт тренировки из .fit + голосового ввода (уже следует из ТЗ).
  • workout_metrics — посчитанные признаки (тоннаж, e1PM, adherence, пульс-метрики).
  • athlete_state — профиль, цели, текущие веса, счётчики недель до делоада.
  • llm_calls — журнал: входная сводка (хэш), ответ, результат валидации, принят ли. Нужен для анализа качества и отладки промптов.

9. Полный жизненный цикл (пример недели)

  1. Пн–Сб. Пользователь тренируется: часы пишут .fit, телефон голосом пишет подходы, всё синхронизируется на сервер.
  2. После каждой тренировки: MetricsEngine обновляет факты → правила двигают веса на следующее занятие (+2.5 кг / удержание / −5%). Без LLM, мгновенно.
  3. После тренировки — опрос 1–10 в приложении (5 секунд).
  4. Воскресенье, cron: сервер собирает сводку недели → LLM предлагает план следующей недели (или «без изменений») → валидатор проверяет → дифф в приложении → пользователь принимает (или авто в рамках бюджета доверия).
  5. Каждые 4–6 недель: LLM строит следующий мезоцикл (делоад/новая волна) на сводке всего цикла.
  6. Всё попадает в журнал → раз в месяц можно глазами оценить: растут ли e1PM, как часто LLM менял план и помогало ли это (сравнение трендов до/после изменений).

10. Риски и их обработка

Риск Обработка
LLM «перетренирует» пользователя Жёсткие лимиты валидатора + делоад-счётчик в правилах (код, не LLM)
LLM занижает нагрузку, прогресс остановится Метрика «плато» в MetricsEngine (нет роста e1PM N недель) триггерит обязательное предложение изменений
Пользователь не принимает правки Бюджет автобонуса + показывать rationale простым языком; правки сохраняются как «edited» и учитываются в следующих промптах
Расходы на API Вызовы редки (1 раз в неделю + мезоцикл), сводка компактная — копейки; микроправки бесплатны (правила)
Некачественный голосовой ввод портит факты Сопоставление голосовых данных с .fit по времени (уже в ТЗ п. 5.1) — расхождения помечаются и не идут в метрики как истина
Субъективность RPE Опрос после тренировки + расчёт по выполненным повторам; расхождения — сигнал, а не приговор

11. Порядок внедрения (в развитие MVP из ТЗ)

  1. Шаг 1 (без LLM): ProgramStore с версиями JSON + правила прогрессии после тренировки + MetricsEngine (тоннаж, adherence, e1PM). Система уже полезна.
  2. Шаг 2: еженедельный вызов LLM (структурный вывод) + валидатор + дифф-подтверждение в приложении + журнал llm_calls.
  3. Шаг 3: мезоцикловое планирование, двухпроходная схема «генератор+ревьюер», опрос RPE, автоматизация бюджета доверия.

Такой порядок даёт работающую систему на каждом этапе и позволяет накапливать данные для LLM, а не проектировать его вслепую.