# ИИ-Тренажёр — план развития модуля Модуль `/trainer`: бесконечные задачи, которые рождаются из **данных-генераторов**, а математика считается и проверяется **детерминированно** через `SimExpr` (без `eval`). LLM в ядре не участвует — его роль (Уровень 1+) сочинять генераторы и текстовые задачи, которые тот же слой верифицирует подстановкой. Тема-пилот: **уравнения, 7 класс**. ## Инвариант корректности (не нарушать) > Любой источник задачи (генератор, шаблон, LLM) обязан пройти `TrainerEngine.verifyRoot`: > заявленный корень подставляется в уравнение, расходится → задача отбрасывается. > Та же подстановка проверяет ответ ученика (принимает `5`, `x=5`, `10/2`, `2+3`). > Выражения — ТОЛЬКО `SimExpr` (whitelist, без `eval`/`new Function`). Цвета/текст от > пользователя — только в безопасные стоки или с escape. ## Состояние: Phase 0 — DONE (прототип, в этом коммите) - **Движок** `frontend/js/trainer/_trainer_engine.js`: `instantiate` / `generateBatch` / `verifyRoot` / `checkStudentAnswer` / `exprToLatex` (AST→LaTeX, reusable) / `makeRng`. - **Генераторы** `frontend/js/trainer/generators.js`: 5 типов (линейное `ax+b=c`, скобки, переменная с двух сторон, дробь `x/a+b=c`, дробный коэффициент `ax/b=c`). Приём «корень-вперёд» → гарантированно целые ответы, самопроверка всегда проходит. - **Страница** `frontend/trainer.html`: KaTeX-рендер уравнений и шагов, чипы-темы, мгновенная проверка, подсказка/решение, авто-выбор первого неосвоенного навыка. - **Прогресс на сервере**: `practice_progress` (мигр.081), `practiceController` + `routes/practice` (`/api/practice/progress|attempt`), клиент `LS.practiceProgressList/Submit`. Мастерство = серия 5 верных подряд (липкое). - **Фича-флаг** `trainer`: тумблер в админке (Модули), `requireFeature('trainer')`, скрытие из сайдбара + редирект страницы (`FEATURE_HREFS`), запись в `MODULE_CATALOG`. - Тесты: `practice.test.js` (10/10), headless-смоуки движка/страницы. lint:routes 0. --- ## Phase 1 — Ширина контента (генераторы) — DONE **Сделано:** таксономия `тема → навык` с метаданными (`topic/order/subject/grade`), `TrainerGenerators.topics()/byTopic()`. **13 генераторов в 3 темах**: Уравнения (7: `ax+b=c`, `a(x+b)=c`, `ax+b=cx+d`, `a(x+b)=c(x+d)`, `x/a+b=c`, `ax/b=c`, `(ax+b)/c=d`), Пропорции (3), Проценты (3, `kind:'compute'` — текстовый prompt + проверка подстановкой). UI: выбор темы (вкладки) → навыки (чипы) с бейджами мастерства, авто-выбор первой неосвоенной темы/навыка. **Подробные объяснения**: каждый шаг расписан словами + шаг «Проверка» (подстановка корня). Движок: `exprToLatex` чинит отрицательные множители (`7·(−5)`), `kind:'compute'`. Смоуки 238/238 (движок) + 19/19 (страница). **Цель (исходная):** перестать быть «демкой одной темы». Структура `класс → предмет → тема → навык`. - Реестр генераторов: вынести в данные с метаданными `{ grade, subject, topic, skill, order, difficulty }`. Группировка чипов по темам/классам; выбор класса/предмета вверху. - Новые генераторы 7 кл: пропорции, раскрытие скобок с обеих сторон `a(x+b)=c(x+d)`, уравнения с дробью-уравнением `(ax+b)/c = d`, простые буквенные преобразования. - Соседние темы (параметрические, без LLM): упрощение выражений, степени, проценты, линейные неравенства (расширить `checkStudentAnswer` под интервалы — см. P5). - **Acceptance:** ≥3 темы × ≥3 навыка, у каждого generateBatch(50) даёт 50 разных корректных задач; solvability-смоук на сетке параметров. ## Phase 2 — Адаптивность и интервальное повторение — DONE **Сделано:** `frontend/js/trainer/adaptive.js` (`window.TrainerAdaptive`, чистая логика) — `nextSkill` (приоритет: in-session повтор → серверный due → прогрессия → удержание по box), `onWrong/onCorrect` (in-session очередь повторения), `sessionStats`. **Умная тренировка** на странице (по умолчанию вкл, тумблер): авто-подбор навыка, ведёт от простого к сложному, возвращает ошибки; сессия из 10 задач с **итогом** (верно/точность/ навыки/«стоит повторить»). Неверный ответ авто-показывает решение. Сервер: SR-поля `box`+`due_at` на `practice_progress` (мигр.**082**, Leitner-интервалы 0/1/3/7/16/30 дней), `listProgress` отдаёт `box/due_at/due`. Смоуки: adaptive 12/12, страница 23/23, practice.test.js 11/11 (+SR box/due). **Цель (исходная):** вести ученика, а не давать случайное. - Диагностика на входе (по 1–2 задачи на навык) → стартовый уровень. - Подбор следующего навыка по мастерству (escalate при серии, откат при ошибках). - Ошибки уходят в очередь повторения (свой лёгкий SR или reuse flashcards Tier-1). - «Продолжить тренировку», дневная норма/цель, сводка сессии (что освоено, над чем работать). - Сервер: расширить `practice_progress` (или новая `practice_review_queue`); агрегаты для аналитики. - **Acceptance:** сессия из N задач сама ведёт от простого к сложному; промахнутый навык всплывает повторно; прогресс переживает перезаход. ## Phase 3 — Уровень 1: LLM-задачи с верификацией — DONE **Сделано:** серверная проверка `backend/src/utils/practiceVerify.js` (грузит `SimExpr` в Node через require, `verifyRoot` подстановкой). Сервис `practiceGenService.js`: `buildMessages`→LLM→`parseProblem`→`validateAndVerify` (компиляция SimExpr + подстановка корня + санитизация story/шагов) с **авторетраем по фидбэку**; LLM-вызов инъектируется (`opts.ask`, дефолт — `assistantController.callLLMFailover`). Пул `practice_problems` (мигр.**083**, status approved/draft). Эндпоинты: `POST /api/practice/generate` (учитель/админ) + `GET /api/practice/pool` (ученикам). Клиент: `LS.practicePool/Generate`, тема **«Текстовые задачи»** на странице (берёт из пула; учителю — кнопка «Сгенерировать»). Гарантия: невалидная/неверная задача в БД НЕ пишется → ученику не попадёт. Тесты `practice-gen.test.js` 13/13 (verify, ретраи, off→503, 403 ученику, пул). **Цель (исходная):** текстовые/контекстные задачи, которых не даёт параметрика. - LLM (через провайдеров админки) генерирует `{ lhs, rhs, answer, story }`; сервер прогоняет `verifyRoot`; расхождение → авторетрай с фидбэком («корень не удовлетворяет, исправь»). - Кэш-пул `practice_problems` (предгенерация, ревью учителем) — не платить за каждый показ. - Генерация «по теме урока» (связка с theory/exam-prep). - **Acceptance:** доля задач, прошедших верификацию с 1–2 ретраев, ≥95%; пул кэшируется; ни одна неверная задача не доходит до ученика (гарантирует инвариант). ## Phase 4 — Авторинг учителем — DONE (lean) **Сделано (переиспользуя P3-проверку):** ручной авторинг — `POST /api/practice/author` (учитель пишет story/lhs/rhs/answer → та же `validateAndVerify` подстановкой → пул; не сходится → 422). Раздача классу — `POST /api/practice/assign` (владелец/админ → durable `pushNotif` каждому ученику класса, ссылка `/trainer`). Клиент: `LS.practiceAuthor/Assign`; в теме «Текстовые задачи» учителю — кнопки «Своя задача» (модалка-форма с серверной проверкой) и «Выдать классу» (пикер классов → уведомление). Тесты: author (валид→пул, неверный→422, ученик→403), assign (владелец уведомляет, чужой→403). **Не делалось (осознанно):** полноценный визуальный конструктор ПАРАМЕТРИЧЕСКИХ генераторов (pick/derive/lhs/rhs DSL) — крупный отдельный билдер; текущий авторинг закрывает «учитель создаёт задачи + раздаёт классу» переиспользованием пула и инварианта проверки. **Цель (исходная):** учитель создаёт свои наборы и раздаёт классу (как sim-builder/Quantik Ф5). - Конструктор генераторов: шаблон `lhs/rhs`, диапазоны параметров, формула ответа, шаги решения; превью + клиентская валидация через `SimExpr.compile`. - Хранение (таблица по образцу `custom_sims`), серверная `validateSpec` без исполнения, раздача классу + уведомление, привязка к ДЗ/уроку. - **Acceptance:** учитель собирает рабочий генератор без кода; ученик решает; права/видимость как у custom-sim (own + раздано). ## Phase 5 — Типы ответов и проверки — DONE (частично) **Сделано:** движок получил **несколько корней** (`gen.answers` → `problem.answers`; `_checkMultiRoot` — ввод всех корней через «;», сверка мультимножеством) и **эквивалентность выражений** (`kind:'simplify'`, `gen.srcExpr`/`answerExpr`; `_sampleEquiv` — численный сэмплинг в фикс. точках, без Math.random; `_checkEquiv`). `exprToLatex` чинит знаковые коэффициенты (`-5x`, `x²−5x+6`, `a−(−b)→a+b`). Новые темы: **Упрощение** (привести подобные, раскрыть скобки) и **Квадратные** (Виета `x²+bx+c=0`, разность квадратов — 2 корня). Страница: префикс «x=» и подсказка ввода по типу, ответ-лейбл (корни/выражение). Смоук движка 291/291 (T11 roots, T12 simplify, T13 latex). **Осталось (стретч):** неравенства (нужен парсер отношений) — не вошло. **Цель (исходная):** не только «корень-число». - Множество корней (квадратные/факторизация), интервалы (неравенства), упрощение выражений (эквивалентность через численный сэмплинг по диапазону, а не строковое равенство). - Пошаговый ввод (проверять каждый шаг подстановкой), несколько форматов ответа. - **Acceptance:** квадратное уравнение принимает оба корня в любом порядке; `(x+1)^2` ≡ `x^2+2x+1` через сэмплинг; неравенство принимает `x>3` и эквивалент. ## Phase 6 — Геймификация, аналитика, UX — DONE (частично) **Сделано:** **учительская аналитика** — `GET /api/practice/class-stats?class_id=` (`classStats`, владелец класса/админ): агрегаты по навыкам (attempted/mastered/accuracy) + матрица ученик×навык. Клиент: кнопка «Аналитика класса» (учителю) → модалка с **тепловой картой** (ученики × навыки, цвет по точности, ✓ освоено) + пикер классов. `LS.practiceClassStats`. **Лёгкая геймификация**: строка общего прогресса «Освоено навыков M из N · решено всего K» (из агрегатов `practice_progress`), бейджи мастерства на чипах (P2). Тесты practice.test.js +4 (владелец видит, чужой/ученик→403, без id→400). Смоук страницы 27/27. **Осталось (стретч):** XP в общую геймификацию, виртуальная клавиатура, сократические подсказки — не вошло (отдельные крупные направления). Изначальный список: - XP/энергия/стрики (reuse инфраструктуры Квантика), бейджи мастерства на чипах (есть основа). - Учительская аналитика: кто на каком навыке застрял, тепловая карта класса, отчёты. - UX: виртуальная клавиатура для дробей/степеней, «почему неверно» (разбор ошибки), сократические подсказки через Квантик-ассистента, мобильная раскладка, доступность. --- ## Сквозное - **Безопасность:** только `SimExpr`; авторские генераторы — серверная `validateSpec` без исполнения (длины/лимиты, escape текста), как `custom_sims`. - **Тесты:** на каждый генератор — solvability-смоук (сетка параметров → есть корректные задачи + достижим целевой ответ); бэкенд-тесты на новые роуты; headless-смоук страницы. - **Контент = данные:** генераторы и темы — JS-данные/таблицы, не код. ## Рекомендуемый следующий шаг **Phase 1** (ширина контента) — даёт наибольшую пользу при минимальном риске и переиспользует готовый движок/проверку/страницу. Затем **Phase 2** (адаптивность) для удержания.