Если у вас бот на проде стабильно выдаёт шаблонную фигню и вы не знаете, какую модель ставить вместо текущей, то самый честный способ выбрать — это не спорить с самим собой и не крутить промпт вслепую, а собрать маленький eval-набор и прогнать его через несколько моделей разом. Я так и сделал. Взял 20 целей пользователей, прогнал их через 4 модели на 2 разных промптах, получил 160 вызовов, оценил каждую ячейку дважды (сам Claude Code и Codex как независимый ревьюер) и по цифрам сменил prod-модель онбординга с Claude Haiku 4.5 на GPT-5-mini через OpenRouter. Вышло дешевле и точнее ровно на тех кейсах, где раньше было плохо. Сейчас расскажу, как это было по шагам и что я из этого вынес.
Откуда вообще взялась проблема
В JustStep есть онбординг, где бот должен из абстрактной цели пользователя сформулировать нормальную цель и первый шаг к ней. И вот этот кусок, функция formulate_goal_and_first_step, стабильно лажал на абстрактных целях. Человек пишет что-то расплывчатое, а бот выдаёт ему «шаг-рефлексию»: подумай о своих ценностях, осознай, прислушайся к себе. Красиво, но бесполезно, потому что после такого первого шага человек закрывает приложение и не возвращается, ведь делать-то ему нечего.
И вот тут есть соблазн, который знаком каждому, кто пилит на нейросетях, — сесть и крутить промпт. Подвигал формулировки, прогнал пару своих целей, вроде стало получше, задеплоил. А на следующей цели опять шаблон. Подвигал ещё. И так по кругу, потому что вы оцениваете на двух-трёх примерах из головы и сами себя обманываете. Я уже наелся этого подхода и решил, что в этот раз буду выбирать по данным, а не по ощущению «вроде норм».
Что я собрал вместо спора с самим собой
Сначала я собрал eval set, 20 целей. Не выдуманных красивых, а разных по характеру: абстрактные («хочу больше энергии»), атомарные («хочу научиться чему-то конкретному»), шаблонные по здоровью и так далее. Это и есть тот самый набор, на котором видно слабые места, а не удобные примеры, на которых всё всегда хорошо.
Дальше матрица: 20 целей умножаем на 4 модели и на 2 промпта. Модели взял такие — Claude Haiku 4.5 (то, что стоит в проде сейчас), Claude Sonnet (как «дорогой умный» вариант), GPT-5-mini и GPT-5-nano. Два промпта, baseline и variant, то есть текущий и переписанный. На выходе получилось 160 вызовов. Звучит как много, но через OpenRouter это прогоняется одним скриптом, который Claude Code мне и написал, я только оркестрировал и проверял.
И вот тут важный момент про оценку, потому что прогнать-то легко, а вот честно оценить 160 ответов — это и есть вся работа. Я не стал полагаться на один взгляд. Claude сам прошёл по всем 160 ячейкам и проставил каждой метку: HIT, MISS, NB или VO. А параллельно я попросил Codex (это уже GPT-движок) пройти те же 160 ячеек как независимый эксперт, который не видел оценок Claude. Получилось два независимых ревьюера по одной и той же матрице, и там, где они сходятся, можно верить, а где расходятся, там и есть пограничные случаи, в которые надо смотреть руками.
Что показали цифры
А показали они вещь, которую на глаз я бы не поймал: слабость была не у модели вообще, а конкретно у Haiku и конкретно на атомарных целях. То есть проблема локальная, а я её принимал за общую. И был соблазн вообще махнуть рукой на всё, у меня прямо в голове крутилось «так может он везде плох», но цифры эту мысль и опровергли, плохо-то было точечно.
GPT-5-mini на baseline-промпте дал 85%, а на variant — 90% по оценке Codex и все 100% по оценке Claude. Расхождение между ревьюерами как раз на тех самых пограничных ячейках, но даже по строгому Codex это явно лучше того, что было. И главное, он чинит ровно те кейсы, которые болели: G01 на baseline, G16 (та самая атомарная цель) и G07 (шаблон про здоровье). То есть не «стало лучше в среднем» абстрактно, а закрытие конкретных дыр, которые я и хотел закрыть.
Sonnet я честно посмотрел и отложил, он умный, но дорогой, а платить за умность там, где хватает mini, смысла нет. Как я сам себе тогда написал, «соннет дорого будет да». А GPT-5-nano провалился где-то на половине кейсов, фейлил примерно 50%. Но я его не выкинул из набора, а оставил как сигнал. Когда у тебя есть заведомо слабая модель в матрице, она показывает нижнюю планку, и на её фоне сразу видно, кто реально вытягивает, а кто просто чуть лучше дна.
Деньги, потому что без них выбор не выбор
Теперь самое приятное, цена. GPT-5-mini стоит $0.25 за миллион входных токенов и $2 за миллион выходных. Haiku — $1 и $5 соответственно. То есть mini не только точнее на проблемных целях, он ещё и в разы дешевле, и в моём проекте на heightened-path (это путь, где запросов больше) экономия получается заметная. Редкий случай, когда лучше и дешевле одновременно, обычно ведь за качество приходится доплачивать — как было, когда я менял Haiku на DeepSeek и проверял миф про «-90% на кэше», там за обещанную экономию пришлось как раз доплатить разочарованием.
Кстати про OpenRouter. У меня всё уже сидит на нём, и это сильно упростило весь эксперимент, потому что не надо плодить ключи под каждого провайдера. Я даже сам себя одёрнул в процессе: зачем мне отдельный ключ под OpenAI, если у нас опенроутер и там херова гора разных моделей, каких только хочешь. Вот эта мысль, по сути, и есть главный аргумент за такой подход к выбору модели: когда у тебя один шлюз на все модели, прогнать сравнение и потом переключиться в проде стоит почти ничего. Если интересно, как я вообще туда переехал и почему, это отдельная история — про переезд с OpenAI на OpenRouter.
Где Codex реально спас
И вот тут отдельное спасибо второму ревьюеру, потому что он поймал не оценочную, а техническую засаду. Codex как второй движок я давно держу в своём ai-консилиуме, и тут он снова отработал. GPT-5-mini — это reasoning-модель, и ведёт она себя не так, как привычные чат-модели. Во-первых, у неё параметр не max_tokens, а max_completion_tokens, и если по привычке передать старое имя, она просто не дослушает. Во-вторых, она не поддерживает temperature вообще. Я бы это словил уже на проде по странным обрезанным ответам и долго чесал бы репу, а Codex указал на это прямо при ревью кода. То есть второй движок пригодился дважды, и как оценщик качества ответов, и как ловец бага в интеграции.
Что в итоге поменял и чего не трогал
Поменял я ровно один кусок, модель в онбординге, в той самой formulate_goal_and_first_step. Matching и decompose я не трогал намеренно, потому что Haiku там я не оценивал, а менять модель в местах, которые ты не замерял, это ровно та слепая правка, от которой я и хотел уйти. Раз нет данных по этим узлам, значит нет и основания их трогать, как ни крути.
Был, конечно, момент усталости в середине, когда я уже написал «я чет уже не знаю че выбрать… или просто на мини меняем и все». Это нормально, на 160 ячейках глаз замыливается. Но дотерпеть до сведённой таблицы стоило, потому что «просто на мини меняем» в итоге оказалось правильным решением, только теперь оно подтверждено цифрами, а не выбрано от отчаяния. И это всё-таки разные вещи: действие одно и то же, но в одном случае ты угадал, а в другом знаешь почему.
Что я из этого забираю на будущее
Главный вывод простой и скучный, но рабочий: выбор LLM для продакшена — это маленький инженерный эксперимент, а не вопрос вкуса. Двадцати целей хватает, чтобы увидеть, где модель ломается, два промпта показывают, докручивается ли проблема промптом или нужна другая модель, а две независимые оценки (свой движок плюс чужой) ловят и качество ответов, и техдолг в интеграции. Стоит это копейки, особенно когда всё идёт через один OpenRouter, а защищает от худшего сценария, когда ты полгода держишь на проде модель, которая тихо отваливается на части кейсов, и даже не знаешь об этом.
Так что если стоите перед тем же вопросом «какую нейросеть поставить» — не спорьте сами с собой и не верьте паре примеров из головы. Соберите eval, прогоните, посчитайте деньги, посмотрите на цифры. Это та самая удочка, которой я бы и сам хотел научиться раньше, чем потратил кучу времени на «вроде норм». Вот и делайте выводы.