Назад к блогу

Jev и «решающие модели»: как классификация на LLM меняет подход к ML-задачам

Jev и «решающие модели»: как классификация на LLM меняет подход к ML-задачам

Jev предлагает альтернативу привычным LLM: вместо генерации текста, требующего парсинга и валидации, модель сразу возвращает калиброванные вероятности по заданным вариантам за один проход. Разбираем, как устроен этот класс «решающих моделей», что именно они отдают на выходе и какие замеры подтверждают их эффективность.

Обычная LLM отвечает текстом, который потом надо разобрать и проверить. Jev устроен иначе: он принимает описание ситуации и список вариантов обычными словами и возвращает калиброванную вероятность для каждого варианта за один прямой проход, без генерации текста и без парсинга. Разберём, что именно модель отдаёт на выходе, как устроен её промпт, откуда берутся вероятности и что показывают замеры.

Что такое «решающая модель» и чем она отличается от LLM

Jev — модель класса System One: она обучена отдавать решение с вероятностью, а не текст. Ответ приходит в виде вероятностей по тем вариантам, которые вы сами перечислили: никакого сгенерированного текста, никакого парсинга JSON, никакой проверки, что модель не выдумала несуществующий код.

Разница принципиальна по типу возвращаемого значения. У обычных LLM выход — строки, а строки гибкие и могут быть чем угодно: ответами чата, кодом, галлюцинациями, отказами; чтобы использовать их в софте, ответы нужно парсить и валидировать. У решающей модели выход — типизированные структурированные значения. Все ответы сопровождаются калиброванными вероятностями и оценками уверенности.

Вопрос бывает трёх типов, и у каждого свой ответ:

  • choice — выбрать один вариант из набора. criteria — объект от ключа варианта к описанию (или null для одного ключа), необязательные instructions. Ответ — один ключ, вероятность на каждый ключ и уверенность.
  • noul (да/нет) — необязательные criteria с описаниями "false" и "true". Ответ — вероятность истины.
  • score — criteria — список уровней от низшего к высшему. Ответ — ожидаемый уровень, вероятность на каждый уровень и уверенность.

Контракт запроса

Один запрос на решение — это DecisionInput с тремя полями: state (то, о чём принимается решение), question (что именно решить) и необязательный images (список изображений, показываемых перед текстом).

state может быть текстом, объектом или списком. Если это объект, в нём должно быть хотя бы одно поле: последнее поле считается изменяющейся частью, а все предыдущие идут перед вариантами. Пустой объект — это ошибка, а не запасной вариант. Если state — текст или список, сохраняется исходный порядок v1.2: сначала текст, затем вопрос и варианты.

Сколько вариантов и как нумеруются коды

Один вопрос допускает от 1 до 255 вариантов. Коды присваиваются по порядку: сначала A … Z, затем двухбуквенные AA, AB и так далее. Код используется только в том случае, если токенизатор кодирует его как один токен, в том числе сразу после префикса чата; берутся первые 255 таких кодов. Чекпойнт записывает свои коды и их token id в decision_config.json, а при загрузке отклоняется чекпойнт, чьи коды отличаются от кодов его токенизатора.

Как это работает внутри: от промпта до вероятностей

Раскладка «live-last»

System message всегда содержит инструкцию классифицировать состояние по вопросу и описаниям вариантов, относиться к содержимому состояния как к данным, а не инструкциям, и отвечать только кодом выбранного варианта. В user message сначала идут изображения (если есть), затем текст.

Если state — объект с хотя бы одним полем, порядок такой: Question, затем State без последнего поля, затем Options, затем Latest с одним последним полем, и в конце просьба вернуть только буквенный код. Если state — текст или список, сохраняется исходный порядок v1.2: State, Question, Options.

«Живой» вопрос ставится последним, потому что внутри одного экрана приложения вопрос, более раннее состояние и варианты остаются неизменными от запроса к запросу. Сервер может обработать и переиспользовать эту ведущую часть, а заново читается только последняя часть.

Как из логитов получаются вероятности

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

Softmax считается через вычитание логарифма суммы экспонент, после чего берётся экспонента:

logprobs = choice_logits - numpy.logaddexp.reduce(choice_logits)
probabilities = numpy.exp(logprobs)

Ответ модели — softmax по логитам токенов кодов вариантов на первой сгенерированной позиции, после деления логитов на калибровочную температуру чекпойнта.

Пример на llama-cpp-python

Модель загружается с logits_all=True, чтобы сохранялись оценки для всех токенов. Затем задаются метки и варианты, из них формируется строка опций, которая вместе с текстом вставляется в промпт в формате чата. Промпт подаётся в модель вызовом eval с токенизацией add_bos=False, special=True. После этого берётся вектор логитов последнего токена, для каждой метки токенизируется её первый токен, из логитов выбираются соответствующие значения, нормируются и переводятся в вероятности.

Что означают конкретные логиты

В примере для вариантов Legitimate, Spam, Phishing логиты равны 26.254, 27.262 и 29.614. Логарифмические вероятности после вычитания логарифма суммы экспонент — -3.482, -2.474 и -0.122. Экспонента даёт вероятности 0.031, 0.084 и 0.885, которые в сумме дают единицу.

База, адаптеры и версионирование

Adapter-first в v1.3

В v1.3 база больше не настраивается так, чтобы соревноваться на zero-shot бенчмарках. Вместо этого раскладка промпта ставит фиксированную часть запроса (инструкции и варианты) вперёд, а меняющийся вход — в конец, чтобы фиксированную часть можно было кэшировать. Плата за это — более слабая zero-shot точность на длинных, незнакомых списках вариантов: legal-clauses без адаптера даёт 66.0% на v1.2 и 7.4% на v1.3. С адаптером v1.3 отстаёт от v1.2 не более чем на 0.7 пункта и опережает не более чем на 0.3 пункта на каждой задаче, кроме legal-clauses (83.6% против 85.7%). Для zero-shot использования без адаптера рекомендуется v1.2. Каждый чекпойнт v1.3 записывает "prompt_layout": "live-last" в decision_config.json.

LoRA-адаптеры

Каждый адаптер — это LoRA для базы Jeff v1.3, обученная в одну эпоху. Результаты приведены на отложенном тестовом наборе, который адаптеры не видели; для каждого указаны точность и в скобках ошибка калибровки, например для guard — 98.2%.

Адаптер привязан к одной версии базы: каждый адаптер записывает SHA-256 весовых файлов своего базового чекпойнта в decision_config.json, секция adapter.base_checkpoint. Сервер jeff-serve отказывает адаптеру, если его база отличается от загруженной, на обоих бэкендах. Поэтому адаптер v1.2 нельзя обслуживать на базе v1.3.

Форматы поставки

Базовая модель поставляется в форматах Q8_0 и Q4_K_M, а каждый адаптер — как небольшой LoRA GGUF (~169 МБ), загружаемый через --lora, так что одна база обслуживает все адаптеры. Q8_0 практически не теряет точность (в пределах 0.4 пункта от полной точности), а Q4_K_M остаётся в пределах 0.67 пункта (−0.61…+0.67 на адаптер) и имеет собственную температуру для калибровки вероятностей. Для Jeff-Code роутер следует запускать на базе Q8_0, так как его решения — это близкие сравнения, и Q4_K_M меняет около 6% из них. При использовании llama-server нужно перечислять каждый адаптер в каждом запросе со scale 1 или 0.

Данные и обучение

Тренировочный микс собран из трёх групп источников. Публичные датасеты конвертируются в решения: boolq, squad2, paws, civil_comments, aegis2, pubmedqa, vitaminc, multinli, massive, snli, commonsense_qa, openbookqa, arc_challenge, arc_easy, social_iqa, cosmos_qa, quartz, qasc, truthful_qa, twitter_financial, liar2, halueval_qa, halueval_dialogue, halueval_summarization, wikibio, ragtruth_train и winogrande_train. Синтетические вопросы — около 31 000 вопросов, написанных и проверенных открытой моделью-учителем Qwen3.8-Flash-Next, обслуживаемой локально. В код встроены 10 000 вероятностных вопросов (кости, карты, лотереи и т. п.) с точными вероятностями в качестве целевых значений, а также вопросы на отслеживание объектов и арифметику дат, генерируемые с точными ответами. Длинные документы с человеческими метками представлены ContractNLI (соглашения о неразглашении, CC BY 4.0), ConditionalQA и CUAD (коммерческие контракты, CC BY 4.0).

Long-list вопросы

Чтобы модели учились отвечать при числе вариантов больше 26, в набор добавлены 32 250 вопросов с 20–254 вариантами. Это опирается на схему кодирования: вопрос имеет от 1 до 255 вариантов, коды идут по порядку A … Z, затем двухбуквенные AA, AB, и код используется только если токенизатор кодирует его как один токен. Набор собран из трёх частей: 20 000 придуманных списков, 6 000 интентов MASSIVE (все 60 как варианты) и 6 250 интентов CLINC150 (все 150 как варианты, а сообщения вне области отвечаются «None of these»). Используются только training splits, а test splits MASSIVE и CLINC150 никогда не используются для обучения, поэтому результаты на этих двух наборах не zero-shot для v1.1 и v1.2. В v1.2 придуманные списки сокращены до 5 001 вопроса, а списки MASSIVE и CLINC150 сохранены полностью.

Голосовые команды

Голосовые команды (5 703 вопроса) были удалены из базового микса v1.2 и перенесены в адаптер nav. Они присутствовали только в версиях v1.0 и v1.1 и были созданы в коде на основе Amazon MASSIVE (английские команды голосового ассистента) и CMU Pronouncing Dictionary путём замены слов на другие, звучащие одинаково, чтобы имитировать ошибки распознавания речи.

Пограничные случаи и ограничения

Jaggedness

Документация Jev 1.13 по jaggedness содержит указания на сильные и слабые стороны Jev. Модель сейчас плохо справляется с числами, датами и «adversarial content», но хороша для всего, что можно выразить как задачу классификации — например, для определения спама, подсказки меток, приоритизации и ранжирования.

Калибровку нужно проверять на своих данных

Одна точка вероятности ничего не говорит о калибровке: для этого нужна размеченная выборка. Первая проверка — прогнать сотню реальных тикетов с известной разметкой и посмотреть, держится ли калибровка. Это важно, поскольку документация прямо предлагает строить логику на порогах: высокая уверенность — действуем сами, средняя — переспрашиваем, низкая — отдаём человеку. Если ответ на пример из собственных доков сдвигается на 14 пунктов, пороги придётся подбирать на своих данных. Публичных замеров на русском нет, поэтому калибровку проверяют на своих размеченных данных.

Caveats

Адаптеры v1.3 работают только на базе v1.3, а v1.2 — только на v1.2; сервер отказывает на любой другой базе. Без адаптера v1.3 намного слабее v1.2 на длинных незнакомых списках опций. Лицензии различаются по адаптеру, некоторые некоммерческие. Сравнение 27B — это одна конфигурация: фиксированная выборка отложенных строк на задачу на одном Mac, 27B с выключенным пошаговым рассуждением. Малые модели не рассуждают: ожидаются быстрые калиброванные выборы между описанными опциями, а не многошаговое рассуждение. Лимиты опций: до 254 для моделей Qwen; Jeff-Gemma4-E2B (всё ещё v1.0) — только до 26. Только английский и текст.

Слово «калиброванные» из документации TypeSafe в замере не проверялось: ни диаграммы надёжности, ни AUROC по вероятностям Noul. Вероятности Choice нормированы внутри списка, поэтому нет порога для отказа: если нужен порог, чтобы модель могла сказать «ни один из тридцати не подходит», списочная схема его не даёт. Также self-host невозможен, версия по алиасу, лимиты меняются.

«Reason in code, decide with Jeff» — не просто лозунг, потому что это классификатор, а не планировщик: нужно указывать, к чему ведёт каждая опция, а не просить прогнозировать. Формулировки важны: описывать опции последовательно и словами; короткие описательные ключи, никогда не голые числа. Независимые вопросы следует задавать вместе в одном запросе, а адаптер использовать, когда zero-shot недостаточно.

Практика: классификация и реранкинг

Две схемы реранкинга

В задаче классификации позиции по справочнику Jev применяется как реранкер двумя схемами.

Парная схема (pairwise). Каноническая, из документации TypeSafe: один вопрос Noul на каждую пару «позиция, кандидат», затем сортировка кандидатов по вероятности «да». В state подаются два поля — позиция и кандидат, вопрос формулируется как «Обозначает ли кандидат ТОТ ЖЕ вид товара, что и классифицируемая позиция?» с критериями true/false. Ответ даёт абсолютную вероятность «да».

Списочная схема (listwise). Один вопрос Choice, где все 30 кандидатов заданы как варианты: ключ варианта — код, описание варианта — текст кандидата. В ответе распределение по всем тридцати, так что полный порядок получается из одного вызова.

Чем схемы отличаются

Noul даёт абсолютную вероятность: «это тот же товар» не зависит от того, кто ещё в списке, поэтому оценки сравнимы между позициями и годятся для порога отсечения. Вероятности Choice нормированы внутри списка: 0.88 означает «лучший из этих тридцати», а не «это точно он». Зато списочная схема тратит один вызов вместо тридцати и не пересылает текст позиции и критерии тридцать раз.

Вызов через typesafe_sdk

Минимальный пример: создаётся вопрос Noul с инструкцией и критериями true/false, клиент TypeSafeClient, затем отправляется запрос на решение — system_one. В этот запрос передаются state и questions, то есть те же поля, из которых состоит DecisionInput. Ответ читается как resp.answers["same_product"].noul — вероятность «да». Альтернативно для выбора одного кода из набора используется Choice с criteria в виде словаря код→описание кандидата, и ответ читается через resp.answers["code"].choice, .probabilities и .confidence.

jev-leftpad

jev-leftpad реализован через промпт «How many spaces are needed before value to reach targetLength?» и запрос выбора (choice query), который позволяет выбирать варианты от «0 spaces are needed» до «10 spaces are needed». Из вероятностей получается число пробелов путём выбора одного из этих вариантов.

Как сравнивали и что получилось

Стенд и методология

В тесте реранкера участвовали: gpt-oss-120b в двух конфигурациях — с reasoning (reasoning_effort=medium) и без него (reasoning_effort=low), Gemma-4-31B, Gemini 3.5 Flash Lite и Gemini 3.8 Flash через Google API, Gemini 3.1 Pro через тот же API как фронтир, а также Jev 1.13 в списочной и парной схемах и кросс-энкодер Qwen3-Reranker-8B. Все участники получали один и тот же набор из 30 кандидатов и один и тот же текст кандидата; различалось только то, кто и как выбирает. Каждая модель жила у своего провайдера, поэтому латенси и модель не разделены: p50 меряет их вместе. Jev оплачивался по публичному тарифу TypeSafe, Gemini — через Google API. Из генеративных моделей проверены только эти: ни GPT-5.x, ни Claude на задаче не гонялись.

Нагрузка: золотой набор из 928 позиций из технических спецификаций госзакупок, эталон проверен двумя независимыми валидаторами, оставлены только совпавшие записи; у записи может быть несколько допустимых кодов, попадание в любой засчитывается. На каждый запрос — один пул из 30 кандидатов: поиск запускается один раз и кэшируется, каждая модель судит один и тот же набор из 30 кодов. Все модели видят кандидата в одном рендере: код, путь по иерархии, название, описание. Для LLM это часть промпта с номерами кандидатов, для Jev — состояние или варианты вопроса без номеров, для кросс-энкодера — документ. Acc@1 считался по этим 928 записям, значимость разниц — парным тестом Макнемара по записям: для двух моделей смотрят, сколько позиций верно только у одной и только у другой.

Результаты

УчастникAcc@1macro-F1p50/p95$ за 1000 позиций
потолок пула (R@30)0.900———
Jev 1.13, списочная0.775 ±0.0010.5440.4/0.6 с0.56
Gemini 3.1 Pro0.7530.50012.2/21.6 с33.46
Gemini 3.8 Flash0.7510.4862.4/6.2 с7.46
gpt-oss-120b без reasoning0.741 ±0.0030.4861.9/3.3 с1.10
gpt-oss-120b с reasoning0.736 ±0.0060.4835.2/10.6 с1.39
Jev 1.13, парная0.7310.4980.6/0.9 с1.71
Gemma-4-31B0.730 ±0.0010.4681.5/2.8 с0.65
Gemini 3.5 Flash Lite0.6980.4191.0/1.3 с2.28
Qwen3-Reranker-8B0.6760.4150.4/1.7 с0.32
поиск без реранка0.4870.250—0

Значимость разниц против 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 при этом значимо выше всех и стоит в шестьдесят раз меньше фронтира.

Оговорки

Латенси и модель не разделены, потому что каждая модель жила у своего провайдера, и p50 меряет их вместе; чтобы отделить одно от другого, нужен один и тот же сервинг для всех, а его нет, так как Jev и Gemini в принципе не ставятся на своё железо. Слово «калиброванные» из документации TypeSafe в замере не проверялось: ни диаграммы надёжности, ни AUROC по вероятностям Noul; данные для этого есть, графика нет. Набор целиком из одной предметной области — автозапчасти, а не справочник целиком; 928 записей дают ошибку выборки около полутора пунктов. На двух других золотых наборах по 200 позиций порядок моделей другой, единого победителя между gpt-oss, Gemma и Jev нет, поэтому вывод «Jev лучший» верен для этих данных, а не вообще. Английский как основной язык модели проверен на одном домене и одном языке.

Измерения: скорость и размер Jeff

Медианное время на одно решение измерялось на 200 бенчмарк-вопросах (примерно по 200 входных токенов каждый), по одному вопросу за раз, от сырого текста до вероятностей. Замеры проводились на NVIDIA RTX PRO 6000, Apple M4 Max (MLX) и CPU (32 потока); для Jeff-Qwen3.5-0.8B получено 22 ms, 28 ms и 463 ms соответственно. Веса в 16-битном формате — 1.7 ГБ. Для AutoJev-27B указано 27B параметров и ~54 ГБ весов, а время на RTX PRO 6000 не опубликовано.

Отдельно измерялись конфигурации с адаптерами на RTX PRO 6000 через путь запросов jeff-serve: 675 запросов, смешивающих тестовые промпты всех девяти исходных адаптеров, при этом v1.2 и v1.3 измерялись подряд на одном и том же свободном GPU 5 октября 2026.

Конфигурацияv1.3v1.2Память GPU
база одна26.6 ms26.2 ms1.74 GB
база + один адаптер31.4 ms30.9 ms1.79 GB
база + все девять адаптеров со сменой на каждом запросе31.8 ms31.5 ms1.96 GB
база + 3 адаптера со сменой на каждом запросе31.9 ms—1.83 GB
база + все 15 адаптеров со сменой на каждом запросе32.1 ms—2.09 GB
один адаптер, влитый в веса26.7 ms26.3 ms1.77 GB

Большая часть каждого решения — фиксированные накладные расходы: промпт на 251 токен занимает около 25 ms, а на 2 569 токенов — около 33 ms.

В сравнении с 27B-моделью Qwen3.8-27B, которая делает каждое решение за 8.1 с и занимает 28.6 ГБ, Jeff + адаптер решает за 0.25 с (в 35 раз быстрее) при точности 94.6% против 86.6%, а Jeff с 27B как fallback — за 0.46 с (в 28 раз быстрее) при точности 95.0%, при этом память Jeff со всеми 15 адаптерами — +2.09 ГБ (1.83 ГБ с 3).

Zero-shot бенчмарки v1.2 и v1.3

Zero-shot бенчмарки v1.2 показывают точность базовых моделей без адаптеров на панели из 4 599 вопросов из пяти публичных бенчмарков плюс отдельно оцениваемый JevBench hard (105 пунктов); например, Jeff-Qwen3.5-0.8B набирает 78.7% overall, а Jeff-Qwen3.5-2B — 81.7%. v1.3 на той же панели набирает 78.6% против 78.8% у v1.2, потому что v1.3 сделан adapter-first: база больше не настраивается для конкуренции в zero-shot, а раскладка промпта ставит фиксированную часть первой, а меняющийся вход — последним, чтобы фиксированную часть можно было кэшировать. Цена этого — более слабая zero-shot точность на длинных незнакомых списках опций: legal-clauses без адаптера даёт 66.0% на v1.2 и 7.4% на v1.3. С адаптером v1.3 находится в пределах от −0.7 до +0.3 пункта от v1.2 на каждой задаче, кроме legal-clauses (83.6% против 85.7%). Эти цифры не показывают производительность с адаптерами на всех задачах и не показывают сравнение с опубликованными Jev и AutoJev-27B, которые измерялись на другой выборке тех же бенчмарков.

Что из этого следует на практике

  • Выход — не текст, а распределение. Если задача сводится к выбору из перечисленных вариантов, оценке «да/нет» или позиции на шкале, решающая модель отдаёт готовые вероятности без парсинга и валидации. Это убирает целый класс ошибок: несуществующий код, отказ, галлюцинацию-строку.
  • Тип схемы определяет, что можно делать с оценками. Noul даёт абсолютную вероятность, сравнимую между запросами, — на ней строится порог отсечения. Choice даёт распределение внутри списка за один вызов, но нормирован внутри него, и порога отказа не даёт. Выбор между схемами — это выбор между сравнимостью оценок и стоимостью вызова.
  • Раскладка промпта — это оптимизация кэша, а не качества. «Live-last» ставит неизменную часть (инструкции и варианты) вперёд, а меняющийся вход — в конец, чтобы ведущую часть можно было обработать один раз. Плата за это — падение zero-shot точности на длинных незнакомых списках, что и видно на legal-clauses.
  • Адаптер — это привязка к базе, а не свободный плагин. SHA-256 весов базового чекпойнта записан в конфигурации адаптера, и сервер отказывает при несовпадении. Адаптер v1.2 нельзя обслужить на базе v1.3.
  • Калибровку нельзя принять на веру. Одна точка вероятности ничего не говорит о калибровке; нужна размеченная выборка. Если ответы сдвигаются, пороги «действуем сами / переспрашиваем / отдаём человеку» приходится подбирать на своих данных.
  • Фронтир не всегда покупает точность. На задаче реранкинга товаров все генеративные участники от Flash Lite до фронтира лежат в полосе 0.70–0.75, а потолок определяет не класс модели, а сам справочник и жанр документа. Списочный Jev при этом значимо выше всех и стоит в шестьдесят раз меньше фронтира.
  • Скорость и размер. Медиана решения для Jeff-Qwen3.5-0.8B — 22 ms на RTX PRO 6000, а большая часть времени — фиксированные накладные расходы, а не объём входа. Одна база обслуживает все адаптеры, каждый из которых занимает около 169 МБ.

Где смотреть в коде

Источники

Похожее