Нужна ли умная модель для рутины? Дал четырём моделям пять одинаковых задач и сравнил

от автора

На этой неделе на Хабре спорили, нужны ли программисту самые умные модели: «Я перестал пользоваться самыми умными ИИ‑моделями». Автор считает, что для рутины хватает дешёвых, а упирается всё теперь в скорость. Под статьёй почти триста комментариев. Я прочитал, наверное, половину: мнений много, замеров на одинаковых задачах не нашёл ни одного. Стало интересно, сел и проверил на своих.

Что мерил

Взял пять задач из тех, что обычно скидываю агенту и иду за кофе. Всё на Python, от одного до пяти файлов:

  1. Off-by-one в пагинации, тест падает, надо починить.

  2. Переименовать функцию во всём проекте. С подвохом: в одном месте имя лежит строкой в словаре и вызывается через getattr.

  3. Нормализация российского номера телефона по описанию словами.

  4. Функция на полсотни строк с тройной копипастой. Надо разбить так, чтобы вывод остался байт в байт, а каждая функция влезала в двадцать строк.

  5. Делёж счёта на n человек, в котором теряются копейки. В требованиях написано: лишние копейки достаются первым в списке, и для возвратов с отрицательной суммой тоже должно работать.

Модели — четыре штуки из одного семейства, от младшей к старшей: Haiku 4.5, Sonnet 5, Opus 5, Fable 5.1. Агент везде один (Claude Code в режиме -p, MCP отключил), промпт один, на каждый прогон чистая папка. Каждую задачу гонял по два раза на каждой модели, итого сорок прогонов. Проверял скрытым скриптом, которого модель не видит: там мои тесты и проверка, что видимые тесты никто не подправил.

Сразу скажу, чем это плохо. Задачи мелкие. Два прогона на клетку — это ни о чём. Семейство одно. Так что это замер на коленке, на бенчмарк не претендую. Задачи и проверки выложу, если кому-то надо.

Что получилось

Модель

Решено

Время на 10 прогонов

Ходов агента

Токенов вывода

Цена к младшей

Haiku 4.5

8 из 10

395 с

100

39 137

Sonnet 5

9 из 10

450 с

109

31 359

3,3×

Opus 5

10 из 10

557 с

108

39 455

6,9×

Fable 5.1

10 из 10

341 с

85

23 010

8,8×

Цену считал сам клиент по списочному прайсу. Абсолютные цифры не пишу: устареют быстрее, чем вы это дочитаете. Соотношение проживёт дольше.

Теперь что меня удивило.

Самая дорогая модель оказалась самой быстрой. 341 секунда против 395 у самой дешёвой. Почему — видно в соседних колонках: ходов меньше, текста почти вдвое меньше. Младшая модель очень много рассуждает, 18 754 токена размышлений на десять прогонов, у старшей 2 674. Токены у неё летят быстрее, только их надо больше, и в сумме выходит дольше. Правда, Opus, вторая сверху, вообще самая медленная из четырёх, так что «дороже значит быстрее» тоже неправда. Я для себя решил смотреть на время задачи целиком. Токены в секунду его не предсказывают.

Дальше. Четыре задачи из пяти решили все модели, оба раза. Пагинация, переименование с подвохом, телефон, рефакторинг: 32 прогона из 32. Если у вас рутина примерно такая, девятикратная разница в цене вам ничего не даёт.

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

Пятая задача

Первое, что приходит в голову:

base, remainder = divmod(total_cents, n)result = [base] * nfor i in range(remainder):    result[i] += 1

1000 на троих даёт [334, 333, 333], красота. А вот возврат, −1000: деление в Python округляет вниз, и получается [-333, -333, -334]. Лишняя копейка уехала в конец списка. То есть человек заплатил на копейку больше остальных, а при возврате получит на копейку меньше. В требованиях было «лишние копейки достаются первым», так что мимо.

Haiku написала именно так оба раза. Sonnet один раз из двух. Opus и Fable все четыре раза делили модуль суммы, а знак возвращали потом. И в отчёте сами объяснили зачем: если делить отрицательное число в лоб, лишняя копейка упадёт в конец, а в требованиях наоборот. Fable в одном прогоне ещё и скрипт написала, перебрала все суммы от −300 до 299 при n от 1 до 11. Я её об этом не просил.

Но больше всего мне понравился отчёт младшей модели. Цитирую как есть:

✓ Остаток достаётся первым в списке✓ Работает для возвратов: split_bill(-100, 3) = [-33, -33, -34]

Две галочки подряд, и вторая строка опровергает первую. Видимый тест зелёный, отчёт бодрый, а баг вылезет на первом же возврате.

Справедливости ради: требование про возвраты можно прочитать по‑разному, и кто‑нибудь в комментариях наверняка скажет, что [-333, -333, -334] тоже нормально. Может, и так. Но старшие модели эту развилку увидели и объяснили, что выбрали. Младшая просто поставила галочку.

Что я теперь делаю

Задачи, где всё расписано и есть тесты, отдаю младшей модели. 32 из 32, и дешевле почти на порядок.

Если в требованиях мелькает «и для отрицательных», «и для пустого», «как было раньше», беру старшую. Код дешёвая модель пишет нормально. Она пропускает сам вопрос.

И отчёту «всё работает» верю ровно настолько, насколько сам вижу проверку. Один скрытый тест на граничный случай дешевле любой модели.

А у вас как? Кто гоняет дешёвые модели на рутине, на чём они у вас сыпятся?

Короткие заметки и замеры между статьями пишу в канале: https://t.me/agent_field_notes

ссылка на оригинал статьи https://habr.com/ru/articles/1084270/