Skip to content

Repository files navigation

Pocket Coach

Карманный ИИ-тренер по силовым

Ты записываешь подходы — он читает всю историю и говорит, что делать на следующей тренировке: упражнения, веса, повторы и почему именно так.

Pocket AI strength coach — a SwiftUI app, a self-hosted stdlib-Python backend and next-workout plans written by Claude.

CI iOS Backend DB LLM MCP

Совет тренера, активная тренировка и экран прогресса

Совет тренера — план, а не мотивация

Карточка «Совет тренера»

Не «делай базу 3×8», а «жим ногами 90 кг × 12 × 3 — между разгрузочными 60 и пиковыми 120 после перерыва».

Модель получает не сырую простыню тренировок, а посчитанные фичи: e1RM по Эпли, даты ПР, процент текущего рабочего веса от пика, недельные объёмы по группам с добором эффективных сетов, тренды веса и талии, дисциплину «факт vs план» за 30 дней.

  • Со сроком. План приходит с ответом «через сколько дней идти», а не абстрактным «на следующей тренировке».
  • С обоснованием. «Почему так» раскрывается прямо в карточке — видно логику фазы, а не только числа.
  • Применяется в один тап. Совет становится планом тренировки: карточки встают в порядке рекомендации, «+» подставляет целевой вес.
  • Обновляется сам. После тренировки, после замера веса или талии и утренним таймером — совет всегда датирован сегодня.
  • Не ломается о методику. Спорный план уходит модели одним авто-репромптом, а не падает ошибкой.

Как собирается промпт и что проверяет валидатор — backend/README.md


Голос: телефон остаётся в кармане

Голосовая запись подхода через Siri

Между подходами телефон лежит заблокированный, в ушах наушники. Достать, разблокировать, найти карточку упражнения, нажать «+» — самая дорогая операция всей тренировки, и она повторяется 20–25 раз за сессию.

Голос убирает её целиком. «Зал жим ногами» — подход записан весом и повторами из плана тренера, ответ приходит в наушники. Экран не разблокируется, приложение не выходит на передний план. Свободная фраза («жим ногами 80 на 10, тяжело»), отмена, «что дальше» и завершение тренировки — тоже голосом, по-русски и по-английски.

Как устроен голосовой слой — ios/VOICE_LOGGING_BRIEF.md

Тренировка в один тап

Ввод подхода

Новая тренировка открывается сразу карточками основной шестёрки — без пустого экрана и без похода в каталог. В каждой карточке видно прошлое выполнение и план на сегодня одной строкой.

  • «+» знает, что дальше. Приоритет один на всё приложение: повтор последнего кастомного подхода → цель применённого плана → история +1 повтор.
  • Long press — конструктор подхода с оценкой тяжести 🙂 / 😐 / 😣, которая потом видна и в истории, и в референсе прошлого выполнения.
  • Суперсеты — норма. Несколько упражнений ведутся параллельно, порядок подходов не навязывается.
  • Ошибся — long press по карточке: удалить последний подход или всё упражнение целиком.
  • Черновик не теряется — переживает выход из приложения и возвращает в конструктор с того же места.

Полное продуктовое поведение экранов — BUSINESS_LOGIC.md


История и прогресс, которые читаются

История тренировок Прогресс за неделю Прогресс по упражнению

История — компактные строки «упражнение → веса × повторы» вместо простыни подходов, с оценкой тяжести там, где она была. Неделя — кольцо по основной шестёрке, объём по группам и дисциплина за 7 дней. Упражнение — e1RM, рабочий вес, процент от пика и все ПР по одному движению.

Тренер знает, почему тебя не было

Событие в ленте истории и заметки к тренировке

Дырка в датах ничего не объясняет: болезнь, командировка и лень выглядят одинаково. После недели гриппа коуч предлагал доболезненные веса — формально ведь ничего не произошло.

  • Событие закрывает разрыв. Период без тренировок с причиной встаёт в ленте истории ровно в том промежутке, который объясняет. Пока событие открыто, оно значит «сейчас не тренируюсь», и закрывается само первой же тренировкой.
  • Заметка к подходу. Вес сравним только внутри одной установки: трицепс на канате идёт легче, чем на прямой ручке, и без пометки переход читается как откат силы.
  • Заметка к тренировке — одна строка на сессию: «спал 4 часа, всё шло тяжело».
  • Ничего из этого не считается. Единственный вход в промпт, который остаётся словами: ни фичи, ни порога, ни баннера из него не появилось.
  • Слова — не команда. Текст атлета объявлен модели фактом о контексте: «сделай сегодня полегче» из заметки учитывается как обстоятельство, но политику фаз не отменяет.

Как контекст доезжает до модели — BUSINESS_LOGIC.md


Вес, талия и баннеры без ИИ

Вес тела

Вес тела — одна запись на дату, график и удаление тапом по точке. Талия — не косметика, а граница фазы: при подходе к лимиту коуч сам меняет тон и правила питания, а замерам старше двух недель просто не верит и просит новый.

Баннеры на экране считает не модель, а пороги в коде:

  • «пора замерить» и «данные протухли» — прежде чем советовать калории;
  • «возврат после перерыва» и «плановый deload» — из фазы подготовки, а не из настроения;
  • «неделя закрыта» — с недельным отчётом тренера, который пишется заранее, в ночь на понедельник.

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

Пороги, severity и жизненный цикл — docs/COACH_SIGNALS.md



Как это устроено

Три процесса поверх одной базы. coach_mcp — не сервис поверх API, а второй процесс поверх того же SQLite: он импортирует те же модули, что и backend, и видит ровно то, что генерирует приложение.

iOS (SwiftUI)  ──HTTP + cookie──►  backend/server.py  ──►  SQLite (trainer.db)
                                                              ▲
Claude Desktop ──MCP──►  coach_mcp/server.py  ────────────────┘

Главный инвариант — граница «алгоритм / LLM». Модель вызывается ровно в двух местах: план следующей тренировки и недельный отчёт. Всё остальное детерминировано.

Слой Что делает LLM
coach_state.py фаза подготовки, неделя блока, ramp объёма, плановый deload, режим возврата после перерыва
coach_features.py e1RM, ПР, % от пика, эффективные объёмы, детектор застоя, тренды веса и талии, дисциплина
prompt_builder.py всё, что читает модель: системный промпт, контекст, фичи, история, JSON-схема; проза — в prompts/*.md
recommender.py оба вызова модели: structured output → валидатор → один авто-репромпт; сам HTTP к API — в anthropic_client.py да
plan_validator.py санитизация ответа и три жёсткие границы: покрытие групп, возвратный потолок весов, потолок сессии
coach_signals.py баннеры: пороги, шаблоны текста, схлопывание семей

Валидатор проверяет ровно три жёсткие границы: покрытие мышечных групп, возвратный потолок весов после перерыва и потолок размера сессии фазы. Диапазоны повторов, нижняя граница сессии и чередование нагрузок сознательно не проверяются — это суждение модели, направляемое промптом, а не константы в коде.

Backend — чистый stdlib Python 3.10+: ни зависимостей, ни venv, ни фреймворка; Claude API вызывается через urllib. Тестов — 563 на backend и 148 на iOS, и это единственный автоматический гейт перед деплоем.

Документация

Документ О чём
BUSINESS_LOGIC.md продуктовое поведение и инварианты — главный ответ на «как это должно работать»
backend/README.md эндпоинты, промпт-пайплайн, фазы, переменные окружения, деплой
ios/README.md слои клиента, API-контракт, UX «Совета тренера» и голоса
coach_mcp/README.md MCP-инструменты: разговор с данными и отладка рекомендаций
docs/COACH_SIGNALS.md баннеры: пороги, severity, сортировка, снуз
docs/WEEKLY_PROGRESS.md 7-дневная сводка и её отличие от LLM-отчёта
docs/PRODUCT_BACKLOG.md отложенное и сознательно не сделанное
design/ макеты из Claude Design — исходник визуального языка и этих скриншотов
CLAUDE.md карта репозитория для Claude Code: что легко сломать и почему

Запуск

Backend, тесты, iOS

Всё запускается из корня репозитория; зависимостей и venv у backend нет.

# backend локально → http://127.0.0.1:8080
MINIAPP_ALLOW_DEBUG_USER=1 python3 backend/server.py
# весь тестовый suite — то же, что гоняет CI
python3 -m unittest discover -s backend/tests -p "test_*.py" -v
# iOS-тесты (из ios/)
xcodebuild -project TrainerIOS.xcodeproj -scheme TrainerIOS -destination 'platform=iOS Simulator,name=iPhone 17' test

Генерация советов требует ANTHROPIC_API_KEY; без ключа остальные эндпоинты работают. Деплой: пуш в main → CI прогоняет тесты → backend уезжает на VPS, если он затронут.

Статус

Personal build. Один захардкоженный пользователь (id=3), без публичной авторизации и без App Store: это личный инструмент, выложенный открыто, а не продукт для установки. Профиль атлета, состояние подготовки и стратегия живут только на сервере; их формат описан в backend/README.md.

Веб-мини-апп и Telegram-бот удалены в июне 2026; Android-клиент остался только в истории git.

About

Pocket AI coach for strength training — SwiftUI iOS app + self-hosted backend, next-workout plans powered by Claude

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages