Как устроены домашние задания и проверки
Квизы, задания в браузере, домашние работы с автотестами и уровнями Simple → Boss, ИИ-ревьюер — как всё это работает и как с этим учиться.
Зачем это нужно
Заголовок раздела «Зачем это нужно»Вы учитесь сами, по вечерам, без преподавателя. Значит, кто-то другой должен сказать вам «верно» или «вот здесь ошибка», причём сразу, а не через неделю. В курсе эту роль играют автоматические проверки. Квиз проверяет понимание, задание в браузере — что вы умеете превратить идею в код, а домашняя работа с автотестами — что код работает и на краевых случаяхкраевой случайEN edge caseНеобычный вход на границе допустимого — пустой список, ноль, пустая строка. Именно на них чаще всего ломается код. (необычных входных данных: пустой текст, ноль, один элемент, очень длинная строка) и не тормозит.
Устроено это по образцу лучших курсов по LLM. Тесты, которые сначала все красные, взяты из Stanford CS336. Ступенчатые уровни Simple / Medium / Strong / Boss — из курсов Ли Хунъи в Национальном университете Тайваня. Промпт для ИИ-ревьюера — из задач Фуданьского университета (nndl/llm-beginner). За 15 минут разберёмся, как всем этим пользоваться, и решим первое задание.
Как вас проверяют
Заголовок раздела «Как вас проверяют»| Что | Где | Как проверяется | Зачем |
|---|---|---|---|
| Квиз «Проверь себя» | в конце урока | сразу, с объяснением. Зачёт — от 60 %, пересдавать можно сколько угодно | понимание идей |
| Задание с кодом | на странице урока, Python в браузере | тесты прямо на странице | первая реализация идеи |
| Домашняя работа | в репозитории, папка homework/ | автотесты pytest + уровни | полноценный код, краевые случаи, скорость |
| Карточки | страница «Повторение» | интервальное повторение: алгоритм FSRS сам решает, когда показать карточку снова | чтобы не забыть через месяц |
| Тетрадь ошибок | страница «Тетрадь ошибок» | сюда автоматически попадает каждый неверный ответ в квизе | повторять именно то, что пока не понято |
Ещё одна китайская привычка — отметка о занятиях (дословно «пробить карточку»): календарь дней занятий на странице «Карта курса и прогресс». Цель — не рекорды, а регулярность: несколько вечеров в неделю.
flowchart LR
L["Урок"] --> Q["Квиз ≥ 60%"]
L --> E["Задание в браузере"]
Q --> D["Отметить урок пройденным"]
E --> D
D --> C["Карточки → повторение"]
D --> H["ДЗ модуля<br/>python homework/check.py"]
H --> M{"Уровень ≥ Medium?"}
M -- "да" --> N["Следующая ступень"]
M -- "нет" --> R["Подсказки, ИИ-ревью, ещё попытка"]
R --> HЗадания в браузере
Заголовок раздела «Задания в браузере»Python в браузере работает благодаря Pyodide — обычному Python, пересобранному в формат WebAssembly, который браузер умеет выполнять почти с родной скоростью. Код выполняется на вашем компьютере, а не на сервере. Поэтому задания работают без регистрации, а ваш черновик сохраняется в браузере автоматически. С таким окном вы уже встречались в уроке «Как компьютер выполняет программу».
Кнопки задания:
- ▶ Запустить (Ctrl+Enter) — просто выполнить код и показать, что напечатал
print. - ✓ Проверить (Shift+Enter) — прогнать тесты. Под каждым тестом — его описание и сообщение об ошибке.
- 💡 Подсказка — открывает подсказки по одной, от общей к почти готовой.
- ↺ Сбросить — вернуть исходную заготовку.
- 👁 Решение — появляется после первой проверки. Смотрите его после честной попытки, а не вместо неё.
Ограничения: в браузере доступны чистый Python и научные пакеты, собранные для Pyodide: NumPy, а в следующих ступенях pandas, scikit-learn, SciPy. Такой пакет при первом запуске скачивается из интернета. PyTorch в браузере не работает, поэтому задачи на нём живут в домашних работах.
Минимум Python для этого урока
Заголовок раздела «Минимум Python для этого урока»Python по-настоящему начнётся в модуле 0.2. Но в этом уроке встретится код с функциями, и для первого задания
хватит четырёх вещей. Прочитайте код и комментарии — всё, что после #, Python пропускает:
def double(x): # def — «определить функцию» с именем double и одним аргументом x return x * 2 # return — что функция вернёт. Отступ в 4 пробела обязателен!
print(double(21)) # вызываем функцию с аргументом 21 → печатается 42
name = "Аня"print(f"Привет, {name}!") # f-строка: значение name подставляется в фигурные скобки → Привет, Аня!
print(3 >= 2, 2 >= 3) # сравнение «больше или равно» даёт True (истина) или False (ложь)print(100 * 3 / 5) # деление / всегда даёт дробное число → 60.042Привет, Аня!True False60.0Функция — это именованный кусок кода, маленькая «машинка»: получает значения (аргументы) и возвращает
результат через return. Тесты вызывают ваши функции с разными аргументами и сравнивают результат с ожидаемым.
Ошибки, которые вы почти наверняка увидите в первый раз, и что они значат:
| Сообщение | Что случилось | Как исправить |
|---|---|---|
IndentationError: expected an indented block after function definition | строка после def ...: без отступа | поставьте 4 пробела перед return |
SyntaxError: expected ':' | забыли двоеточие в конце строки def | def greet(name): |
SyntaxError: unterminated string literal | не закрыта кавычка | "Привет, {name}!" — кавычка в начале и в конце |
NameError: name 'nmae' is not defined | опечатка в имени | имя должно в точности совпадать с аргументом |
| тест пишет «вернула None» | нет return: функция что-то посчитала, но ничего не вернула | начните строку с return |
| тест пишет «вернула Ellipsis» | в коде осталось ... из заготовки | замените многоточие своим выражением |
Этого достаточно, чтобы решить задание в конце урока. Разделы ниже про домашние работы с автотестами пригодятся к концу ступени 0, когда дойдёт до первой домашней работы: сейчас прочитайте их для общего представления.
Анатомия домашней работы
Заголовок раздела «Анатомия домашней работы»Каждая домашняя работа — папка в репозитории курса:
homework/s0-hw01-python-warmup/├── README.md # условие: что сделать, уровни, подсказки, промпт для ИИ-ревью, источники├── task.yaml # метаданные: ступень, модуль, файлы, нужен ли GPU, сколько часов├── code/ # ← ЗДЕСЬ вы пишете решение│ └── textstats.py└── tests/ # тесты — не меняйте их ├── conftest.py └── test_textstats.pysolutions/s0-hw01-python-warmup/ # эталонное решениеВ code/ лежит заготовка: функции с готовыми сигнатурами и докстрингами, но без тела.
def tokenize(text: str) -> list[str]: """Разбить текст на слова: нижний регистр, только буквы и цифры (кириллица и латиница).
Пример: "Привет, мир! Hello world" -> ["привет", "мир", "hello", "world"] """ raise NotImplementedErrorЗапись text: str и -> list[str] — это подсказки типов: аргумент text — строка, а функция возвращает
список строк. Python их не проверяет, они для людей и редактора. Тройные кавычки """...""" — докстринг,
описание функции с примером.
Сигнатуру (имя функции, аргументы) менять нельзя: по ней тесты вызывают ваш код. Внутри можно делать что угодно —
добавлять вспомогательные функции, импортировать стандартную библиотеку и NumPy. Команда raise NotImplementedError
сразу останавливает функцию с ошибкой «ещё не реализовано». Пока тело функции — только эта строка, все тесты красные. Так и задумано, как в CS336: вы превращаете красное в зелёное.
Что такое тест
Заголовок раздела «Что такое тест»модульный тестмодульный тест (юнит-тест)EN unit testМаленькая функция, которая вызывает ваш код и проверяет результат через assert. — это маленькая функция, которая вызывает ваш код и проверяет результат через assert:
def test_simple_tokenize_basic(): assert tokenize("Привет, мир! Hello world") == ["привет", "мир", "hello", "world"]assert условие значит «здесь обязано быть верно». Если условие после assert ложно, Python выбрасывает
AssertionError, и тест считается проваленным. Тесты запускает pytest — библиотека для тестов, которую вы
поставили из homework/requirements.txt в прошлом уроке. Она находит файлы test_*.py, а в них — функции test_*,
выполняет каждую и собирает отчёт.
Префикс имени задаёт уровень: test_simple_*, test_medium_*, test_strong_*, test_boss_*.
Напишите свой тест
Заголовок раздела «Напишите свой тест»Тест — это ещё и спецификация: он заставляет решить, как функция должна вести себя в неочевидном случае.
Создайте файл my_first_test.py:
def greet(name): return f"Привет, {name}!"
def test_greet(): assert greet("Аня") == "Привет, Аня!"
def test_greet_empty(): assert greet("") == "Привет!"и запустите python -m pytest my_first_test.py -q. Конец вывода будет таким:
def test_greet_empty():> assert greet("") == "Привет!"E AssertionError: assert 'Привет, !' == 'Привет!'EE - Привет!E + Привет, !E ? ++
my_first_test.py:10: AssertionErrorFAILED my_first_test.py::test_greet_empty - AssertionError: assert 'Привет, !...1 failed, 1 passed in 0.02sПервый тест прошёл, второй нашёл краевой случай, о котором вы не подумали: пустое имя даёт «Привет, !».
pytest даже показал, чем строки различаются: знаки ++ под лишними символами. Теперь нужно решить, что правильно,
и поправить либо функцию, либо тест. Ровно такие краевые случаи и проверяют уровни Strong в домашних работах.
Автогрейдер
Заголовок раздела «Автогрейдер»Команды запускаются из корня репозитория с активированным окружением:
python homework/check.py s0-hw01-python-warmup # проверить своё решениеpython homework/check.py s0-hw01 # можно писать начало idpython homework/check.py s0-hw01 -v # подробный вывод pytest с трассировкойpython homework/check.py --list # все ДЗ и ваш лучший уровень по каждомуПример отчёта на частично готовом решении. Тесты в вашей версии ДЗ могут отличаться, важен формат:
━━━ s0-hw01-python-warmup: Разминка на Python — анализ русского и английского текста ━━━
🥉 Simple: 4/4 ✓ test_simple_tokenize_basic ✓ test_simple_tokenize_digits_and_yo ✓ test_simple_top_words ✓ test_simple_bigrams
🥈 Medium: 0/3 ✗ test_medium_top_words_ties_alphabetical → AssertionError: assert [('в', 1), ('б', 1), ('а', 1)] == [('а', 1), ('б', 1), ('в', 1)] ✗ test_medium_char_counts → NotImplementedError ✗ test_medium_cyrillic_ratio → NotImplementedError
🥇 Strong: 0/1 ✗ test_strong_real_text → NotImplementedError
🐉 Boss: 1/1 ✓ test_boss_performance
Итог: 🥉 Simple — задание зачтено ✅Для перехода на следующую ступень нужен уровень 🥈 Medium.Обратите внимание: Boss-тест на скорость прошёл, но итог — Simple. Правило такое: уровень засчитывается,
только если пройдены все тесты этого уровня и всех уровней ниже. Нельзя получить Boss, не доделав Medium.
Лучший результат автогрейдер сохраняет в homework/.results.json. Этот файл стоит в .gitignore, поэтому
в истории Git ваш прогресс хранят коммиты с решениями: уровень всегда можно восстановить, снова запустив check.py.
Уровни: Simple → Boss
Заголовок раздела «Уровни: Simple → Boss»| Уровень | Что проверяет | Что это значит для вас |
|---|---|---|
| 🥉 Simple | базовая корректность на простых примерах | задание зачтено, идея понята |
| 🥈 Medium | полный функционал, все требования условия | нужно для перехода на следующую ступень |
| 🥇 Strong | краевые случаи, устойчивость, качество | вы пишете код, которому можно доверять |
| 🐉 Boss | скорость, масштаб или исследовательская часть | уровень выше ожидаемого, по желанию |
Задания со ступени 5, которым нужен GPU, проверяют Boss по артефакту: ваш скрипт обучения сохраняет
artifacts/metrics.json, а тест сверяет метрики с порогом. Если файла нет, Boss-тест просто пропускается.
Как читать упавший тест
Заголовок раздела «Как читать упавший тест»Сообщение assert — это подсказка, а не приговор. Разберём строку из отчёта выше:
AssertionError: assert [('в', 1), ('б', 1), ('а', 1)] == [('а', 1), ('б', 1), ('в', 1)]Слева — что вернул ваш код, справа — что ожидалось. Слова те же, частоты те же, отличается только порядок
при равных частотах. Имя теста (ties_alphabetical) прямо говорит, какой порядок нужен. Как это исправить,
оставим вам: это часть ДЗ.
Алгоритм отладки:
- Прочитайте имя теста и сообщение: что ожидалось и что получено.
- Откройте файл теста и найдите этот тест. Там видны точные входные данные.
- Воспроизведите вызов вручную в Python или Jupyter:
from textstats import top_words(из папкиcode/), затемtop_words("в б а", 3). - Добавьте
printвнутрь функции или запустите только этот тест подробно:python -m pytest homework/s0-hw01-python-warmup/tests -k ties -v. - Исправьте код и снова запустите
check.py. Остальные тесты не должны «покраснеть».
Если автогрейдер не запускается
Заголовок раздела «Если автогрейдер не запускается»| Что видите | Почему | Что делать |
|---|---|---|
pytest не запустился. Установлены ли зависимости? | окружение не активировано или в нём нет pytest | source .venv/bin/activate, затем pip install -r homework/requirements.txt |
can't open file '.../homework/check.py' | вы не в корне репозитория | cd в папку ChinaAI (проверьте ls: там должны быть homework/ и site/) |
ДЗ «…» не найдено | опечатка в id | python homework/check.py --list покажет все id |
все тесты падают с NotImplementedError, хотя код написан | правите не тот файл, например копию в solutions/ | решение должно лежать в homework/<id>/code/ |
| проверка не заканчивается | бесконечный цикл в вашем коде | Ctrl+C, затем запустите с -v, чтобы увидеть, на каком тесте зависло |
ИИ-ревьюер: как просить, чтобы он учил
Заголовок раздела «ИИ-ревьюер: как просить, чтобы он учил»Языковая модель отлично проверяет код. Но если попросить «реши задание», вы получите решение и не получите навык.
Поэтому в README каждой домашней работы есть раздел «ИИ-ревью» с готовым промптом. Идея взята из задач
Фуданьского университета (файл eval/tutor_prompt.md в nndl/llm-beginner). Общий шаблон:
Ты — преподаватель курса ChinaAI. Проверь моё решение домашней работы <hw-id>.
Условие и рубрика (из README.md):<вставьте раздел «Что нужно сделать» и таблицу уровней>
Мой код:<вставьте файлы из code/>
Вывод автогрейдера:<вставьте вывод python homework/check.py <hw-id>>
Правила:1. Не пиши за меня исправленный код и не давай готового решения.2. Оцени решение по рубрике: корректность, краевые случаи, читаемость, эффективность.3. Назови 1–3 самые важные проблемы через наводящие вопросы или подсказки.4. Задай мне 3 вопроса на понимание моего же решения.5. Отвечай по-русски.Подойдёт любой сильный ассистент: Claude, DeepSeek, Qwen. Отвечать на вопросы ревьюера полезнее, чем читать его похвалу. Если не смогли ответить, значит, тема понята не до конца.
Практика
Заголовок раздела «Практика»Ваше первое задание (всё нужное для него — в разделе «Минимум Python для этого урока» выше). Оно простое, но проходит весь путь: прочитать условие, написать код, запустить, проверить,
разобраться с красными тестами. Нажмите ▶ Запустить сразу: пока вместо ответов печатается Ellipsis.
Заменяйте ... по одному и запускайте снова. Когда все строки печатают то, что нужно, нажмите ✓ Проверить.
# Первое задание курса. Функция — это «машинка»: получает значения в скобках
# (аргументы) и возвращает результат командой return.
#
# Готовый пример — посмотрите, как устроена функция:
def double(x):
return x * 2 # double(21) вернёт 42
# 1. Приветствие: greet("Аня") должна вернуть строку "Привет, Аня!"
# f-строка f"Привет, {name}!" подставляет значение name внутрь текста.
def greet(name):
return ... # TODO: замените ... (многоточие) на f-строку
# 2. То же с кавычками-«ёлочками»: greet_quoted("Аня") -> "Привет, «Аня»!"
# Внимание: «ёлочки» « и » — другие символы, не обычная кавычка ".
# Скопируйте их прямо отсюда.
def greet_quoted(name):
return ... # TODO
# 3. Процент правильных ответов в квизе: quiz_percent(3, 5) -> 60.0
def quiz_percent(correct, total):
return ... # TODO: 100 * correct / total
# 4. Зачтён ли квиз: порог — 60 % и выше.
# quiz_passed(3, 5) -> True, quiz_passed(2, 5) -> False
# Сравнение >= («больше или равно») само даёт True или False.
def quiz_passed(correct, total):
return ... # TODO: вызовите quiz_percent(correct, total) и сравните с 60
# Кнопка «▶ Запустить» выполнит строки ниже — так вы увидите свои результаты до тестов:
print(double(21))
print(greet("Аня"))
print(greet_quoted("Аня"))
print(quiz_percent(3, 5), quiz_passed(3, 5), quiz_passed(2, 5))
⭐ Задача со звёздочкой: правило уровней
Заголовок раздела «⭐ Задача со звёздочкой: правило уровней»Это задание пока сложнее, чем нужно: в нём словари, циклы и условия из модуля 0.2. Вернитесь сюда после урока «Переменные, типы, условия, циклы» — так работает спиральная программа: тема возвращается, когда вы готовы взять её на новом уровне. Вы запрограммируете то самое правило, по которому автогрейдер курса выставляет уровень.
LEVELS = ["simple", "medium", "strong", "boss"]
def best_level(passed):
"""Лучший уровень по правилу автогрейдера курса (homework/check.py).
passed — словарь вида {"simple": True, "medium": False, ...}:
True, если ВСЕ тесты этого уровня пройдены.
Уровни идут по порядку LEVELS: simple → medium → strong → boss.
- уровень засчитывается, только если пройден он сам и все уровни ниже;
- уровня нет в словаре (в задании нет таких тестов) — просто пропускаем его;
- на первом непройденном уровне останавливаемся;
- если не засчитан ни один уровень — вернуть None.
best_level({"simple": True, "medium": True, "strong": False}) -> "medium"
best_level({"simple": True, "medium": False, "strong": True}) -> "simple"
"""
# TODO: цикл for по LEVELS, внутри — if/continue/break.
# Многоточие ниже — заглушка из заготовки: замените его своим кодом.
return ...
print(best_level({"simple": True, "medium": True, "strong": False}))
print(best_level({"simple": True, "medium": False, "strong": True}))
Проверь себя
Заголовок раздела «Проверь себя»- Проверки: квиз (≥ 60 %), задание в браузере, домашняя работа с автотестами, карточки и тетрадь ошибок с вашими ошибками.
- ДЗ: код пишем в
homework/<id>/code/, проверяем командойpython homework/check.py <id>, эталон вsolutions/<id>/смотрим после попытки. - Уровень засчитывается, только если пройдены все тесты этого и всех нижних уровней. Для перехода дальше нужен Medium.
- Сообщение
assert— подсказка: слева ваш результат, справа ожидаемый. - ИИ-ревьюер полезен, когда ему запрещено решать за вас и велено задавать вопросы.
Дальше: модуль 0.2 — Python с нуля: от первой программы до функций, классов, файлов и мини-проектов. Каждая тема — с задачей в браузере.