Строгая типизация и линтеры как автопилот для LLM: связка mypy --strict и Ruff

Языки с динамической типизацией (Python, JavaScript) исторически прощали небрежность на этапе написания кода, перенося ошибки в рантайм. Но когда код со скоростью 500 строк в минуту пишет ИИ-агент, отсутствие строгой проверки типов превращает каждую задачу в лотерею: угадал ли агент название атрибута в возвращаемом объекте ORM или придумал несуществующее поле?

1. Тайпчекер как детерминированный критик

Когда агент работает в цикле с терминалом, запуск mypy и ruff после правки файла создаёт мгновенную детерминированную обратную связь. Если метод репозитория возвращает UserDTO | None, а агент пытается обратиться к user.balance без проверки на None, строгий тайпчекер немедленно вернёт ошибку с точным номером строки. Агент исправит её за одну секунду сам — без участия ревьюера.

2. Почему неконтролируемый Any и type: ignore опасны

Если в проекте не запрещены неявные Any и голые комментарии # type: ignore, языковая модель выбирает путь наименьшего сопротивления: при первой же сложной ошибке дженериков она просто глушит предупреждение линтера. Поэтому в стандарте VibeCoder School действует жёсткий запрет на подавление типов без указания конкретного кода ошибки и обоснования.

Правило проверки качества: включите флаг enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] в конфигурации Mypy. Это физически не позволит агенту оставить безымянный # type: ignore.

3. Эталонная конфигурация pyproject.toml для AI-Driven проектов

Ниже приведён базовый профиль mypy и ruff, который развёрнут на всех проверочных стендах нашей школы:

pyproject.toml
[tool.mypy]
python_version = "3.12"
strict = true
warn_unreachable = true
disallow_any_unimported = true
disallow_any_generics = true
enable_error_code = [
    "ignore-without-code",
    "redundant-expr",
    "truthy-bool",
    "unused-awaitable",
]
plugins = ["pydantic.mypy"]

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP", "SIM", "ASYNC", "S", "RUF"]
ignore = ["S101"]

4. Обратите внимание на код ошибки unused-awaitable

Одна из самых коварных ошибок при генерации асинхронного кода на Python — забытый оператор await перед вызовом корутины (например, uow.commit() или отправкой уведомления в очередь). В обычном режиме Python просто создаёт объект корутины и молча уничтожает его. Флаг unused-awaitable в Mypy и правила группы ASYNC в Ruff отлавливают 100% таких ошибок на этапе статического анализа.


Читайте также в инженерном блоге