Гайд

Как составлять карточки — гайд RepeatMe

Хорошая карточка — половина запоминания. Этот гайд объясняет, как устроены карточки RepeatMe, когда какой режим уместен и как не наступить на типичные грабли: синонимы, словарная и контекстная формы, разрывные выражения. Он же — спецификация для ИИ: машинную версию формата можно отдать любой модели по ссылке /prompts/v1/card-format.

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

Карточка — единица знания. У неё один или несколько кейсов (cases) и для каждого кейса — набор режимов (modes), в которых это знание проверяется.

  • Кейс (case) = пара front / back. Один кейс = обычная карточка. Несколько кейсов = «карточка-правило» (например, спряжение to be): при показе берётся случайный кейс, а расписание повторений — одно на всю карточку.
  • Режим (mode) = форма подачи: переворот, выбор варианта, ввод ответа, заполнение пропуска, подбор пар.

Профиль — пресет набора режимов, отражает намерение (intent):

Профиль Режимы Для чего
Факт Flip, ReverseFlip, Choice, ReverseChoice, Type, Match факты, формулы, определения — самооценка + авто-проверка дискретного ответа
Лексика / перевод весь набор слова, выражения, грамматика — дистракторы, синонимы, предложения

Профиль — лишь предзаполнение набора режимов; автор может менять режимы вручную. Профиль не ограничивает доступные режимы: факты спокойно используют Choice/Type/Match, если есть дистракторы или точный ответ. Правила генерации для лексики языко-независимы — отдельного подтипа «английский / французский» не нужно.

«Что показываем» ≠ «что принимаем»

  • back / frontканоническая форма: то, что показывается при раскрытии ответа.
  • Принимаемые ответы — множество форм, которые засчитываются как верные (синонимы, варианты рода/числа, орфографические варианты). Любая из них = верно; каноническая форма принимается всегда.

Принимаемые ответы живут в данных режима (answers), а не в кейсе: у Flip их быть не должно, а кейс держит только общие для всех режимов поля.

context и explanation — две разные подсказки; их легко перепутать:

  • context — показывается вместе с вопросом, до ответа. Его единственная задача — снять реальную неоднозначность: ставь его, когда по одному front правильных ответов больше одного, чтобы выбрать нужное прочтение. Омоним: front: "язык"context: "орган" ⇒ просят tongue, а не language. Кейс про артикль: front: "Я вижу птицу.", back: "I see a bird." — перевод the bird так же верен, поэтому context: "какую-то, не конкретную" выбирает a. Лакмус: был бы без подсказки правильным и другой ответ? Если нет — context ОБЯЗАН остаться null, null — поведение по умолчанию.
  • context уточняет, что front значит, и никогда — как устроен ответ: он НЕ должен содержать ответ или его часть и НЕ должен намекать на проверяемое правило. На карточке про a/an подсказка "university начинается с согласного звука [j]" пересказывает само проверяемое правило и превращает вспоминание в чтение — этой фразе место в explanation. Ученик, не знающий правила, не должен узнать из context ничего.
  • context показывается только когда промпт — сторона front (прямые режимы: Flip, Type, Choice, ClozeType, ClozeChoice); в обратных режимах и в Match не выводится. Поэтому неоднозначность обратной стороны context не лечит: если один и тот же back отвечает на разные front (многозначное слово), варианты перечисляются в answers обратных режимов — см. E18. Уточнение держи только в context, не вписывай его в сам front (front: "высокий", context: "о росте" — а не front: "высокий (о росте)"): всё, что внутри front, становится частью термина — попадёт в варианты выбора, в правильный ответ обратных режимов, в поле ввода и в списки терминов. В front — только чистый термин.
  • explanation — показывается после ответа (кнопка-лампочка): разбор, правило или мнемоника; может ссылаться на ответ и правило.

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

title: ставь только когда он добавляет сведения, которых нет ни в front, ни в context — грамматическая пометка («Phrasal verb»), категория, регистр. Никогда не копируй front в title: это дублирует то, что ученик и так видит. В большинстве карточек title = null.

Формат answers (важно): массив слотов — по одному слоту на каждый пропуск {{answer}} (для Type/ReverseType слот ровно один). Слот — массив принимаемых форм, контекстная форма первой. Число {{answer}} в sentence обязано равняться числу слотов.

Два разных смысла «нескольких ответов» — не путать:

  • Синонимы / альтернативы — подходит любой из вариантов (till / until; оба / обе) → кладём в один слот: [["оба","обе"]].
  • Последовательность / разные пропуски — нужны все части, каждая на своём месте (3 формы неправильного глагола; разрывное from … till …) → по слоту на часть: [["from"],["till","until"]].
  • Разные значения одного слова (fair = «справедливый» и «ярмарка») ведут себя как синонимы — один слот — но только с той стороны, где вопрос их не различает. Обратный промпт у обоих значений один (fair), поэтому в answers обратных режимов идут все значения; прямой промпт у каждого свой, и там слот остаётся одиночным. См. рецепт многозначного слова и правило E18.

Слот существует только для пропуска {{answer}} — не добавляй слот для слов, которые остаются видимыми в предложении. Для "Our team {{answer}} new features." правильно [["develops","builds"]], а не [["develops","builds"],["new features"]] (1 пропуск против 2 слотов — импорт это отклонит).

Все равноправные формы — в слот, а не одну «каноническую». Ввод сверяется посимвольно: любая верная форма, которой нет в слоте, засчитывается как ошибка и роняет карточку в переучивание. Поэтому в слот идёт всё, что ты сам признал бы верным ответом:

  • вежливость и адресат: успокойтесь / успокойся;
  • род и число, когда задание их не задаёт: оба / обе, рад / рада;
  • вид, когда уместны оба: остыть / остывать;
  • равноправные синонимы перевода: till / until;
  • разные значения многозначного слова, когда промпт их не различает: fairсправедливый / ярмарка;
  • служебное слово, которое можно опустить: to go for a walk / go for a walk.

Регистр, ё/е, вид кавычек и знак в конце ответом не считаются — приложение приводит ввод к общему виду само, перечислять такие написания не нужно. Первой в слоте по-прежнему стоит основная форма — её карточка показывает как правильный ответ.

{ "front": "Calm down", "back": "Успокойтесь",
  "modes": [
    { "type": "Flip" },
    { "type": "Type", "data": { "answers": [["Успокойтесь", "Успокойся"]] } }
  ] }

Когда правильный ответ — отсутствие слова (нулевой артикль, нулевой предлог), принимаемая форма — пустая строка, и back тоже пустой. Не выдумывай символ-заменитель (, 0, нет артикля): приложение само подписывает пустой ответ, поэтому в режимах с вводом ученик оставляет поле пустым, а в режимах с выбором выбирает вариант «ничего».

{ "front": "article with most country names (France, Russia)", "back": "",
  "modes": [
    { "type": "Flip" },
    { "type": "Choice", "data": { "distractors": ["a", "an", "the"] } },
    { "type": "ClozeType", "data": { "sentence": "She lives in {{answer}} France.", "answers": [[""]] } },
    { "type": "ClozeChoice", "data": { "sentence": "She lives in {{answer}} France.", "answers": [[""]], "distractors": ["a", "an", "the"] } }
  ] }

В текстах ответов не должно быть символа | (он служебный — разделяет варианты в передаче).

Ловушка словарной формы (citation vs in-context)

Слово в словарной форме несёт служебные элементы, которых в предложении быть не должно:

  • инфинитивное to: to go for a walk → в предложении «we often go for a walk»;
  • артикль: an apple → «I ate the apple»;
  • связку: be keen on → «he is keen on …»;
  • спряжение: wait for → «I'm waiting for …».

Правило: в предложении/ответе используем форму, в какой слово реально стоит в контексте. Если она отличается от канонической — кладём обе в слот, контекстную первой: [["go for a walk","to go for a walk"]].

Рецепты по типу данных

Ниже data показан как объект — так его принимают вкладка JSON формы карточки и импорт колоды. (В сыром API POST /api/cards data — это JSON-строка: тот же объект, сериализованный в строку.)

Факт / формула / определение → профиль Факт

{ "front": "Площадь круга", "back": "πr²",
  "modes": [ { "type": "Flip" }, { "type": "ReverseFlip" } ] }

Минимально — только самооценка. Если есть правдоподобные неверные значения того же типа, добавь Choice с дистракторами (факты тоже авто-проверяются — это не делает их «лексикой»).

Слово → перевод → профиль Лексика / перевод

{ "front": "яблоко", "back": "apple",
  "modes": [
    { "type": "Flip" }, { "type": "ReverseFlip" },
    { "type": "Type" },
    { "type": "Choice",  "data": { "distractors": ["banana","orange","grape"] } },
    { "type": "ClozeType", "data": { "sentence": "I eat an {{answer}} every day.", "answers": [["apple"]] } }
  ] }

Синонимы / варианты рода → один слот answers

Хранить front: "оба, обе" нельзя (придётся вводить буквально, с запятой). Вместо этого — канон + принимаемые варианты на стороне ответа:

{ "front": "оба", "back": "both",
  "modes": [
    { "type": "ReverseType", "data": { "answers": [["оба","обе"]] } },
    { "type": "Type" }
  ] }

Теперь при ReverseType засчитывается и оба, и обе.

Многозначное слово (несколько значений) → одна карточка, кейс на значение

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

Чинится на стороне ответа, тем же механизмом, что и синонимы: в слоте answers обратных режимов перечислены все значения (значение самого кейса — первым, оно показывается как правильный ответ), а соседнее значение никогда не попадает в дистракторы ReverseChoice — оно там было бы вторым верным вариантом.

{ "title": "fair",
  "cases": [
    { "front": "справедливый", "back": "fair",
      "modes": [
        { "type": "Flip" },
        { "type": "Choice", "data": { "distractors": ["unfair", "honest", "equal"] } },
        { "type": "ReverseChoice", "data": { "distractors": ["честный", "равный", "нечестный"] } },
        { "type": "ReverseType", "data": { "answers": [["справедливый", "ярмарка"]] } },
        { "type": "ClozeType", "data": { "sentence": "The judge made a {{answer}} decision.", "answers": [["fair"]] } }
      ] },
    { "front": "ярмарка", "back": "fair",
      "modes": [
        { "type": "Flip" },
        { "type": "Choice", "data": { "distractors": ["market", "festival", "exhibition"] } },
        { "type": "ReverseChoice", "data": { "distractors": ["рынок", "фестиваль", "выставка"] } },
        { "type": "ReverseType", "data": { "answers": [["ярмарка", "справедливый"]] } },
        { "type": "ClozeType", "data": { "sentence": "We went to a street food {{answer}}.", "answers": [["fair"]] } }
      ] }
  ] }

Одно слово — одна карточка: значения учатся и планируются вместе, а не соревнуются за один и тот же промпт из разных карточек. ReverseFlip можно оставить — он самооценочный, ученик сам засчитает своё значение. Match безопасен: две плитки с одинаковым текстом в один раунд не попадают. title здесь оправдан — он называет само слово, которого нет ни в одном front.

Словарная форма с to / артиклем → контекстная форма + принимаемый вариант

{ "front": "идти на прогулку", "back": "to go for a walk",
  "modes": [
    { "type": "Type", "data": { "answers": [["to go for a walk","go for a walk"]] } },
    { "type": "ClozeType", "data": {
        "sentence": "After dinner we often {{answer}} in the park.",
        "answers": [["go for a walk","to go for a walk"]] } }
  ] }

В предложении — go for a walk (контекстная форма первой), словарная — как принимаемый вариант.

Разрывное выражение from … till … → слот на часть

{ "front": "с … до …", "back": "from … till …",
  "modes": [
    { "type": "ClozeType", "data": {
        "sentence": "The shop is open {{answer}} 9 a.m. {{answer}} 6 p.m.",
        "answers": [["from"],["till","until"]] } },
    { "type": "ClozeChoice", "data": {
        "sentence": "The shop is open {{answer}} 9 a.m. {{answer}} 6 p.m.",
        "answers": [["from"],["till","until"]],
        "distractors": ["at","on","in"] } }
  ] }

Два пропуска {{answer}} ↔ два слота. В ClozeChoice кнопка показывает канонический вариант (from / till); until не предлагается как (неверная) кнопка.

Неправильный глагол (3 формы) → 3 пропуска

{ "front": "идти", "back": "go, went, gone",
  "modes": [
    { "type": "ClozeType", "data": {
        "sentence": "{{answer}}, {{answer}}, {{answer}}",
        "answers": [["go"],["went"],["gone"]] } }
  ] }

Так проверяется каждая форма, а не «угадай пунктуацию» одной длинной строки.

Грамматическое правило → одна карточка, много кейсов, пропуск в точке решения

Ученик путает do/does. Неправильно: карточка на каждое исправленное предложение — заучится предложение, а не правило. Правильно: одна карточка-правило, где каждый кейс — новое предложение, пропуск стоит ровно в точке принятия решения, а дистракторы — именно те формы, которые путаются:

{ "title": "Present Simple questions: do or does?",
  "explanation": "he/she/it → does; I/you/we/they → do. After does the main verb returns to its base form.",
  "cases": [
    { "front": "Она живёт в Польше?", "back": "Does she live in Poland?",
      "modes": [
        { "type": "ClozeChoice", "data": { "sentence": "{{answer}} she live in Poland?", "answers": [["Does"]], "distractors": ["Do", "Did", "Is"] } },
        { "type": "ClozeType", "data": { "sentence": "{{answer}} she live in Poland?", "answers": [["Does"]] } },
        { "type": "Flip" }
      ] },
    { "front": "Ты работаешь по выходным?", "back": "Do you work on weekends?",
      "modes": [
        { "type": "ClozeChoice", "data": { "sentence": "{{answer}} you work on weekends?", "answers": [["Do"]], "distractors": ["Does", "Did", "Are"] } },
        { "type": "ClozeType", "data": { "sentence": "{{answer}} you work on weekends?", "answers": [["Do"]] } },
        { "type": "Flip" }
      ] },
    { "front": "Почему он изучает английский?", "back": "Why does he study English?",
      "modes": [
        { "type": "ClozeChoice", "data": { "sentence": "Why {{answer}} he study English?", "answers": [["does"]], "distractors": ["do", "did", "is"] } },
        { "type": "ClozeType", "data": { "sentence": "Why {{answer}} he study English?", "answers": [["does"]] } },
        { "type": "Flip" }
      ] }
  ] }

Настоящая карточка продолжается до 6–12 кейсов: меняй подлежащие, глаголы и темы и придумывай новые предложения сверх тех, что ученик уже видел. При каждом показе берётся случайный кейс, поэтому расписание отслеживает правило, а не какое-то одно предложение. Все кейсы и все режимы держатся одного решения, о котором карточка, — здесь пропуск всегда на do/does. Режим, прячущий другое слово (например, смысловой глагол), проверяет другое знание и относится к другой карточке.

context у кейса карточки-правила уместен только когда у front больше одного правильного ответа. Канонический пример — артикли: в русском их нет, поэтому для front: "Я вижу птицу." верны и I see a bird, и I see the birdcontext: "какую-то, не конкретную" выбирает нужный смысл, а без этой подсказки дистрактор the был бы вторым правильным вариантом (некорректный режим выбора). Никогда не пересказывай в context триггер правила — context: "university начинается с согласного звука [j]" на карточке про a/an выдаёт ответ до вспоминания. Если у кейса один правильный ответ — context = null; пересказ правила живёт в explanation карточки и показывается после ответа.

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

{ "front": "артикль с большинством стран (Франция, Россия)", "back": "",
  "modes": [
    { "type": "Flip" },
    { "type": "Choice", "data": { "distractors": ["a", "an", "the"] } },
    { "type": "ClozeType", "data": { "sentence": "She lives in {{answer}} France.", "answers": [[""]] } },
    { "type": "ClozeChoice", "data": { "sentence": "She lives in {{answer}} France.", "answers": [[""]], "distractors": ["a", "an", "the"] } }
  ] }

Соседние кейсы ("back": "the" для the UK, "back": "a" для a doctor) по той же причине несут "" среди своих distractors: «без артикля» должен оставаться живым вариантом везде, иначе ученик выберет нужный артикль методом исключения.

Путающиеся слова → одна карточка-различение, кейсы чередуют верный член

Ученик путает speak (уметь говорить на языке) и learn (процесс изучения). Одна карточка на один набор путающихся слов; дистракторы — остальные члены набора, а кейсы чередуют, какой член верный, — тогда ученику приходится различать, а не заучивать один фиксированный ответ:

{ "title": "speak vs learn",
  "explanation": "speak = уметь говорить на языке; learn = учить, осваивать (процесс).",
  "cases": [
    { "front": "говорить (на языке)", "back": "speak",
      "modes": [
        { "type": "Flip" }, { "type": "Match" },
        { "type": "ClozeChoice", "data": { "sentence": "Do you {{answer}} Polish?", "answers": [["speak"]], "distractors": ["learn", "study", "talk"] } },
        { "type": "ClozeType", "data": { "sentence": "Do you {{answer}} Polish?", "answers": [["speak"]] } }
      ] },
    { "front": "учить, осваивать (язык)", "back": "learn",
      "modes": [
        { "type": "Flip" }, { "type": "Match" },
        { "type": "ClozeChoice", "data": { "sentence": "I {{answer}} Polish and English.", "answers": [["learn", "study"]], "distractors": ["speak", "talk"] } },
        { "type": "ClozeType", "data": { "sentence": "I {{answer}} Polish and English.", "answers": [["learn", "study"]] } }
      ] }
  ] }

Это карточка слова, поэтому во front/back — только слово и его значение; предложения с контекстом живут внутри data cloze-режимов. Так Flip остаётся чистым (слово ↔ значение, а не пара предложений) и открывается Match — самый быстрый режим, чтобы прогнать много карточек-слов за раз. Если в предложение честно подходят несколько членов набора — принимай их все одним слотом answers (как learn / study выше): дистрактор никогда не должен совпадать с принимаемым вариантом. Если предложение само по себе не задаёт единственное прочтение — добавь кейсу подсказку context, а не искажай предложение.

Одно слово, несколько контекстов → одна карточка, кейс на контекст

Ученик неверно употребил конкретное слово (make a service → нужно develop a service). Сфокусируй карточку на этом слове и покажи его работу в разных предложениях — при каждом показе случайный кейс:

{ "title": "Collocation: develop (software)",
  "explanation": "develop / build a service, an app — not \"make a service\".",
  "cases": [
    { "front": "разрабатывать (сервис, приложение)", "back": "develop",
      "modes": [
        { "type": "Flip" }, { "type": "ReverseFlip" }, { "type": "Type" },
        { "type": "ClozeType", "data": { "sentence": "I {{answer}} a spaced repetition service.", "answers": [["develop", "build"]] } }
      ] },
    { "front": "разрабатывать (сервис, приложение)", "back": "develop",
      "modes": [
        { "type": "ClozeChoice", "data": { "sentence": "We {{answer}} mobile apps for banks.", "answers": [["develop", "build"]], "distractors": ["make", "do", "invent"] } },
        { "type": "ClozeType", "data": { "sentence": "Our team {{answer}} new features every month.", "answers": [["develops", "builds"]] } }
      ] }
  ] }

Вводимый ответ — всегда слово, никогда не целое предложение. Обрати внимание на ClozeType второго кейса: контекстная форма (develops) стоит в слоте первой — по правилам слотов ответов.

Факт с дистракторами (ПДД-стиль, родной язык)

{ "front": "Что означает сплошная линия разметки?", "back": "Пересекать запрещено",
  "modes": [
    { "type": "Flip" },
    { "type": "Choice", "data": {
        "distractors": ["Можно перестраиваться","Только для автобусов","Парковка разрешена"] } }
  ] }

Дистракторы — правдоподобные, но неверные факты. (Фрейминг «перевод» здесь неуместен — это колода с намерением fact.)

Текстовый формат (cards)

Карточки можно писать простым текстом вместо JSON — именно его просят у чат-бота промпты «Свой ИИ», и его же проще всего писать руками. Карточка называет то, что знает, — стороны, принимаемые формы, неверные варианты, предложение с пропуском, — а режимы собираются из этого и меню режимов колоды. Те же карточки занимают примерно втрое меньше символов, чем в JSON, обрезанный ответ теряет одну карточку, а не всю колоду, а превью импорта говорит по каждой карточке, что именно не удалось прочитать. Его читают диалог «Импорт колоды», поля вставки ИИ-путей и результат урока; машинам контракт отдаётся по адресу /prompts/v2/card-format.

Текстовый контракт колоды (формат cards). То, что читают диалог «Импорт колоды», поля вставки «Свой ИИ» и результат урока — рядом со старым JSON колоды, который они по-прежнему принимают. Документ — это шапка (ключи колоды) и карточки. Каждая строка — ключ: значение, ключ в нулевой колонке; карточка называет факты, которые знает, — стороны, принимаемые формы, неверные варианты, предложение с пропуском — и никогда не перечисляет режимы: их собирают из фактов и меню режимов колоды (см. таблицу фактов ниже).

deck: English basics
description: Core vocabulary, first 100 words.
front-language: ru
back-language: en
modes: Flip | ReverseFlip | Match | Type | Choice | ClozeType | ClozeChoice
---
front: яблоко
back: apple
wrong: banana | orange | grape
cloze: I eat an {{apple}} every day.
---
note: till and until are interchangeable; from opens the interval.
front: с … до …
back: from … till …
cloze: The shop is open {{from}} 9 a.m. {{till|until}} 6 p.m.
cloze-wrong: at | on | in

Грамматика:

  • --- на отдельной строке начинает новую карточку. case: (значение — необязательная метка) начинает ещё один кейс той же карточки — карточка-правило с многими примерами, слово с несколькими смыслами. Второй front: без того и другого читается как новая карточка.
  • Значение продолжается на следующих строках, если они начинаются хотя бы с одного пробела (Markdown внутри стороны, многострочная заметка). Строка без ключа и без отступа игнорируется.
  • Спискиwrong, accept, front-wrong, front-accept, cloze-wrong, tags, modes — разделяют элементы через |. Пустой вариант («здесь нет слова») пишется как "".
  • Больше ничего не экранируется и не берётся в кавычки. Зарезервированы только: ключ в нулевой колонке, --- в нулевой колонке, | внутри списка, {{ }} внутри cloze.
  • Весь документ печатается внутри одного блока ```cards; всё вне блока игнорируется, поэтому внутри него не должно быть другого текста.

Ключи:

Где Ключ Смысл Имя в JSON
шапка deck название колоды (обязательно) title
шапка description одно-два предложения о колоде description
шапка front-language, back-language коды языков (ru, en, …) стороны вопроса — родного языка ученика — и изучаемой стороны, по которой подбирается произношение frontLanguage, backLanguage
шапка modes меню режимов колоды: все режимы, которые может получить карточка. Карточка получает те из них, что позволяют её факты и правила размера
шапка tags, visibility (private / unlisted / public) необязательно tags, visibility
карточка title необязательная метка (грамматическая пометка, тема); никогда не копия front title
карточка note заметка, показываемая после ответа — пересказ правила, мнемоника; может называть ответ explanation
карточка format markdown, если сторона использует Markdown; по умолчанию простой текст contentFormat
кейс front сторона вопроса (обязательно) — один чистый термин, без пояснений в скобках front
кейс back сторона ответа — одна каноническая форма; пустая, когда ответ «нет слова» back
кейс context подсказка, показываемая вместе с вопросом — ТОЛЬКО когда у front больше одного верного ответа; никогда не намекает на ответ или правило context
кейс accept другие формы back, которые ученик может ввести (accept: go for a walk) Typeanswers
кейс front-accept другие вводимые формы front ReverseTypeanswers
кейс wrong три неверных варианта на языке back Choicedistractors
кейс front-wrong три неверных варианта на языке front ReverseChoicedistractors
кейс cloze одно предложение с ответом внутри {{ }} — см. правила пропусков sentence + answers
кейс cloze-wrong три неверных варианта для пропуска ClozeChoicedistractors

Никогда не пишите front-image, back-image, back-audio и front-audio: картинки загружает владелец колоды, произношение прикрепляет сервер — выдуманная ссылка всегда битая (E19).

Там, где правило ниже говорит именами JSON, читайте его через последнюю колонку: «слот» — это группа {{a|b}} в cloze или back вместе со списком accept; «дистракторы» — wrong, front-wrong и cloze-wrong; «предложение» (sentence) — cloze; explanationnote; «данные» (data) режима — эти самые факты.

Как факты становятся режимами. Карточка никогда не перечисляет режимы. Из меню modes колоды каждый кейс получает:

Факт кейса Какой режим включает Только если
Flip, ReverseFlip есть в меню; ReverseFlip требует непустой back
Match есть в меню и обе стороны размером со слово
back (+ accept) Type есть в меню и back — слово или короткая фраза, не целое предложение
front (+ front-accept) ReverseType есть в меню и front — слово или короткая фраза
wrong Choice есть в меню
front-wrong ReverseChoice есть в меню
cloze ClozeType есть в меню
cloze + cloze-wrong ClozeChoice есть в меню
cloze + wrong без cloze-wrong Choice и ClozeChoice делят один список wrong ответ пропуска — сам back

Поэтому карточка размером с предложение (правило, отрабатываемое примерами) естественно получает cloze-режимы и Flip, но не ввод, а карточка-слово — Flip / ReverseFlip / Match / Type плюс то, что добавляют её факты. Чтобы режим не попал на одну карточку, не пишите его факт: нет wrong: — нет Choice. Давайте факт только тогда, когда можете заполнить его хорошо — три правдоподобных неверных варианта, естественное предложение с пропуском в точке решения; слабый факт хуже, чем никакого.

Пропуски и принимаемые формы (важно). Ответ пропуска стоит внутри предложения:

front: Она живёт в Польше?
back: Does she live in Poland?
cloze: {{Does}} she live in Poland?
cloze-wrong: Do | Did | Is

Два разных смысла «несколько ответов» — не путайте их:

  • Синонимы / альтернативы — верен ЛЮБОЙ из них (till / until; оба / обе) → одна группа через |, форма в контексте — первой: {{till|until}}. Первая — та, что карточка показывает как правильный ответ.
  • Последовательность / отдельные пропуски — нужны ВСЕ части, каждая на своём месте (три формы неправильного глагола; разрывное from … till …) → одна группа на пропуск: cloze: The shop is open {{from}} 9 a.m. {{till|until}} 6 p.m.
  • Разные смыслы одного слова (fair = «справедливый» и «ярмарка») ведут себя как синонимы, но только на той стороне, где вопрос их не различает: каждый смысл — свой кейс со своим front, а обратная сторона принимает все смыслы (E18).

Группа существует только для пропуска — то, что остаётся видимым в предложении, ответом не является, как и всё вне {{ }}. В Our team {{develops|builds}} new features. один пропуск и одна группа.

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

  • вежливость и адресат: {{успокойтесь|успокойся}};
  • род и число, когда вопрос их не задаёт: {{оба|обе}}, {{рад|рада}};
  • вид, где подходят оба: {{остыть|остывать}};
  • равноценные переводы: {{till|until}};
  • смыслы многозначного слова, которые вопрос не различает;
  • служебное слово, которое можно опустить: {{go for a walk|to go for a walk}}.

Регистр букв, ё/е, вид кавычек и знак в конце — не ответы: приложение выравнивает их само, такие написания перечислять не нужно.

То же правило — для вводимого ответа самой карточки: back держит одну каноническую форму, а accept перечисляет остальные (back: Успокойтесь / accept: Успокойся; back: to go for a walk / accept: go for a walk). Для стороны вопроса — front-accept.

Когда верный ответ — «здесь нет слова» (нулевой артикль, нулевой предлог), пропуск пустой и back тоже пустой — последовательно во всех фактах кейса. Никогда не выдумывайте заменитель (, 0, no article): приложение показывает пустой ответ своей подписью, ученик оставляет поле пустым в режимах ввода и выбирает вариант «нет слова» в режимах выбора.

front: article with most country names (France, Russia)
back:
wrong: a | an | the
cloze: She lives in {{}} France.

Соседние кейсы (back: the для the UK, back: a для a doctor) по той же причине несут "" среди своих wrong: «нет артикля» должен оставаться живым вариантом везде, иначе ученик выбирает верный артикль методом исключения.

В ответах и вариантах не должно быть символа | — он разделяет альтернативы и элементы списков.

deck: English basics — sample
description: A tiny sample deck showing every key.
front-language: ru
back-language: en
modes: Flip | ReverseFlip | Match | Type | ReverseType | Choice | ReverseChoice | ClozeType | ClozeChoice
---
front: яблоко
back: apple
wrong: banana | orange | grape
cloze: I eat an {{apple}} every day.
---
front: язык
back: tongue
context: орган
front-wrong: зуб | нос | ухо
---
title: Phrasal verb
front: идти на прогулку
back: to go for a walk
accept: go for a walk
cloze: After dinner we often {{go for a walk|to go for a walk}} in the park.
---
note: till и until взаимозаменяемы; from задаёт начало интервала.
front: с … до …
back: from … till …
cloze: The shop is open {{from}} 9 a.m. {{till|until}} 6 p.m.
cloze-wrong: at | on | in
---
note: be – was/were – been.
front: быть (3 формы)
back: be – was/were – been
cloze: {{be}}, {{was|were}}, {{been}}
case:
front: он усталый (Present Simple, to be)
back: he is tired
front-accept: он устал

Формат JSON (машинный контракт)

Старшая, полностью явная форма: каждый кейс перечисляет свои режимы с данными. Диалог «Импорт колоды» и все поля вставки по-прежнему её принимают, на ней же говорят экспорт и API. Машине отдаётся по ссылке /prompts/v1/card-format.

Deck JSON contract. The complete deck object — what the "Import deck" dialog and POST /api/decks/import-json accept. Cards carry no deckIds: the deck itself is created by the import.

{
  "title": "string",                  // deck name (required)
  "description": "string | null",
  "frontLanguage": "ru",              // language of the question side (front) — the learner's native language (optional)
  "backLanguage": "en",               // language of the studied side (back) — drives pronunciation (optional; null when not applicable)
  "isPublic": false,
  "cards": [
    {
      "title": "string | null",       // optional label; never copy the front — set it only when it adds information (see the hint rules)
      "explanation": "string | null", // post-answer note (rule recap / mnemonic); MAY reference the answer
      "contentFormat": "PlainText",   // or "Markdown"
      "cases": [
        {
          "front": "string",          // question side (usually the native language)
          "back": "string",           // answer side (usually the studied language)
          "context": "string | null", // cue shown WITH the question; set ONLY when several answers would be correct without it; never hints the answer or the tested rule
          "backAudio": null,          // pronunciation mp3 URL for the back word — ALWAYS null (see the backAudio rule)
          "frontImage": null,         // picture shown WITH the question — ALWAYS null (see the images rule)
          "backImage": null,          // picture revealed after the answer — ALWAYS null (see the images rule)
          "modes": [
            { "type": "Flip", "data": null }   // "type": a mode name; "data": object or null per the mode table below
          ]
        }
      ]
    }
  ]
}

type is one of: Flip, ReverseFlip, Choice, ReverseChoice, Match, ClozeType, ClozeChoice, Type, ReverseType.

The import dialog also accepts a bare array of cards ([ { "cases": … }, … ]) — the deck title is then taken from the dialog's name field. A single card (with deckIds) can be pasted into the JSON tab of the card form.

data по режимам:

Режим data Смысл
Flip, ReverseFlip, Match null данных нет
Type { "answers": [[ ... ]] } (опц.) один слот — принимаемые формы back
ReverseType { "answers": [[ ... ]] } (опц.) один слот — принимаемые формы front
Choice { "distractors": [ 3 items ] } неверные варианты на языке back
ReverseChoice { "distractors": [ 3 items ] } неверные варианты на языке front
ClozeType { "sentence": "... {{answer}} ...", "answers": [[ ... ]] } пропуски + слоты ответов
ClozeChoice { "sentence": "...", "answers": [[ ... ]], "distractors": [ ... ] } пропуски + слоты + неверные

Правила генерации (Lexical):

  • L1. Ставь каждый пропуск/ответ в форме, в какой он стоит в предложении (убери словарное to, проспрягай под подлежащее, убери уже имеющийся артикль) — по грамматике соответствующего языка. Если форма в предложении — отсутствие слова (нулевой артикль, нулевой предлог), пиши пустую строку, а не символ-заменитель.
  • L2. Если контекстная форма ≠ исходному слову — укажи обе, контекстную первой.
  • L3. Синонимы/варианты — отдельные элементы слота, не объединяй запятыми внутри одного элемента.
  • L4. Разрывное выражение — по пропуску/слоту на часть.
  • L5. Дистракторы — правдоподобные, но неверные, того же типа, что и ответ; не дублируй принимаемые варианты.
  • L6. Предложение — естественное и грамматичное.

backAudio (произношение). Аудио озвучивает слово-ответ (back) и показывается кнопкой ▶ рядом с ним во всех режимах. Не придумывай ссылку: URL словарей (напр. api.dictionaryapi.dev) непредсказуем по шаблону, выдуманная ссылка почти всегда битая. Всегда ставь backAudio: null (или опускай поле) — реальную ссылку подставляет сервер, запрашивая словарный API. Аудио осмысленно только для одиночных слов изучаемого языка; для словосочетаний и предложений его нет.

frontImage / backImage (картинки). frontImage показывается вместе с вопросом (картинка как часть вопроса), backImage — только после ответа, как и произношение. Не придумывай ссылки: URL картинки нельзя вывести из названия — выдуманная ссылка почти всегда битая, а рабочую подставить неоткуда. Всегда ставь frontImage: null и backImage: null (или опускай поля) — картинку владелец колоды загружает сам в форме карточки. Единственное исключение: пользователь сам дал прямую https-ссылку на конкретную картинку — тогда её можно поставить как есть.

Намерение (intent) — определяет фрейминг:

intent Когда front / back Профиль
translation изучение слов/выражений/грамматики другого языка родной ↔ изучаемый Лексика / перевод
definition термин ↔ значение на одном языке (биология, право, словарь терминов) термин ↔ определение Лексика / перевод
fact вопрос ↔ факт (ПДД, история, формулы) вопрос/понятие ↔ факт/значение Факт (+ Choice/Match, если есть правдоподобные дистракторы)

Типы знания → форма карточки (определи ДО написания карточек):

Карточка — единица знания, а не один вопрос. Сначала классифицируй, что именно учится, затем выводи форму карточки из типа. Никогда не делай карточку на каждое предложение-пример: сгруппируй материал по стоящему за ним правилу / слову / контрасту и положи примеры в cases одной карточки — при каждом показе берётся случайный кейс, поэтому усваивается паттерн, а не одна заученная строка.

Тип знания Пример Форма карточки Ядро режимов Дистракторы
Правило / паттерн — выбор формы (do/does, артикль, форма глагола, окончание) вопросы с does 1 правило = 1 карточка, 6–12 кейсов: разные предложения на одно правило ClozeChoice + ClozeType, пропуск в точке решения; опционально Flip минимальные пары: do/does/did, work/works
Порядок слов / позиция usually перед смысловым глаголом 1 паттерн = 1 карточка, 5–8 кейсов Choice (целые предложения, отличающиеся только порядком), Flip те же слова, неверный порядок
Контраст-набор (интерференция) speak vs learn vs study 1 набор = 1 карточка; front/back = член набора + его значение; кейсы чередуют, какой член верный ClozeChoice с остальными членами как дистракторами, ClozeType, Flip/Match; подсказка context, когда предложение само по себе неоднозначно остальные члены набора — в этом смысл карточки
Слово / коллокация develop a service 1 слово = 1 карточка, 2–5 кейсов: одно слово в разных предложениях Flip/ReverseFlip/Type/Match (только слово), ClozeType на каждый контекст близкие по смыслу, но неверные здесь
Орфография study, а не stady 1 слово = 1 карточка Type/ClozeType — ввод слова И ЕСТЬ проверка не нужны
Факт / определение столица X; термин ↔ значение 1 факт = 1 карточка, 1 кейс Flip/ReverseFlip; Choice/Match только при правдоподобных неверных фактах того же рода; Cloze/Type обычно не нужны правдоподобные факты того же рода
  • Один вопрос — одно решение. Ввод целого предложения проверяет пять вещей сразу и размывает сигнал ошибки; cloze-пропуск ровно в точке решения изолирует то, что тренируем.
  • Все кейсы и все режимы проверяют одно решение карточки. Карточка про do/does никогда не прячет смысловой глагол в одном из режимов — другое решение живёт на другой карточке.
  • Многозначное слово — одна карточка, кейс на значение. fair = «справедливый» и «ярмарка» — это не две карточки: у них общий обратный промпт, и порознь они превращают обратные режимы в угадайку. Собери значения в одну карточку и перечисли их все в слоте answers обратных режимов (E18).
  • У карточек-слов во front/back — само слово (слово ↔ значение), не предложение; предложения с контекстом живут в data режимов ClozeChoice/ClozeType. Так Flip остаётся чистым и открывается Match — самый быстрый режим, чтобы прогнать много карточек-слов за раз.
  • Регистр букв проверяется только выбором. Вводимые ответы оцениваются без учёта регистра, поэтому правило про заглавную букву (English, а не english) требует Choice/ClozeChoice с дистракторами, отличающимися регистром (English vs english) — проверка через Type/ClozeType молча примет строчный вариант.
  • Воспроизведение сильнее узнавания. Choice/ClozeChoice — ступеньки; всегда добавляй к ним вводимую форму (ClozeType/Type), чтобы знание в итоге воспроизводилось, а не узнавалось.
  • Карточке-правилу нужны свежие примеры. Меняй подлежащие, глаголы и темы за пределами предложений, которые ученик уже видел, — иначе карточка проверяет память об уроке, а не правило.
  • Обычная лексика (intent translation) сохраняет классический базовый набор: Flip, ReverseFlip, Type, Choice, ClozeType (опционально ReverseChoice, ClozeChoice, ReverseType, Match).
  • Упрощай по умолчанию. Не добавляй тяжёлую разметку без пользы: для 3-форменных неправильных глаголов достаточно back: "go, went, gone" + Flip/ReverseFlip/Type.

Чек-лист валидности:

  • E1. У каждого кейса есть front и хотя бы один режим (back может быть пустым — см. E16).
  • E2. Число {{answer}} в sentence равно числу слотов в answers.
  • E3. Каждый слот — непустой массив строк, контекстная форма первой. Сама строка в нём может быть пустой — это ответ «слово не нужно» (E16).
  • E4. Синонимы — отдельные элементы слота, не "a, b" одной строкой.
  • E5. Контекстная форма грамматична в своём предложении (нет «we often to go», «is be keen on»).
  • E6. Дистракторы неверны и не совпадают ни с одним принимаемым вариантом.
  • E7. Карточки с самооценкой (только Flip/ReverseFlip) — без дистракторов/предложений в data.
  • E8. В текстах ответов нет символа | (он служебный).
  • E9. Вводимые ответы (Type/ReverseType, слоты cloze) — слово или короткая фраза, никогда не целое предложение. Чтобы проверить решение внутри предложения — ClozeType/ClozeChoice с пропуском ровно в этой точке.
  • E10. Карточка-правило несёт несколько cases (ориентир — 6+), каждый — новое предложение-пример; одно правило никогда не режется на карточку-на-предложение.
  • E11. Дистракторы Choice/ClozeChoice — минимальные пары проверяемого решения (do/does/did): неверные здесь, но правдоподобно путающиеся; никогда не случайные слова.
  • E12. Различие, которого ввод не видит, — только регистр букв (English vs english) или только ё/е (всё vs все) — проверяется через Choice/ClozeChoice и никогда через Type/ClozeType: ввод оценивается без учёта регистра и с ё = е, и неверная форма была бы засчитана.
  • E13. Все кейсы и все режимы карточки проверяют одно её решение. Карточка про do/does никогда не прячет смысловой глагол в одном из режимов — другое решение = другая карточка.
  • E14. У карточек-слов во front/back — само слово (слово ↔ значение), никогда не целое предложение; предложения с контекстом уходят в data cloze-режимов. Так Flip остаётся чистым и открывается Match. И одна форма, а не список: "вверх ногами; вверх дном; перевёрнутый" ученик увидит перечислением в Match и в вариантах ReverseChoice, а в режимах с вводом должен будет набрать целиком, вместе с точками с запятой. Равноправные формулировки идут вариантами слота (E17), разные значения — разными кейсами с context (E15).
  • E15. context ставится только когда у front больше одного правильного ответа; он выбирает нужное прочтение и никогда не намекает на ответ или проверяемое правило (пересказ правила — в explanation).
  • E16. Когда правильный ответ — отсутствие слова (нулевой артикль, нулевой предлог), он пишется пустой строкой — и в back, и в слоте ответа, одинаково во всех режимах кейса. Никаких символов-заменителей (, 0, нет артикля): приложение подписывает пустой ответ само.
  • E17. Каждая форма, которую засчитал бы автор, — отдельный вариант слота: вежливость (успокойтесь/успокойся), род и число (оба/обе), равноправные синонимы (till/until). Перечисление внутри одной строки ("оба, обе") не работает — его придётся вводить буквально, вместе с запятой. Регистр, ё/е и знаки препинания по краям перечислять не нужно. Обратное тоже верно: ответ, который ученик должен выдать целиком ("red, green, blue"), и цельный термин со слешем (and/or) — это один вариант, разбивать их нельзя.
  • E18. Один вопрос — один набор верных ответов. Два кейса карточки (или две карточки колоды) не могут давать на один и тот же промпт разные ответы: ученик угадывает, какой из них сейчас спрашивают. Прямая сторона (одинаковый front, разный back) разводится разными context на каждом кейсе (E15). Обратная (одинаковый back, разный front — многозначное слово) context'ом не лечится, он в обратных режимах не показывается: ReverseType каждого такого кейса перечисляет в слоте все значения (своё — первым), а ReverseChoice не берёт соседнее значение в дистракторы. Если ни то, ни другое не сделано — оставь у этих кейсов только прямые режимы.
  • E19. Не выдумывай ссылку на картинку. В frontImage/backImage попадает либо /media/card/…, который сайт выдал при загрузке, либо https-ссылка, которую дал сам пользователь, — и ничего больше. Угаданная ссылка — это битая картинка, а битая картинка хуже, чем никакой.

Создание карточек с ИИ

Составлять карточки вручную не обязательно — есть три пути:

  • Базовый ИИ — кнопка «Создать с ИИ» на странице колод: опишите колоду свободным текстом, отредактируйте план и получите готовые карточки.
  • Свой ИИ — тот же диалог собирает готовый промпт: скопируйте его в любой чат-бот (ChatGPT, Claude, Gemini), а ответ вставьте обратно — блок ```cards, который он напечатает, или JSON, если он настоит на своём. Так же работает обогащение полей в форме карточки и в массовом редактировании.
  • Внешний чат после занятия — если вы разбирали тему с ИИ-чатом, дайте ему ссылку /prompts/v2/deck-builder: он задаст пару уточняющих вопросов о ваших слабых местах, соберёт колоду и выдаст текст для импорта.

Импорт

  1. Откройте страницу колод и нажмите «Импорт колоды» (или перейдите по ссылке /decks?import=1 — диалог откроется сам).
  2. Вставьте текст: диалог читает блок ```cards, объект колоды JSON { "title", …, "cards": [ … ] }, просто массив карточек [ { "cases": … }, … ] или по паре вопрос — ответ на строку — название берётся из текста, если он его называет, иначе из поля «Название» диалога.
  3. Прочитайте превью — сколько карточек найдено и, по каждой карточке, что не удалось прочитать — и подтвердите: колода и все карточки создадутся за один шаг.

Отдельную карточку можно вставить во вкладку JSON формы карточки.

Полный пример колоды

Валидная колода-образец, в которой каждый режим встречается хотя бы раз — можно вставить в диалог «Импорт колоды» как есть:

{
  "title": "English basics — sample",
  "description": "A tiny sample deck showing every mode and field.",
  "isPublic": false,
  "frontLanguage": "ru",
  "backLanguage": "en",
  "cards": [
    {
      "cases": [
        {
          "front": "яблоко",
          "back": "apple",
          "backAudio": null,
          "modes": [
            { "type": "Flip" },
            { "type": "ReverseFlip" },
            { "type": "Match" },
            { "type": "Type" },
            { "type": "Choice", "data": { "distractors": ["banana", "orange", "grape"] } },
            { "type": "ClozeType", "data": { "sentence": "I eat an {{answer}} every day.", "answers": [["apple"]] } }
          ]
        }
      ]
    },
    {
      "cases": [
        {
          "front": "язык",
          "back": "tongue",
          "context": "орган",
          "modes": [
            { "type": "Flip" },
            { "type": "Type" },
            { "type": "ReverseChoice", "data": { "distractors": ["зуб", "нос", "ухо"] } }
          ]
        }
      ]
    },
    {
      "title": "Phrasal verb",
      "cases": [
        {
          "front": "идти на прогулку",
          "back": "to go for a walk",
          "modes": [
            { "type": "Type", "data": { "answers": [["to go for a walk", "go for a walk"]] } },
            { "type": "ClozeType", "data": { "sentence": "After dinner we often {{answer}} in the park.", "answers": [["go for a walk", "to go for a walk"]] } }
          ]
        }
      ]
    },
    {
      "explanation": "till и until взаимозаменяемы; from задаёт начало интервала.",
      "cases": [
        {
          "front": "с … до …",
          "back": "from … till …",
          "modes": [
            { "type": "ClozeChoice", "data": { "sentence": "The shop is open {{answer}} 9 a.m. {{answer}} 6 p.m.", "answers": [["from"], ["till", "until"]], "distractors": ["at", "on", "in"] } }
          ]
        }
      ]
    },
    {
      "explanation": "be – was/were – been.",
      "cases": [
        {
          "front": "быть (3 формы)",
          "back": "be – was/were – been",
          "modes": [
            { "type": "Flip" },
            { "type": "ClozeType", "data": { "sentence": "{{answer}}, {{answer}}, {{answer}}", "answers": [["be"], ["was", "were"], ["been"]] } }
          ]
        },
        {
          "front": "он усталый (Present Simple, to be)",
          "back": "he is tired",
          "modes": [
            { "type": "ReverseType", "data": { "answers": [["он усталый", "он устал"]] } }
          ]
        }
      ]
    }
  ]
}