
Публичные тарифы, токены фактические (у Gemini учтены токены мышления). Поиск без реранка даёт Acc@1 0.487 — ниже нижней границы графика.
Мы классифицируем позиции закупок по справочнику ОКТРУ: 77 772 кодов. Схема обычная, retrieve → rerank: поиск отбирает 30 кандидатов, LLM выбирает один. Второй шаг съедает почти всё время и почти все деньги пайплайна. Я проверил, можно ли отдать его модели, которая не генерирует текст: TypeSafe Jev принимает состояние и типизированные вопросы, а возвращает вероятности. Сравнивал на 928 позициях из технических спецификаций госзакупок с той же gpt-oss-120b в двух режимах, без reasoning и с ним, с Gemma-4-31B, с облачными Gemini 3.5 Flash Lite, 3.8 Flash и 3.1 Pro и с классическим кросс-энкодером Qwen3-Reranker-8B, на одном пуле кандидатов. Коротко: Jev дал лучшую точность из всех участников сравнения, и по парному тесту значимо лучшую — включая фронтир, который стоит в шестьдесят раз дороже и точнее не оказался; при этом Jev вдвое дешевле LLM без reasoning, в 2.5 раза дешевле LLM с reasoning и в 13 раз дешевле облачной модели с мышлением, а латенси на позицию ниже в 4.5 и в 12 раз. И ещё одно: gpt-oss меняет свой ответ примерно на каждой шестой позиции от прогона к прогону, и выключение reasoning это не лечит. Ниже методика сравнения, метрики и код.
Почему поиск с реранком, а не классический ML-классификатор
ОКТРУ — казахстанский классификатор товаров, работ и услуг. Позиция вроде «Спикерфон Yealink CP900 with dongle Teams, максимальная мощность 7 Вт…» должна получить листовой код с полным путём по иерархии. Соседние коды отличаются нюансами: «диктофоны» рядом с «устройствами громкой связи», «кабель силовой» рядом с «кабелем контрольным».
Первый вопрос от любого, кто занимался ML: почему здесь вообще LLM или Jev, а не обученный классификатор? Ответ короткий, а подробности заслуживают отдельной статьи. Классов 78 тысяч, и по большинству из них размеченных примеров нет совсем: реальные закупки покрывают справочник неравномерно, длинный хвост кодов встречается по несколько раз в год или не встречается вовсе. Обучать не на чем. Синтетика в общем случае не спасает: чтобы сгенерировать правдоподобные позиции для кода, нужны правдоподобные примеры этого кода, а их как раз нет, и как модель, обученная на синтетике, поведёт себя на реальных закупках, заранее неизвестно. Поэтому задача решается как поиск по описаниям кодов с последующим выбором, а не как обучение с учителем.
Гибридный поиск по индексу (BM25 плюс эмбеддинги) с этим справляется до определённого предела. На нашем наборе он ставит правильный код первым в 49% случаев, но в top-30 держит его в 90%. Дальше работает реранкер, и одним вызовом он поднимает точное попадание в листовой код до 78%, а попадание в правильную группу третьего уровня (это уровень «кабели силовые» против «кабели контрольные») до 82%. Полный продовый пайплайн поверх того же реранкера даёт 72–76% точных попаданий, а там, где он сам ставит высокую уверенность (78% позиций), точность 83%; остальное уходит на ручную проверку. Вот что стоит один вызов реранкера на позицию в замере ниже:
один вызов gpt-oss-120b, ~6 400 входных токенов (текст позиции плюс 30 описаний кандидатов) и ~700 выходных: reasoning-трейс, то есть цепочка рассуждений, которую модель пишет перед ответом и которая тоже оплачивается, плюс JSON с решением;
5.2 секунды на позицию;
по публичному тарифу gpt-oss-120b на OpenRouter ($0.15 за 1M входных, $0.60 за 1M выходных) это $1.39 за тысячу позиций.
При потоке в миллион позиций в квартал реранк по этим тарифам стоит около $1 400 и занимает 9 дней машинного времени на восьми воркерах. Отсюда вопрос: обязательно ли для выбора одного кода из тридцати генерировать 700 токенов текста? Первое, что напрашивается, это выключить reasoning у той же модели, и такой вариант в сравнении тоже есть.
Что такое Jev и чем он отличается от LLM
Jev от TypeSafe — модель класса System One: она обучена отдавать решение с вероятностью, а не текст. Вместо промпта и текстового ответа она принимает state (данные строкой или JSON) и словарь типизированных вопросов трёх видов: Noul (да/нет, возвращает вероятность «да»), Choice (один вариант из набора, возвращает распределение по вариантам) и Score (позиция на упорядоченной шкале). Никакого сгенерированного текста, никакого парсинга JSON, никакой проверки, что модель не выдумала несуществующий код: ответ приходит в виде вероятностей по тем вариантам, которые вы сами перечислили. Цена считается по входным токенам ($0.042 за 1M), выход в usage есть, но бесплатен. Латенси не зависит от длины рассуждения, потому что рассуждения нет.
Это и есть главное отличие от сравнения «LLM с reasoning против LLM без». Reasoning у генеративной модели можно выключить, но она всё равно генерирует ответ токен за токеном: в нашем замере gpt-oss без reasoning пишет 230 выходных токенов на позицию вместо 700. Jev не пишет ничего.
В замере использовалась версия jev-1.13.0 (алиас jev-latest на момент прогона). Одна оговорка из документации TypeSafe: основной язык модели английский, остальные «поддерживаются, но не одинаково хорошо». Весь бенчмарк ниже на русском тексте.
Реранк на Jev делается двумя способами, и я проверил оба.
Парная схема (pairwise). Каноническая, из документации TypeSafe: один вопрос Noul на каждую пару «позиция, кандидат», затем сортировка кандидатов по вероятности «да». Тридцать кандидатов, тридцать независимых вызовов. Текст инструкции и критериев ниже сокращён.
from typesafe_sdk import Noul, NoulCriteria, TypeSafeClient question = Noul( instructions=( "Классифицируется позиция из прайс-листа или техзадания по справочнику ОКТРУ. " "Кандидат — один листовой код справочника со своей иерархией, названием и описанием. " "Обозначает ли кандидат ТОТ ЖЕ вид товара, что и классифицируемая позиция?" ), criteria=NoulCriteria( true="Кандидат называет тот же вид товара. Расхождения в марке, типоразмере, " "исполнении или производителе попаданию не мешают.", false="Другой вид товара: соседняя группа, изделие того же материала иного назначения, " "деталь вместо изделия, услуга вместо товара, слишком общая надкатегория.", ), ) client = TypeSafeClient() resp = client.system_one( state={"позиция": text, "кандидат": candidate_text}, questions={"same_product": question}, ) resp.answers["same_product"].noul # -> 0.87
Списочная схема (listwise). Один вопрос Choice, где все 30 кандидатов заданы как варианты: ключ варианта — код, описание варианта — текст кандидата. В ответе распределение по всем тридцати, так что полный порядок получается из одного вызова.
from typesafe_sdk import Choice resp = client.system_one( state={"позиция": text}, questions={"code": Choice( instructions="Какой код обозначает тот же вид товара, что и позиция? ...", criteria={code: candidate_text for code, candidate_text in candidates}, )}, ) resp.answers["code"].choice # -> "1071-0001-0006-100043174" resp.answers["code"].probabilities # -> {"1071-...": 0.88, "1083-...": 0.11, ...} resp.answers["code"].confidence # -> 0.87
Noul даёт абсолютную вероятность: «это тот же товар» не зависит от того, кто ещё в списке, поэтому оценки сравнимы между позициями и годятся для порога отсечения. Вероятности Choice нормированы внутри списка: 0.88 означает «лучший из этих тридцати», а не «это точно он». Зато списочная схема тратит один вызов вместо тридцати и не пересылает текст позиции и критерии тридцать раз.
Контекст: 64k токенов на запрос, 32k на состояние плюс самый длинный вопрос. Тридцать описаний кандидатов вместе с инструкцией занимают 13 тысяч токенов, влезает. На случай более длинных списков в коде стоит защита от переполнения: список режется на чанки, победители чанков разыгрывают финал. На top-30 она ни разу не сработала.
Участники
Сначала о том, по какому принципу набран список, потому что «почему именно эти модели» — первый вопрос к любому такому сравнению.
Я не ищу лучшую модель мира. Я решаю производственную задачу: миллион позиций в квартал, реранк — самая дорогая стадия пайплайна, и вопрос в том, чем заменить то, что там стоит сейчас. Поэтому список набран по осям бюджета вокруг продовой точки: то, что работает сегодня (gpt-oss-120b с reasoning); та же модель с выключенным reasoning — соперник Jev по деньгам; вдвое более дешёвая открытая модель у того же провайдера (Gemma); специализированный кросс-энкодер — нижняя граница по цене; облачные модели классом выше (Gemini Flash Lite и 3.8 Flash) и, чтобы знать потолок, фронтир — Gemini 3.1 Pro.
Все участники сравнения получают один и тот же набор из 30 кандидатов и один и тот же текст кандидата (об этом в следующем разделе). Различается только то, кто и как выбирает.
gpt-oss-120b с reasoning (
reasoning_effort=medium): продовый промпт, калиброванный JSON-ответ. Верхняя планка по качеству и по цене.gpt-oss-120b без reasoning (
reasoning_effort=low): та же модель, тот же промпт, изменён один параметр. Честный соперник Jev по бюджету.Gemma-4-31B: тот же промпт, другая модель, reasoning не пишет.
Gemini 3.5 Flash Lite и Gemini 3.8 Flash через Google API, тот же промпт. Lite не думает (около 100 выходных токенов), 3.8 Flash думает (650 токенов мышления на позицию, они тарифицируются как выходные и в замере учтены). Это ответ на вопрос «а если взять облачную модель классом выше».
Gemini 3.1 Pro через тот же API, тот же промпт — фронтир на момент замера. Нужен ровно за одним: показать, сколько точности на этой задаче ещё можно купить за деньги, если бюджет не ограничен.
Jev 1.13, списочная и парная схемы.
Qwen3-Reranker-8B, классический кросс-энкодер: модель читает пару «запрос + один документ» одним куском и выдаёт одну оценку релевантности. Схема поточечная, кандидаты друг друга не видят. Мы зовём его по HTTP, и все 30 документов уходят одним запросом, но внутри это 30 независимых прогонов.
Как сравнивал
Золотой набор: 928 позиций из технических спецификаций госзакупок. Эталон проверен двумя независимыми валидаторами, в наборе оставлены только записи, где они сошлись. В эталоне у записи может быть несколько допустимых кодов, и попадание в любой засчитывается. Так вышло потому, что справочник дублирует позиции: «Радиатор» и «Радиатор охлаждения» это два разных кода, а два «Генератора» в одной группе третьего уровня отличаются только номером. Эталон дополнен такими дубликатами по двум правилам: коды, которые справочник связывает как один товар, и коды с буквально одинаковым названием (справочник повторяет одну позицию в каждой ветке применения: «Глушитель» встречается отдельно у легковых, грузовых, автобусов и спецтехники, а «Болт» — в тридцати четырёх ветках). Правила применены к эталону независимо от того, что ответили модели; без них все модели теряли около пяти пунктов, а различия между ними тонули в шуме разметки.
Три условия, без которых разница между реранкерами не отделима от шума поиска.
Один пул кандидатов на всех. Поиск запускается один раз, его результат кэшируется. Каждая модель судит один и тот же набор из 30 кодов, разница в метриках принадлежит реранкеру. Заодно известен потолок: R@30 поиска, 0.900. Выше него не прыгнет никто.
Один и тот же текст кандидата. Все модели видят кандидата в одном рендере: код, путь по иерархии, название, описание. Для LLM это часть промпта с номерами кандидатов, для Jev состояние или варианты вопроса без номеров, для кросс-энкодера документ. Приор поиска при этом никуда не девается: в списочной схеме порядок вариантов в вопросе совпадает с порядком выдачи поиска, это неявный аналог номеров в промпте LLM.
Повторы там, где модель шумит. Обе конфигурации gpt-oss, Gemma и списочный Jev прогнаны трижды.
Метрики: Acc@1, macro-F1, латенси p50/p95, стоимость по фактическим токенам из usage. Отдельной строкой в таблице стоит R@30 поиска — доля позиций, где правильный код вообще есть среди тридцати кандидатов. Это потолок: реранкер выбирает из того, что ему дали, и выше R@30 не прыгнет никто, а разрыв между его Acc@1 и этой строкой — то, что реранкер ещё не добрал. Одна оговорка: при одном предсказании на позицию micro-F1 численно равна Acc@1, поэтому в таблице её нет. Macro-F1 (усреднение по классам, включая коды, в которые модель промахнулась) показывает, не выезжает ли модель на частых кодах.
Значимость разниц по Acc@1 считал парным тестом Макнемара по записям: для двух моделей смотрим, сколько позиций верно только у одной и только у другой. Межпрогонный разброс модели для этого не подходит, он показывает шум модели, а не ошибку выборки.
Результат

Знак ± показан там, где модель прогонялась трижды. Цены и латенси — в таблице ниже.
Acc@1 | macro-F1 | p50 / p95 на позицию | $ за 1000 позиций | |
|---|---|---|---|---|
потолок: код среди 30 кандидатов (R@30) | 0.900 | — | — | — |
Jev 1.13, списочная | 0.775 ±0.001 | 0.544 | 0.4 / 0.6 с | 0.56 |
Gemini 3.1 Pro, фронтир | 0.753 | 0.500 | 12.2 / 21.6 с | 33.46 |
Gemini 3.8 Flash, с reasoning | 0.751 | 0.486 | 2.4 / 6.2 с | 7.46 |
gpt-oss-120b без reasoning | 0.741 ±0.003 | 0.486 | 1.9 / 3.3 с | 1.10 |
gpt-oss-120b с reasoning | 0.736 ±0.006 | 0.483 | 5.2 / 10.6 с | 1.39 |
Jev 1.13, парная | 0.731 | 0.498 | 0.6 / 0.9 с * | 1.71 |
Gemma-4-31B | 0.730 ±0.001 | 0.468 | 1.5 / 2.8 с | 0.65 |
Gemini 3.5 Flash Lite | 0.698 | 0.419 | 1.0 / 1.3 с | 2.28 |
Qwen3-Reranker-8B | 0.676 | 0.415 | 0.4 / 1.7 с | 0.32 |
поиск без реранка | 0.487 | 0.250 | — | 0 |
* У парной схемы тридцать вызовов на позицию идут параллельно, и p50 — время самого медленного из них, а не сумма; подробности в разделе про скорость.
Строки отсортированы по точности: сверху потолок пула, снизу поиск без реранка, между ними участники. Знак ± стоит там, где модель прогонялась трижды. Потолок — свойство пула, а не реранкера, поэтому в остальных колонках у него прочерки.
Дальше по осям.
Точность
Списочный Jev первый и по Acc@1, и по macro-F1, и при 928 записях парный тест показывает, что он значимо лучше каждого соперника. Против gpt-oss с reasoning: только у Jev верно 80 позиций, только у gpt-oss 40, p < 0.001. Против gpt-oss без reasoning 76 против 45, p = 0.006. Против Gemini 3.8 Flash с мышлением 48 против 27, p = 0.02. Против фронтира, Gemini 3.1 Pro, 45 против 26, p = 0.03. Против Gemma 58 против 17, p < 0.001. Против Gemini 3.5 Flash Lite 96 против 26, p < 0.001. Против кросс-энкодера 121 против 30, p < 0.001. Против собственной парной схемы 61 против 21, p < 0.001.
Отдельно про фронтир. Gemini 3.1 Pro на этой задаче встаёт там же, где Flash: 0.753 против 0.751, разница 20 записей против 18 в другую сторону, p = 0.87. От gpt-oss без reasoning он тоже неотличим (64 против 52, p = 0.31), а стоит в тридцать раз дороже и думает 1 670 токенов на позицию вместо 230. То есть потолок этой задачи определяет не класс модели: от Flash Lite до фронтира все генеративные участники лежат в полосе 0.70–0.75, а оставшиеся 15 пунктов до потолка пула (0.900) деньгами и размером модели не покупаются. Упирается всё в другое — в сам справочник, где один товар размазан по десяткам почти одинаковых кодов, и в жанр техспецификации, где название товара упоминается один раз среди страницы требований. Это, кстати, и есть главный аргумент против «давайте просто возьмём модель посильнее»: сильнее уже некуда, а Jev при этом значимо выше всех и стоит в шестьдесят раз меньше фронтира.
То есть модель, которая не генерирует текст, обходит и генеративную модель с reasoning, и облачную модель с мышлением, и обходит значимо.
Отдельно стоит посмотреть на пару «gpt-oss с reasoning против него же без reasoning»: 0.736 против 0.741. Reasoning здесь не помогает, и это воспроизводится во всех трёх повторах (0.731 / 0.742 / 0.734 против 0.740 / 0.738 / 0.744), при том что он утраивает выход (712 токенов против 228) и утраивает латенси. У Gemini похожая история в свою сторону: мышление даёт 3.8 Flash преимущество над Lite в 5.3 пункта, но 3.8 Flash стоит в 3.3 раза дороже Lite и всё равно уступает Jev.
И ещё одна цифра: R@3 у списочного Jev 0.887 при потолке пула 0.900. То есть правильный код он затаскивает в тройку почти всегда, когда тот вообще есть в пуле.
Цена
По публичным тарифам списочный Jev стоит $0.56 за тысячу позиций против $1.10 у gpt-oss без reasoning (в 2.0 раза), $1.39 с reasoning (в 2.5 раза), $7.46 у Gemini 3.8 Flash (в 13 раз) и $33.46 у фронтира (в 60 раз). Дешевле него только кросс-энкодер, $0.32, но он проигрывает по точности 9.9 пункта.
Выключение reasoning у gpt-oss экономит только 21%: выход падает с 712 до 228 токенов, но вход в 6 400 токенов никуда не девается, а он даёт две трети счёта. У Jev входных токенов вдвое больше (13 тысяч на позицию: текст кандидатов уходит в варианты вопроса, плюс инструкция), но тариф на вход в 3.6 раза ниже, а выход бесплатен.

Публичные тарифы, токены фактические. У Jev выход бесплатен, поэтому бледной части нет. Столбец фронтира обрезан: $33.46 в шкалу не влезает.
На диаграмме видно, где у кого деньги: насыщенная часть столбца — вход, бледная — выход. У Jev бледной части нет вовсе, у генеративных моделей выход съедает около трети счёта, а у фронтира — почти две трети, и его столбец в шкалу уже не влезает.
Пунктир на скаттере в начале статьи — Парето-фронт без Jev: как точность растёт с ценой у всех остальных, от кросс-энкодера за $0.32 до фронтира за $33. Кривая выходит на плато около 0.75 и дальше не поднимается ни за какие деньги. Jev лежит не на ней, а выше: за те же $0.56 фронт даёт 0.68.
Если считать не за позицию, а за верный ответ, порядок почти тот же: $0.47 за тысячу верных у кросс-энкодера, $0.72 у Jev, $0.89 у Gemma, $1.49 и $1.90 у gpt-oss без reasoning и с ним, $9.93 у Gemini 3.8 Flash и $44.42 у Gemini 3.1 Pro.
Парная схема дороже любой другой модели в таблице: $1.71. Тридцать вызовов на позицию означают, что текст позиции, инструкция и критерии пересылаются тридцать раз, 41 тысяча входных токенов вместо 13. Если нужны абсолютные вероятности, чтобы поставить порог и отказываться от ответа на сомнительных позициях, за них придётся платить.
Скорость
p50 на позицию 0.42 секунды у Jev против 1.9 у gpt-oss без reasoning и 5.2 с reasoning: в 4.5 и в 12 раз.
Сразу оговорка, без которой эти числа читать нельзя: латенси меряет не модель, а связку «модель плюс провайдер». gpt-oss и Gemma я звал у стороннего провайдера, Jev в облаке TypeSafe, Gemini в Google. Разное железо, разная загрузка, разная география.

Каждая модель работала у своего провайдера, так что это латенси сервиса, а не только модели.
Одна цифра в таблице требует отдельной сноски: у парной схемы Jev p50 равен 0.6 секунды, почти как у списочной, хотя вызовов на позицию там тридцать, а не один. Так вышло потому, что p50 на позицию — это время самого медленного из тридцати вызовов, а не их сумма: они уходят параллельно. Один Noul занимает около 0.4 секунды, тридцать параллельных укладываются в 0.6. Стоит это тридцатикратного расхода вызовов, и видно это не в p50, а в общем времени прогона: 564 секунды против 30 у списочной схемы на тех же 928 позициях и том же числе рабочих потоков. Если гнать пары последовательно, позиция займёт около двенадцати секунд.
Что от провайдера не зависит, так это объём генерации: 700 выходных токенов у gpt-oss с reasoning, 230 без него, 650 токенов мышления у Gemini 3.8 Flash, 1 670 у Gemini 3.1 Pro и ноль у Jev. Это свойство схемы, а не хостинга, и разрыв в латенси в основном оттуда. Но утверждение «Jev в 12 раз быстрее gpt-oss» верно для тех сервисов, которые я покупал, а не для моделей в вакууме. Заявленный лимит Jev в 1200 запросов в минуту в парной схеме не сработал: вызовы проходили примерно втрое быстрее, без единого 429. Документация TypeSafe предупреждает, что лимиты «меняются динамически и могут измениться без уведомления», так что на это я бы не закладывался.
Миллион позиций в квартал списочным Jev это около 8.5 часов и $560 вместо 8 дней и $1 400.
Стабильность
Четыре модели прогонялись трижды при температуре 0. Средняя Acc@1 у всех стоит почти на месте, но ответы по позициям расходятся, и очень по-разному.

Считается доля позиций, где во всех трёх прогонах выбран один и тот же код. Выключение reasoning у gpt-oss эту цифру почти не меняет: 85.2% против 82.3%.
Jev воспроизводит top-1 на 97.8% позиций, Gemma на 99.2%, gpt-oss — на 82.3% с reasoning и 85.2% без него. То есть продовый реранкер выдаёт разные коды на каждой шестой позиции от прогона к прогону, а Jev — на каждой сорок пятой.
Для агрегированной метрики это не важно: 0.736 — среднее по случайному выбору модели, а не фиксированный ответ. Для прода важно: кэшировать нельзя, воспроизвести ошибку из тикета нельзя, diff двух прогонов индекса покажет сотни изменений, большинство из которых шум.
Где Jev проигрывает и что не проверено
Вероятности Choice нормированы внутри списка. Если нужен порог, чтобы модель могла сказать «ни один из тридцати не подходит», списочная схема его не даёт: нужна парная, отдельный Noul-вопрос «есть ли в списке подходящий код» поверх списочного выбора (я это не пробовал) или кросс-энкодер, у которого оценки абсолютные по построению. То есть самая точная и самая дешёвая схема здесь же и самая неудобная для отказа от ответа.
Слово «калиброванные» из документации TypeSafe я в этом замере не проверял: ни диаграммы надёжности, ни AUROC по вероятностям Noul. Данные для этого есть, графика нет.
Английский как основной язык модели. На русских описаниях товаров она сработала лучше всех, но это один домен и один язык.
Self-host невозможен, версия по алиасу, лимиты меняются. Для прода это три отдельных риска, у своего хостинга их нет.
Про кросс-энкодер
Стоит проговорить, чем классический кросс-энкодер отличается от двух схем Jev, потому что снаружи они выглядят похоже. Qwen3-Reranker судит пару «запрос + документ» и отвечает одним числом, вероятностью токена «yes» против «no»; тридцать кандидатов это тридцать независимых прогонов. По смыслу это ровно парная схема Jev, а не списочная. Проверяется в три строки: оценки не нормированы (сумма по шести документам 0.86, а не 1), единственный слабый документ в запросе получает 0.0003, а не всю вероятность, и оценка документа не меняется, если положить рядом заведомо лучший. То есть у кросс-энкодера, как и у Noul, оценки абсолютные и сравнимы между позициями, и порог отказа на них строится; у Choice нет.
Побочное наблюдение оттуда же: два идентичных запроса подряд дают чуть разные числа, 0.00032099 и 0.00032221. Разница в четвёртом знаке, ранжирование от неё не меняется (разрывы между кандидатами на порядки больше), но это тот же сервер, на котором gpt-oss меняет top-1 на каждой шестой позиции, и та же природа: зависимость вычислений от того, как запросы попали в батч. У кросс-энкодера это безвредно, у генеративной модели с длинной траекторией декодирования выливается в другой ответ.
По качеству кросс-энкодер здесь последний: 0.676 против 0.775 у Jev, 121 расхождение в пользу Jev против 30. Зато он самый дешёвый ($0.32 за тысячу) и один из самых быстрых. Если считать деньги за верный ответ, разрыв сокращается: $0.47 против $0.72 у Jev. Так что выбор между ними это выбор между «дешевле за штуку» и «меньше ошибок разгребать руками».
Чего в этих цифрах нет
Набор целиком из одной предметной области. 928 записей дают ошибку выборки около полутора пунктов, и парный тест на них работает уверенно, но это автозапчасти, а не справочник целиком.
Те же модели я гонял ещё на двух золотых наборах поменьше, по 200 позиций: позиции маркетплейса и строки прайс-листов поставщиков. Там порядок моделей другой, и единого победителя между gpt-oss, Gemma и Jev нет: на одном наборе впереди одна модель, на другом другая, причём разрывы внутри ошибки выборки. Так что вывод «Jev лучший» верен для этих данных, а не вообще.
Цены публичные. gpt-oss и Gemma мы вызываем у стороннего провайдера и платим не по тарифам OpenRouter; Jev по тарифу $0.042 за 1M и все три Gemini по тарифам Google это живые деньги, они оплачены по счётчику.
Латенси и модель здесь не разделены: каждая модель жила у своего провайдера, и p50 меряет их вместе. Чтобы отделить одно от другого, нужен один и тот же сервинг для всех, а его у меня нет: Jev и Gemini в принципе не ставятся на своё железо.
Из генеративных моделей проверены только эти. Ни GPT-5.x, ни Claude на задаче не гонялись: фронтир-точка нужна была одна, и на ней выводы про потолок уже видны.
Аффилиации с TypeSafe у меня нет, Jev оплачивался по публичному тарифу: все его прогоны вместе обошлись примерно в $3, а один прогон фронтира — в $31.
Что дальше
Реранкер общего назначения — это способ работать там, где учить классификатор не на чем. Но «не на чем» верно для справочника целиком, а не для каждой его ветки. Поэтому дальше я двигаюсь в обратную сторону: дистиллировать модели поменьше, генерировать синтетику там, где это осмысленно, копить размеченные данные и учить на них собственные BERT-классификаторы по веткам. На автозапчастях это уже получилось. По большинству остальных веток данных всё ещё не хватает — и это тема для отдельной статьи.
Если вы гоняете LLM-реранкер в проде и ни разу не запускали его дважды на одних данных, запустите. Мне интересно, у кого ещё среднее стоит на месте, а top-1 меняется на каждой шестой позиции, и лечится ли это у вас выключением reasoning. У нас не лечится.
Источники: TypeSafe, модели и тарифы, схема реранка на Jev

