GLM-5.2 собрала виджет «Что сейчас читают» за 15 минут. Что получилось и что с ценой.
Продолжаю серию экспериментов: беру модель из OpenRouter, даю ей живую задачу на своём проекте и смотрю, что получится. В прошлый раз тёмную тему делала DeepSeek V4 Flash, до этого MiMo переписывала страницы авторизации. Сегодня — z-ai/glm-5.2 в связке с Cline и задача посложнее: сквозная фича через весь стек, от миграции базы до вёрстки.
Задача
На главной у меня справа висит баннер с предложением зарегистрироваться — но только для гостей. У залогиненных правая колонка пустует. Хотелось закрыть эту дыру виджетом «Сейчас читают»: топ-5 статей по просмотрам, иконка, нумерация 1–5, заголовок и категория.
Проблема в том, что просмотры на сайте не считались вообще. Никак. Ни поля в базе, ни счётчика в коде. То есть задача только звучит как «добавить блок в сайдбар», а на деле это новое поле в базе, миграция, механизм подсчёта, выборка топа, два эндпоинта и уже потом вёрстка.
Вот промт, который я дал целиком, одним куском:
Нужно добавить на сайт виджет — сейчас читают — топ 5 статей по популярности. Маленькая иконка рядом с «сейчас читают», 5 цифр от одного до пяти, и заголовок статьи + категория. Располагается под баннером предложения регистрации у незарегистрированных пользователей, у зарегистрированных пользователей — на месте баннера. Нужно: механизм подсчёта просмотров статей (сейчас просмотры вообще не считаются), способ получить топ-5 статей по просмотрам. Бэкенд в папке API работает с postgres, статьи хранятся в базе devgeek. Я так понимаю, при входе баннер пропадает и топ-5 статей начинают располагаться выше. Проверь всё сначала и подготовь план. Только сделать без дублирования логики.
Никаких подсказок про архитектуру, никаких имён файлов. Дальше — plan mode.
Что модель нашла сама
Прежде чем что-то предлагать, она прочитала 26 файлов и сделала один поиск по кодовой базе. Три находки из плана мне понравились больше всего, потому что все три — это то, что я бы выяснял руками минут двадцать:
- Просмотров нет нигде. Не только поля в модели — вообще ни следа логики учёта. Это она сформулировала явно, а не молча пошла писать.
- Правая колонка существует только для гостей. Она обёрнута в гостевой гейт целиком, а не блок внутри неё. То есть моё «у зарегистрированных виджет встаёт на место баннера» требовало не просто переставить блок, а вытащить саму колонку из-под гостевого условия.
- Страница статьи серверная и запрашивает статью дважды — один раз для метаданных, второй для рендера. Если инкрементить счётчик прямо в получении статьи, каждый просмотр считался бы за два. Вот это уже настоящая ловушка, на которой я бы поймал себя только после того, как цифры поехали.
Дальше был план на два экрана: восемь пунктов по бэкенду, восемь по фронту, отдельным блоком — логика размещения виджета и раздел «Риски». В рисках, кстати, честно: миграция применяется автоматически при старте, поэтому база должна быть доступна; и «проверю версию пакета» — она действительно потом полезла в конфиг проекта, чтобы убедиться, что нужный метод доступен в этой версии ORM.
Решения, которые я бы засчитал за собеседование
Инкремент атомарный, без загрузки статьи. Не «прочитать сущность, прибавить единицу, сохранить», а обновление одним запросом на стороне базы. Никаких гонок при параллельных открытиях, никакого лишнего трафика к базе.
Индекс частичный. Не просто индекс по счётчику, а с сортировкой по убыванию и фильтром — только опубликованные и не скрытые. Ровно под тот единственный запрос, который к нему ходит. Мелочь, но именно она отличает «работает» от «работает на десяти тысячах статей».
Счётчик вынесен в отдельный клиентский вызов. Ровно то самое решение проблемы двойного счёта: страница серверная и рендерится дважды, поэтому подсчёт живёт в маленьком клиентском компоненте, который стреляет один раз при монтировании и защищён от повторного срабатывания. Побочный бонус — счётчик не накручивается ботами, которые не исполняют JS.
Виджет — один компонент на оба состояния. Мой пункт «без дублирования логики» она поняла правильно: не два похожих блока для гостей и для авторизованных, а один компонент, который просто вставлен в две ветки размещения. Список статей, стили, разметка — в одном месте.
Где споткнулась
Дважды, и обе — операционные мелочи.
Первая: попыталась склеить команды через &&, а у меня PowerShell, который такого не понимает. Прочитала ошибку, переписала через ;, пошла дальше — без моего участия.
Вторая: в проекте два контекста базы, и генератор миграций отказался угадывать. Модель прочитала сообщение об ошибке, добавила явное указание контекста и повторила. Тоже сама.
Это, пожалуй, главное отличие от опыта годовой давности: раньше такие спотыкания превращались в цикл «модель падает — я объясняю — модель падает иначе». Сейчас это просто две лишние строчки в логе.
Обе сборки — бэкенд и фронт — прошли с первого раза, без единой правки руками. Никаких «поправь импорт», никаких красных подчёркиваний в редакторе после того, как агент отрапортовал об успехе.
Весь прогон обошёлся в $1.30.
В эту сумму вошло всё: чтение 26 файлов кодовой базы, поиск по проекту, план на два экрана, реализация шестнадцати пунктов этого плана в двух проектах, генерация миграции, две сборки и разбор двух ошибок по дороге.
Для сравнения — мои прошлые замеры в этой же серии: тёмная тема на DeepSeek V4 Flash обошлась в $0.163, рефакторинг страниц авторизации на MiMo — примерно в $0.20. То есть эта задача вышла в шесть-восемь раз дороже. Но и масштаб другой: там правки в пределах одного слоя, здесь — сквозная фича через базу, API и фронт, с миграцией, индексом и новыми эндпоинтами.
Доллар тридцать за то, на что у меня самого ушёл бы вечер.
Чего в фиче нет
И вот что за эти $1.30 не куплено — пишу честно, чтобы никто не повторил за мной вслепую:
- Дедупликации просмотров нет. F5 = +1. Ни по сессии, ни по IP, ни по времени. Для сайта с нулевым трафиком это нормально, для боевого счётчика — нет.
- Кэша нет. Каждый рендер главной — это запрос в базу за топом. Индекс спасает, но кэш на минуту всё равно нужен.
- На мобильных виджета не видно — правая колонка скрывается на узких экранах. Это соответствует поведению баннера, но по факту большая часть аудитории виджет просто не увидит.
- «Просмотр» = открытие страницы. Не дочитывание, не время на странице. Топ будет отражать кликабельность заголовков, а не качество статей.
Ни один из этих пунктов не был в промте — и модель их не выдумала за меня, что правильно. Но и в «Рисках» она указала только технические, а не продуктовые. Границу между «сделала как просили» и «подумала за меня» стоит держать в голове: думать за вас она не будет.
Вывод
Главное в цифре $1.30 — не то, что это дёшево. Главное, что при такой цене меняется сам способ работы.
Когда прогон стоит доллар, нет смысла вылизывать промт до идеала и бояться неудачного результата. Мой промт был написан в один заход, с опечатками и рассуждением вслух — «я так понимаю, при входе баннер пропадает». Этого хватило. А если бы не хватило, я бы откатил изменения и переформулировал задачу ещё за доллар. Три-четыре попытки всё равно дешевле часа моего времени. Раньше цена ошибки заставляла составлять техзадание для модели дольше, чем сделать руками — сейчас дешевле просто попробовать.
И обратная сторона той же цифры: за эти деньги покупается исполнение, а не мышление. Ловушку с двойным подсчётом модель нашла — это инженерия, она лежит в коде и вычитывается из него. Вопрос «а стоит ли вообще считать просмотром простое открытие страницы» она не задала — это продукт, он лежит в голове у владельца. Пока за $1.30 продаётся только первое.
Больше про LLM и AI — в нашем Telegram-канале (@devgeek_sh). Разбираем новые модели, делимся опытом и полезными находками.