The Jerusalem PostSuspect arrested in fatal shooting of 30-year-old man in Jaffa as police probe motive of incidentESPN DeportesBoston Celtics: resumen de temporada baja y previa 2026-27וואלהצה"ל: חוסל ראש חולייה מארגון הטרור גא"פ שפשט לכיסופים ב-7 באוקטוברRTP DesportoBenfica aponta ao tricampeonato de futsalInquirerDy thanks Marcos for directing VAT removal on system loss chargeDaily MaverickCULTURAL PHENOMENON: Star Trek at 60 and the optimistic future it still dares us to imagineESPNDay: Ohio State 'to make some hard decisions' after Texas collapseDeadlineAmazon Prime Video Debuts $30-A-Month Bundle With AMC+, BritBox, MGM+, PBS Masterpiece & StarzABC NewsMeasles-related deaths in Pennsylvania rise to 4, health officials sayVarietyQuentin Tarantino to Publish ‘Cliff Booth’ Novel Before Brad Pitt and David Fincher’s Movie Streams on NetflixCBS Sports2026 Week 2 NFL odds, start times, betting lines, spreads: Get Week 2 NFL picks, predictions for every gameXatakaVivió hasta los 103 años, actuó en más de 90 películas y fue nominado a tres Óscar. Hoy su hijo es toda una leyenda de Hollywood
The Daily Newsstand · Free, Always
Tuesday, September 15, 2026

Каскадное планирование тренировок с LLM: как удержать план в рамках методики

Translate

LLM хорошо справляется с формой тренировочного плана: распределяет занятия по дням, описывает интенсивность, объясняет упражнения. Но связный текст ещё не означает связную методику. Модель сама по себе не хранит актуальный контекст спортсмена и не обеспечивает преемственность между целью сезона и завтрашней тренировкой. В 1trAIner мы решаем эту задачу через каскад горизонтов, явный контекст и ограничения поверх генерации. Ниже — устройство этого подхода, его защитные механизмы и границы.

Почему один запрос не работает

Запрос «составь мне план тренировок» недостаточно определён. Из него непонятно, что человек делал вчера, сколько времени у него есть завтра и как ближайшая неделя связана с более длинной целью. Модель может заполнить эти пробелы правдоподобными предположениями. Получится аккуратный ответ, но выбор нагрузки окажется случайным относительно реальной ситуации спортсмена.

Проблема не устраняется требованием «будь опытным тренером». Такая инструкция задаёт роль, но не передаёт историю. Даже подробный профиль не заменяет свежие тренировки и показатели восстановления. Цель может оставаться прежней, а основания для завтрашнего занятия — измениться.

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

Поэтому вопрос к архитектуре звучит не «как написать самый подробный промпт», а «какие решения должны существовать до этого запроса». Каскадное планирование тренировок с LLM — способ явно задать такие зависимости. Оно не делает модель безошибочной, зато не оставляет порядок построения плана на её усмотрение.

Каскад горизонтов

В сервисе используется цепочка:

сезон → месяц → неделя → день

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

Если нужный верхний горизонт отсутствует, сервис сначала достраивает его. Это важно отличать от инструкции модели «учитывай сезон»: учитывать можно только то, что действительно передано. Отсутствующий план — задача для оркестрации, а не предложение модели догадаться о содержимом.

Есть дополнительная проверка: текст плана должен содержать не менее 300 знаков. Заглушка «планировщик не вернул текст» не считается планом и не становится основанием для следующего горизонта.

Ниже — иллюстрация выбора недостающей опоры, а не копия исходного кода сервиса. Контекст здесь условно представлен объектом с планами по горизонтам:

// Какой горизонт нужно построить раньше текущего (null — ничего не нужно).
// Смотрим ровно на одну ступень вверх: месяц чинит недельная генерация.
const MIN_PLAN_TEXT_LENGTH = 300;

function planUsable(plan) {
  if (!plan || !plan.id) return false;
  return String(plan.markdown_text || '').trim().length >= MIN_PLAN_TEXT_LENGTH;
}

function missingParentScope(scope, ctx = {}) {
  if (scope === 'day') return planUsable(ctx.weekly_plan) ? null : 'week';
  if (scope === 'week') return planUsable(ctx.monthly_plan) ? null : 'month';
  return null;
}

Функция короткая, но она закрывает целый класс ошибок: до неё дневной план мог построиться поверх пустого недельного и честно написать об этом прямо в тексте задания. Пользователь получал план, в котором вместо тренировки стояло объяснение, почему её нет.

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

Что кладём в контекст

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

Контекст собирается на каждый запрос заново — это дороже, чем один раз записать профиль в системный промпт, но только так модель отвечает про сегодня, а не про абстрактного спортсмена.

  • Последние тренировки с датами и отметкой «сегодня / вчера». Без явных дат модель путает порядок и разбирает позавчерашнюю работу как свежую.

  • Авто-разбор свежих активностей по данным Garmin: пульсовые зоны, сплиты, погода. Это то, что превращает «пробежал 10 км» в «пробежал 10 км во второй зоне с положительным сплитом при +24 °C».

  • Показатели восстановления: сон, пульс покоя, вариабельность пульса, готовность, накопленная нагрузка. На них опирается решение о завтрашней интенсивности.

  • Тренировочные метрики: пульс порога и зоны, прогнозы на дистанции. Интенсивность привязывается к ним, а не к общим таблицам из интернета.

  • Дневник питания за неделю. Нужен ровно для одного: предупредить о недозаправке перед объёмным днём. Диет сервис не назначает.

  • Ограничения из кабинета: заблокированные даты, доступность по дням, лимит минут. Это жёсткие рамки, а не пожелания.

  • Профиль, цели и память атлета — то, что меняется редко.

Как показатели меняют завтрашний день

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

Здесь важна точность формулировки: это правила поверх модели, а не её собственное решение. Модель формулирует тренировку словами, но рамку «сегодня можно качество / сегодня только лёгкое» задаёт не она.

Валидаторы: что мы проверяем до того, как план увидит человек

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

Между ответом модели и пользователем стоит несколько проверок.

  • Пригодность опоры. Тот самый порог в 300 знаков: короткий ответ считается сбоем генерации, а не планом.

  • Достройка горизонта. Если недельного плана нет, он строится до дневного.

  • Жёсткие ограничения. Заблокированные даты и лимит минут не обсуждаются: в день, помеченный как недоступный, тренировка не ставится, даже если по методике она там нужна.

  • Медицинская граница. Боль, травма, симптомы болезни — повод отправить к врачу, а не подобрать нагрузку. Сервис информационный и диагнозов не ставит.

// Пригоден ли план как опора для нижележащего горизонта.
const usable = planUsable(ctx.weekly_plan);
if (!usable) {
  await generatePlanForScope('week', ctx);   // сначала достраиваем неделю
}
await generatePlanForScope('day', ctx);     // и только потом выпускаем день

Стоимость запроса

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

Контекст на каждый запрос стоит денег, поэтому цену стоит считать заранее. С 10.09.2026 тарифы модели такие: вход — $0,15 за миллион токенов, кэшированный вход — $0,003 за миллион, выход — $0,60 за миллион. В пиковые часы они вдвое выше: $0,30, $0,006 и $1,20 соответственно.

Пик по прайсу провайдера — 01:00–04:00 и 06:00–10:00 UTC в будни; выходные пиком не считаются. Отсюда и ночная сборка суточных планов в 03:15–03:59 по Москве: по UTC это off-peak, и та же работа обходится вдвое дешевле. Кэшированный вход дешевле обычного в полсотни раз, поэтому неизменную часть контекста выгодно держать стабильной от запроса к запросу.

Что не получилось с первого раза

Три истории, каждая стоила отдельной правки.

Первая — план поверх пустоты. Недельная генерация отметилась запущенной, плана не появилось, а ночная сборка дня построила задание поверх пустого недельного плана и написала об этом прямо в тексте. Лечится проверкой опоры, с которой начинается статья.

Вторая — заглушки вместо текста. Ответ «планировщик не вернул текст» формально является планом: есть запись, есть горизонт, есть дата. Отсюда порог по длине.

Третья — распознавание голоса. Голосовые сообщения расшифровывает собственный сервис на faster-whisper. Без доменной подсказки модель уверенно превращала «Ликийскую тропу» в «лекистскую», а Body Battery — во что придётся. Помог список терминов и имён собственных в подсказке распознавателя.

Что дальше

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

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.