Telegram Quiz AI

Повторение — мать учения

🇬🇧 English version

Повторение — мать учения

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

Раньше вопросы приходилось придумывать вручную. Сейчас достаточно скормить материал ChatGPT или NotebookLM и попросить из него тест. Дальше — файл, который понимает бот: он принимает его, проверяет формат и ведёт по вопросам.

Сначала — материал

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

  • NotebookLM хорош, когда источников несколько и важно, чтобы модель не выдумывала: он отвечает только по загруженным документам и показывает, откуда взял факт.
  • ChatGPT удобнее, когда материал уже текстом в переписке или его надо переупаковать: сократить, разбить на темы, перевести.

Один файл — одна тема. Не надо мешать в один тест три лекции и полкниги: повторять такое невозможно.

Промт

Скопируйте целиком и вставьте материал в конец. Если материал большой — приложите файлом и напишите «материал в приложенном файле».

Ты — методист, который делает тренировочный тест по материалу ниже. Материал — единственный источник фактов: ничего не добавляй от себя.

Сделай 12–20 вопросов, которые проверяют понимание и вспоминание, а не узнавание. Больше половины вопросов — с кнопками (inline_choice, situational_scenario), чтобы ученик тапал, а не печатал. Остальные — с вводом текста (how_to_say, text_input_translation, fill_blank, error_correction, word_order). Текстовые ответы должны быть короткими — не длиннее 3–4 слов.

Верни ТОЛЬКО валидный JSON без markdown-обёрток, без комментариев и без текста до и после. Результат должен быть JSON-файлом (не ответами на тест и не пояснениями) — сохрани его как .json файл. Схема:

{
  "format": "tift-quiz-v4",
  "version": 4,
  "language": "ru",
  "title": "короткое название темы",
  "description": "одно предложение о том, что проверяет файл",
  "topics": [
    {"id": "latinitsey_s_podchyorkivaniem", "title": "Название темы"}
  ],
  "rules_summary": [
    {
      "rule_id": "RULE_ZAGLAVNIMI",
      "title": "Правило или факт",
      "category": "Раздел",
      "summary": "формулировка правила в 1–2 предложениях",
      "formula_or_pattern": "формула или шаблон, если есть",
      "source_ref": "откуда факт: файл, слайд, раздел"
    }
  ],
  "questions": [ ... ]
}

Поля вопроса:
id — уникальная строка: q1, q2, q3 и так далее;
exercise_type — один из: inline_choice, situational_scenario, how_to_say, text_input_translation, fill_blank, error_correction, word_order, new_word;
topic — ровно один id из topics;
difficulty — 1, 2 или 3;
prompt — текст задания;
explanation — 1–2 предложения, почему ответ правильный;
rule_reference — {"rule_id": "<id из rules_summary>", "title": "<title правила>"};
source — откуда взят вопрос.
hint — необязательная ссылка на публичный источник (документация, статья, официальная страница), который поможет ученику прочитать подробнее; в Telegram показывается как подсказка после ответа.

Поле с ответом зависит от типа:
inline_choice: "inline_keyboard": [["вариант 1"], ["вариант 2"], ["вариант 3"]], "correct_answer": "точная копия правильного варианта";
situational_scenario: "options": ["вариант 1", "вариант 2", "вариант 3"], "correct_answer": "точная копия правильного варианта";
how_to_say, text_input_translation, fill_blank, error_correction, word_order, new_word: "expected_input": ["допустимый ответ", "ещё один допустимый вариант"];

Требования:
1. expected_input — все формулировки, которые считаются правильными: с точкой и без, с сокращением и без, цифрой и словом. Только реально верные варианты, без мусора.
2. correct_answer обязан дословно совпадать с одним из вариантов.
3. В inline_choice — 3–4 варианта, неверные должны быть правдоподобными.
4. Не ставь таймкоды (00:00:00) в source и source_ref, если не уверен, что они реальные. Если ставишь — минуты и секунды должны быть от 0 до 59.
6. id, rule_id и topic — латиницей; текст заданий — на русском; ответы — на изучаемом языке, если учим язык.
7. Никаких ```json, никаких многоточий, никаких // комментариев. JSON должен парситься как есть.
8. По возможности добавляй поле `hint` со ссылкой на публичный источник (официальная документация, статья, Wikipedia), чтобы ученик мог прочитать подробнее. В Telegram подсказка показывается после ответа.

Материал:
[ВСТАВЬТЕ СЮДА КОНСПЕКТ, ТЕКСТ ИЛИ ПРИЛОЖИТЕ ФАЙЛ]

Промт длинный намеренно: модель ломает формат ровно там, где ей дали свободу. Чем жёстче схема, тем меньше правок потом.

Что должно получиться

Файл — обычный JSON. Минимальный рабочий пример с тремя типами вопросов:

{
  "format": "tift-quiz-v4",
  "version": 4,
  "language": "ru",
  "title": "Present Perfect Continuous",
  "description": "Тренировка по конспекту урока 3.",
  "topics": [
    { "id": "tenses", "title": "Времена" }
  ],
  "rules_summary": [
    {
      "rule_id": "RULE_PPC",
      "title": "Present Perfect Continuous",
      "category": "Грамматика",
      "summary": "Действие началось в прошлом и продолжается сейчас.",
      "formula_or_pattern": "have/has + been + V-ing",
      "source_ref": "конспект, урок 3"
    }
  ],
  "questions": [
    {
      "id": "q1",
      "exercise_type": "inline_choice",
      "topic": "tenses",
      "difficulty": 1,
      "prompt": "Выберите правильный вариант: I ___ English for two years.",
      "inline_keyboard": [["learn"], ["am learning"], ["have been learning"]],
      "correct_answer": "have been learning",
      "explanation": "Есть for two years — действие длится до сих пор.",
      "rule_reference": { "rule_id": "RULE_PPC", "title": "Present Perfect Continuous" },
      "source": "конспект, урок 3"
    },
    {
      "id": "q2",
      "exercise_type": "fill_blank",
      "topic": "tenses",
      "difficulty": 1,
      "prompt": "Вставьте пропущенное: I have been waiting ___ 9 утра.",
      "expected_input": ["since"],
      "explanation": "since — точка отсчёта, for — длительность.",
      "rule_reference": { "rule_id": "RULE_PPC", "title": "Present Perfect Continuous" },
      "source": "конспект, урок 3"
    },
    {
      "id": "q3",
      "exercise_type": "error_correction",
      "topic": "tenses",
      "difficulty": 2,
      "prompt": "Найдите и исправьте ошибку: I have been waiting since two hours.",
      "expected_input": ["I have been waiting for two hours."],
      "explanation": "since — точка отсчёта, for — длительность.",
      "rule_reference": { "rule_id": "RULE_PPC", "title": "Present Perfect Continuous" },
      "source": "конспект, урок 3",
      "hint": "https://www.grammarly.com/blog/since-vs-for/"
    }
  ]
}

Что проверяет бот

Перед запуском бот читает файл целиком и собирает все проблемы сразу — не по одной. Достаточно, чтобы в файле было:

Поле Требование
format начинается с tift-quiz; для этой версии — tift-quiz-v4
questions непустой список
id есть у каждого вопроса и не повторяется
prompt непустой текст задания
exercise_type одно из восьми значений выше
topic есть и совпадает с одним из topics[].id, если topics заполнен
rule_reference.rule_id есть и совпадает с одним из rules_summary[].rule_id, если rules_summary заполнен
ответ поле, соответствующее типу: correct_answer, expected_input или pairs
source, source_ref таймкоды, если они есть, должны быть реальными: минуты и секунды от 0 до 59

Если topics или rules_summary оставить пустыми, проверка на совпадение пропускается. Но тогда topic и rule_reference.rule_id всё равно должны быть непустыми. Проще заполнить оба списка и следить, чтобы вопросы ссылались на них.

Как скормить файл боту

  1. Сохраните ответ модели в файл с расширением .json, например quiz.json, в кодировке UTF-8. Проверьте, что он начинается с { и заканчивается } — без ```json и без «Вот ваш файл:».
  2. В боте должен быть шаг Wait Input с типом ввода File upload. Бот приостанавливается на нём и ждёт документ.
  3. Отправьте quiz.json боту как документ. Текст сообщением не подойдёт: шаг ждёт именно файл.
  4. Файл сохраняется на сервере, и следующий узел — условие Uploaded quiz file is valid — скачивает его и проверяет.
  5. Если формат сломан, бот уходит в ветку «нет» и показывает на её сообщении список проблем — их берёт из { $user->extra.quiz_problems }. Исправьте всё из списка и отправьте файл снова — проверка ничего не запоминает, каждый раз читает файл заново.
  6. Если файл валиден, сценарий идёт по ветке «да»: бот ведёт quiz по вопросам из файла, а ответы складывает в запись пользователя — как отвечать на каждый тип вопроса, написано в разделе Как проходит квиз.

Своя последовательность узлов выглядит так:

/start → Wait Input (File upload) → Condition (Uploaded quiz file is valid)
                                        ├── да  → quiz по файлу
                                        └── нет → «файл не подошёл, пришлите другой»

Как проходит квиз

Когда файл прошёл проверку, бот ведёт ученика по вопросам по одному:

Тип вопроса Как отвечают
inline_choice, situational_scenario по кнопке на каждый вариант; ученик нажимает одну
how_to_say, text_input_translation, fill_blank, error_correction, word_order ученик вводит ответ текстом
match_pairs бот показывает термины и определения двумя списками, определения в другом порядке; ученик вводит каждую пару строкой в формате термин - определение

После каждого ответа бот убирает вопрос из чата, показывает вердикт (✅ Correct! или ❌ Not quite) коротким сообщением без звука, отправляет пояснение следующим сообщением, а затем — следующий вопрос. Повторное нажатие на уже отвеченный вопрос игнорируется, поэтому один и тот же вопрос в чате не дублируется. В конце — счёт: сколько ответов верных, сколько неверных и сколько вопросов в файле. Ответы остаются в записи пользователя, так что неверный можно показать позже.

Два правила решают, засчитан ли введённый ответ:

  • Текстовый ответ сравнивается целиком, без учёта регистра и пробелов вокруг. Он должен совпадать с одним из вариантов в expected_input — правильная формулировка, которой в списке нет, считается ошибкой.
  • Ответ на match_pairs сверяется по парам: строки могут идти в любом порядке, разделителем может быть -, : или =, регистр не важен — но все пары должны быть на месте и должны совпадать с файлом. После неверной попытки бот показывает правильное соответствие.

Частые ошибки

  • Обёртка ```json. Модель любит добавлять её «для красоты». Уберите: файл должен начинаться с {.
  • Умные кавычки. «Ёлочки» и “лапки” вместо " ломают JSON. Замените на обычные.
  • Дубли id. Два q3 — файл не примут. Нумеруйте по порядку.
  • correct_answer не совпадает с вариантом. Даже лишний пробел в конце делает ответ неверным.
  • Пустой expected_input. Список должен содержать хотя бы одну непустую строку.
  • Нереальный таймкод. 00:96:00 не существует; бот это заметит.
  • topic не из topics. Если списки заполнены, ссылки должны совпадать точно.

Зачем повторять

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