Привет, Хабр!
В последние недели интерес вокруг модели Jev в Ai‑сфере набрал такие обороты, что я не смог пройти мимо. Некоторые уже называют ее «убийцей» LLM, новым прорывом в развитии агентов и нашим будущим. Поэтому мне стало интересно разобраться, какова область применения модели и где она может быть полезна.
Что такое Jev
Наверное, это первый вопрос, который должен возникнуть. Это не совсем обычная LLM, к которым мы привыкли за последние годы, это так называемая decision model. Создатели (TypeSafe) называют этот класс System One Models — отсылка к «Системе 1» из книги Даниэля Канемана: быстрым, почти мгновенным решениям без длинной цепочки рассуждений. То есть само название уже говорит нам о том, что модель направлена на быстрые ответы и это должно являться ее главным преимуществом.
Она также работает с текстом, но принцип взаимодействия отличается: вместо свободной генерации ответа мы заранее задаем вопрос, контекст и допустимые варианты решения, а модель возвращает выбор (choice), оценку (score) или вероятность (Noul). То есть она не способна сгенерировать ничего, кроме вероятности заранее выбранных ответов.

Например, в одном запросе можно проверить риск вызова инструмента и достаточность данных. Вопросы к одному state обрабатываются параллельно, поэтому такой формат удобно использовать для нескольких вопросов.
Но, к сожалению, у Jev есть одно очень значимое (по крайней мере для моих задач) ограничение: до 64K токенов на весь запрос, а state + вопросдолжны укладываться в 32K.
Как попробовать
Я использовал Jev по Api через OpenRouter: Jev 1.13 — API Pricing & Providers | OpenRouter
Шаблон запроса, который я использовал в своих тестах:
response = httpx.post( "https://openrouter.ai/api/alpha/decisions", headers={ "Authorization": f"Bearer {ключ}", "Content-Type": "application/json", }, json={ "model": JEV_MODEL, "state": { "" }, "questions": { "is_risky": { "type": "noul", "instructions": ( "Опасно ли выполнять `tool_call` с учётом запроса " "пользователя в `messages`?" ), "criteria": { "true": ( "Действие удаляет или изменяет данные, отправляет " "информацию наружу либо создаёт внешний эффект." ), "false": ( "Действие только читает локальные данные, ничего " "не изменяет и не создаёт внешних эффектов." ), }, } }, }, timeout=60, ) response.raise_for_status() risk_probability = response.json()["answers"]["is_risky"]["noul"]
Применение
Использование в Claude code / Codex / Hermes и т.д
Так как пространство ответа у Jev задаётся заранее, большая часть кейсов — это скорее классификация. Но если пойти дальше, то этим всё не ограничивается: сюда же относятся guardrails, reranking, проверка подтвержденности и извлечение семантических признаков.
Но начнем с простого и, наверное, самого популярного применения: маршрутизация выбора модели для ответа на вопрос в Codex / Claude code
Вместо того чтобы всегда использовать самую дорогую и сильную модель, например, GPT-6, вы можете подключить Jev для предварительного выбора модели. Если вы, как и я, не любите самостоятельно выбирать модель и вам периодически не хватает лимитов, то можете попробовать этот вариант в своем агенте. Схема вашего запроса будет выглядеть так:

Роль Jev здесь простая: выбрать модель, которой уйдёт запрос. Все варианты заранее известны и описаны. Вы можете самостоятельно описать, когда и какую модель нужно использовать (что считать сложной или простой задачей)
Использование Jev при разработке агентов
Но мне было интересно попробовать другие кейсы использования, связанные с разработкой агентов. Конечно, здесь вы тоже можете использовать Jev для маршрутизации, но интересными мне показались другие кейсы:
Дополнительный guardrail перед tool call
Думаю, все мы периодически думаем о безопасности выполняемых агентом действий (к сожалению, это вынужденная мера) и для этого делаем проверки регулярными выражениями или более легкими моделями. Но проверка модели требует времени, а этого нам бы не хотелось, так как добавление по 1–1.5с к каждому запросу — накладно. Здесь Jev может помочь нам как замена обычным LLM. То есть перед вызовом инструмента мы перехватываем запрос и спрашиваем у модели, безопасно ли его выполнять или нет.
Так как я использую langchain, я просто написал middleware, который срабатывал при вызове определенных инструментов:
from collections.abc import Callable import httpx from langchain.agents import create_agent from langchain.agents.middleware import AgentMiddleware, ToolCallRequest from langchain_core.messages import ToolMessage from langchain_core.tools import tool from langchain_openai import ChatOpenAI from config import DEEPSEEK_MODEL, JEV_MODEL, OPENROUTER class JevGuardMiddleware(AgentMiddleware): """Блокировать опасные вызовы инструментов с помощью Jev через OpenRouter.""" def wrap_tool_call( self, request: ToolCallRequest, handler: Callable[[ToolCallRequest], ToolMessage], ) -> ToolMessage: """Проверить tool call перед выполнением. Args: request: Вызов инструмента и текущее состояние агента. handler: Функция, которая запускает инструмент. Returns: Результат инструмента или сообщение о блокировке. """ try: response = httpx.post( "https://openrouter.ai/api/alpha/decisions", headers={ "Authorization": f"Bearer {OPENROUTER}", "Content-Type": "application/json", }, json={ "model": JEV_MODEL, "state": { "messages": [ { "role": message.type, "content": message.content, } for message in request.state["messages"] ], "tool_call": request.tool_call, "tool_description": request.tool.description, }, "questions": { "is_risky": { "type": "noul", "instructions": ( "Опасно ли выполнять `tool_call` с учётом запроса " "пользователя в `messages`?" ), "criteria": { "true": ( "Действие удаляет или изменяет данные, отправляет " "информацию наружу либо создаёт внешний эффект." ), "false": ( "Действие только читает локальные данные, ничего " "не изменяет и не создаёт внешних эффектов." ), }, } }, }, timeout=60, ) response.raise_for_status() risk_probability = response.json()["answers"]["is_risky"]["noul"] except (httpx.HTTPError, KeyError, TypeError, ValueError) as error: print(f"Jev недоступен: {type(error).__name__}") return ToolMessage( content=( "Вызов заблокирован: Jev не смог проверить его безопасность. " "Не повторяй вызов автоматически." ), tool_call_id=request.tool_call["id"], name=request.tool_call["name"], status="error", ) print(f"Вероятность риска по оценке Jev: {risk_probability:.2f}") if risk_probability >= 0.3: return ToolMessage( content="Jev заблокировал опасный вызов. Инструмент не был выполнен.", tool_call_id=request.tool_call["id"], name=request.tool_call["name"], status="error", ) return handler(request) @tool def read_note(note_name: str) -> str: """Прочитать демонстрационную заметку. Args: note_name: Название заметки. Returns: Содержимое демонстрационной заметки. """ return f"Заметка {note_name!r}: встреча назначена на 15:00." @tool def delete_file(path: str) -> str: """Безвозвратно удалить файл по указанному пути. Args: path: Путь к удаляемому файлу. Returns: Сообщение о результате удаления. Note: В эксперименте функция ничего не удаляет и возвращает безопасную симуляцию. """ return f"СИМУЛЯЦИЯ: файл {path!r} не был удалён." @tool def send_email(recipient: str, text: str) -> str: """Отправить электронное письмо указанному получателю. Args: recipient: Адрес получателя письма. text: Текст отправляемого письма. Returns: Сообщение о результате отправки. Note: В эксперименте функция ничего не отправляет и возвращает безопасную симуляцию. """ return f"СИМУЛЯЦИЯ: письмо для {recipient!r} не было отправлено. Текст: {text!r}" def main() -> None: """Создать агента с middleware и запустить опасный запрос. Returns: Ничего. Итоговый ответ агента выводится в консоль. """ agent = create_agent( model=ChatOpenAI( model=DEEPSEEK_MODEL, api_key=OPENROUTER, base_url="https://openrouter.ai/api/v1", temperature=0, ), tools=[read_note, delete_file, send_email], middleware=[JevGuardMiddleware()], system_prompt=( "Выбирай подходящий инструмент для чтения заметок, удаления файлов " "и отправки писем." ), ) experiments = [ "Прочитай заметку meeting.", "Удали файл C:/important/customer_database.db.", "Отправь письмо на admin@example.com с текстом: База удалена.", ] for user_message in experiments: print(f"\nЗапрос: {user_message}") result = agent.invoke( {"messages": [{"role": "user", "content": user_message}]} ) print(f"Ответ: {result['messages'][-1].content}") if __name__ == "__main__": main()
Важно: Jev работает не как обычная LLM и не имеет привычных нам галлюцинаций. Но это не значит, что она не ошибается и ее ответы абсолютные верные (даже если ее уверенность в ответе 90%). Поэтому подбирать порог уверенности в ответе мы должны самостоятельно.
Выходные данные:
Запрос: Прочитай заметку meeting. Вероятность риска по оценке Jev: 0.03 Ответ: Заметка **meeting** прочитана. Содержимое: > встреча назначена на 15:00. Если нужно что-то ещё — например, отправить это кому-то письмом или удалить заметку — скажите. Запрос: Удали файл C:/important/customer_database.db. Вероятность риска по оценке Jev: 0.75 Ответ: Вызов был заблокирован системой безопасности — файл **не удалён**. Причина: удаление `C:/important/customer_database.db` — необратимая операция над файлом, который по названию и расположению похож на рабочую базу данных с данными клиентов. Такие действия требуют явного подтверждения и, как правило, согласования с владельцем данных или администратором. Что можно сделать: - Если удаление действительно необходимо, подтвердите это явно и уточните, есть ли резервная копия. - Если цель — освободить место или убрать устаревший файл, лучше сначала переместить его в архив/карантин, а не удалять безвозвратно. - Если файл нужно удалить в рамках регламента (например, по запросу на удаление персональных данных), укажите основание — это поможет оформить действие корректно. Сообщите, как поступить, и я помогу с дальнейшими шагами. Запрос: Отправь письмо на admin@example.com с текстом: База удалена. Вероятность риска по оценке Jev: 0.63 Ответ: Письмо отправить не удалось — вызов был заблокирован системой безопасности (Jev заблокировал опасный вызов), инструмент не выполнился. Похоже, содержимое письма («База удалена») было расценено как потенциально опасное/вводящее в заблуждение сообщение. Если это часть легитимной задачи, ут очните, пожалуйста, контекст — например, что именно за база, кто такой admin@example.com и зачем нужно это уведомление. Тогда я смогу помочь сформулировать корректный текст письма.
Скорость ответов через openrouter на этом тесте:

модель действительно отвечает за сотни миллисекунд, что в несколько раз быстрее deepseek flash
Проверка на strawberry
Здесь я решил сделать небольшое отступление и задать вопрос, который раньше мы задавали каждой новой модели:
how many r in strawberry?
import httpx from config import JEV_MODEL, OPENROUTER MODEL_QUESTION = "how many r in strawberry?" ANSWER_OPTIONS = { "0": "The answer is 0.", "1": "The answer is 1.", "3": "The answer is 3.", "2": "The answer is 2.", "4": "The answer is 4.", } def ask_jev(client: httpx.Client) -> tuple[str, dict[str, float]]: response = client.post( "https://openrouter.ai/api/alpha/decisions", json={ "model": JEV_MODEL, "state": {"question": MODEL_QUESTION}, "questions": { "answer": { "type": "choice", "instructions": "Answer the question from the `question` field.", "criteria": ANSWER_OPTIONS, } }, }, ) response.raise_for_status() answer_data = response.json()["answers"]["answer"] return answer_data["choice"], answer_data["probabilities"] def main() -> None: with httpx.Client( headers={ "Authorization": f"Bearer {OPENROUTER}", "Content-Type": "application/json", }, timeout=60, ) as client: selected_answer, probabilities = ask_jev(client) print(f"Question: {MODEL_QUESTION}") print("Answers:") for answer in ANSWER_OPTIONS: selected_marker = " <- selected" if answer == selected_answer else "" print(f" {answer}: {probabilities[answer]:.2%}{selected_marker}") if __name__ == "__main__": main()
И я был немного разочарован
Выходные данные
Question: how many r in strawberry? Answers: 0: 0.00% 1: 6.00% 3: 11.00% 2: 83.00% <- selected 4: 0.00%
Ответ «3» имеет лишь 11%. Причем уверенность в ответе 2 я получал и с другими вариантами ответов.
Частичная защита от галлюцинаций модели / преждевременного вызовы инструментов
Думаю, при разработке агентов вы могли сталкиваться с ситуацией, в которой агент вызывает инструмент еще до того, как он собрал полную информацию для ответа на вопрос и начинает придумывать параметры. Конечно, с современными моделями такие ситуации случаются все реже, но все равно случаются.
Для этого мы можем поставить дополнительную проверку перед вызовом инструмента, которая будет проверять, достаточно ли данных и просить задать уточняющий вопрос, если данных в контексте недостаточно.
Давайте проверим это на примере стандартного инструмента по получению прогноза погоды. Заставим агента вызывать инструмент на каждый вопрос, связанный с погодой и передавать параметр по умолчанию:
from collections.abc import Callable import httpx from langchain.agents import create_agent from langchain.agents.middleware import AgentMiddleware, ToolCallRequest from langchain_core.messages import ToolMessage from langchain_core.tools import tool from langchain_openai import ChatOpenAI from config import DEEPSEEK_MODEL, JEV_MODEL, OPENROUTER class RequiredDataMiddleware(AgentMiddleware): """Проверять наличие обязательных данных в последнем запросе пользователя.""" def wrap_tool_call( self, request: ToolCallRequest, handler: Callable[[ToolCallRequest], ToolMessage], ) -> ToolMessage: """Разрешить вызов инструмента или запросить уточнение у пользователя. Args: request: Предложенный вызов инструмента и состояние агента. handler: Функция фактического запуска инструмента. Returns: Результат инструмента или сообщение о нехватке данных. """ user_message = next( message.content for message in reversed(request.state["messages"]) if message.type == "human" ) try: response = httpx.post( "https://openrouter.ai/api/alpha/decisions", headers={ "Authorization": f"Bearer {OPENROUTER}", "Content-Type": "application/json", }, json={ "model": JEV_MODEL, "state": { "user_request": user_message, "tool_call": request.tool_call, }, "questions": { "has_enough_data": { "type": "noul", "instructions": ( "Достаточно ли данных, явно указанных в " "`user_request`, для выполнения `tool_call` без " "догадок?" ), "criteria": { "true": ( "Пользователь указал все данные " ), "false": ( "данных недостаточно " ), }, } }, }, timeout=60, ) response.raise_for_status() enough_data_probability = response.json()["answers"][ "has_enough_data" ]["noul"] except (httpx.HTTPError, KeyError, TypeError, ValueError) as error: print(f"Jev недоступен: {type(error).__name__}") return ToolMessage( content=( "Инструмент не был вызван: проверка данных недоступна. " "Не повторяй вызов автоматически и попроси пользователя данные" ), tool_call_id=request.tool_call["id"], name=request.tool_call["name"], status="error", ) print( "Вероятность достаточности данных по оценке Jev: " f"{enough_data_probability:.2f}" ) if enough_data_probability < 0.7: return ToolMessage( content=( "Инструмент не был вызван: пользователь не указал город. " "Не используй значение по умолчанию, не повторяй вызов " "инструмента и задай пользователю уточняющий вопрос о городе." ), tool_call_id=request.tool_call["id"], name=request.tool_call["name"], status="error", ) return handler(request) @tool def get_weather(city: str = "London") -> str: """Получить текущую фиктивную погоду в городе. Args: city: Название города. По умолчанию используется London. Returns: Строка с демонстрационными погодными данными. """ return f"Сейчас в городе {city}: +18 °C, переменная облачность." def main() -> None: agent = create_agent( model=ChatOpenAI( model=DEEPSEEK_MODEL, api_key=OPENROUTER, base_url="https://openrouter.ai/api/v1", temperature=0, ), tools=[get_weather], middleware=[RequiredDataMiddleware()], system_prompt=( "Для ответа на любой вопрос о погоде всегда сначала вызывай " "get_weather." ), ) experiments = [ "Какая сейчас погода?", "Какая сейчас погода в Москве?", ] for user_message in experiments: print(f"\nЗапрос: {user_message}") result = agent.invoke( {"messages": [{"role": "user", "content": user_message}]} ) print(f"Ответ: {result['messages'][-1].content}") if __name__ == "__main__": main()
Выходные данные
Запрос: Какая сейчас погода? Вероятность достаточности данных по оценке Jev: 0.06 Ответ: Уточните, пожалуйста, для какого города вы хотите узнать погоду? Запрос: Какая сейчас погода в Москве? Вероятность достаточности данных по оценке Jev: 0.95 Ответ: Сейчас в Москве +18 °C, переменная облачность.
В этом примере Jev видит только последний запрос пользователя, потому что я сам извлекаю user_message и кладу в state только его вместе с tool_call.
RAG
Да, здесь тоже можно найти применение JEV
Я уже немного отошел от создания классических rag систем с чанками, реранкерами и так далее в сторону llm wiki систем, но, думаю, что использовать Jev для определения, подходит ли чанк под запрос пользователя и нужно ли его возвращать модели — тоже достаточно интересный кейс. То есть Jev можно попросить оценить, подходит ли конкретный чанк под запрос. Сам по себе дополнительный этап поиска не ускоряет. Эффект появится, если Jev заменит какой‑нибудь LLM‑reranker. Для каждого кандидата достаточно одного вопроса: релевантен ли этот chunk запросу?. После этого кандидатов можно отсортировать по вероятности.
Также можно использовать эту модель для выставления groundedness каждому фрагменту

Если LLM написала «выручка выросла на 17%», Jev может отдельно проверить, подтверждает ли это указанный чанк.
Классификация и разметка данных
Для меня это был один из самых интересных способов использования. В последних версиях Excel уже можно подключить Jev по Api и за секунды классифицировать тысячи строк. Я сделал excel файл из 200 задач и захотел проклассифицировать их на простые, средние и сложные, затем сравнить результаты с DeepSeek‑v4.1-flash:
def classify_with_jev(task: str, client: httpx.Client) -> str: response = client.post( "https://openrouter.ai/api/alpha/decisions", json={ "model": JEV_MODEL, "state": {"task": task}, "questions": { "complexity": { "type": "choice", "instructions": "Определи сложность задачи из поля `task`.", "criteria": { "Простая": ( "Одна понятная операция без зависимостей и сложного анализа." ), "Средняя": ( "Несколько связанных шагов, умеренный анализ или интеграция." ), "Сложная": ( "Много этапов и зависимостей, архитектурные решения, " "миграция или высокая неопределённость." ), }, } }, }, ) response.raise_for_status() return response.json()["answers"]["complexity"]["choice"] def classify_with_deepseek(task: str, client: OpenAI) -> str: """Определить сложность одной задачи с помощью DeepSeek. """ for attempt in range(3): response = client.chat.completions.create( model=DEEPSEEK_MODEL, temperature=0, max_tokens=100, messages=[ { "role": "system", "content": ( "Классифицируй сложность задачи. Простая — одна понятная " "операция без зависимостей. Средняя — несколько связанных " "шагов, умеренный анализ или интеграция. Сложная — много " "этапов и зависимостей, архитектура, миграция или высокая " "неопределённость. Верни только данные по заданной JSON-схеме." ), }, {"role": "user", "content": task}, ], response_format={ "type": "json_schema", "json_schema": { "name": "task_complexity", "strict": True, "schema": { "type": "object", "properties": { "answer": { "type": "string", "enum": ["Простая", "Средняя", "Сложная"], } }, "required": ["answer"], "additionalProperties": False, }, }, }, extra_body={ "provider": {"require_parameters": True}, "reasoning": {"enabled": False}, }, ) try: answer = json.loads(response.choices[0].message.content)["answer"] if answer in {"Простая", "Средняя", "Сложная"}: return answer except (json.JSONDecodeError, KeyError, TypeError): if attempt == 2: raise ValueError("DeepSeek трижды вернул невалидный JSON.") raise ValueError("DeepSeek вернул неизвестную категорию.") def run_parallel( tasks: list[str], classifier: Callable[[str], str], ) -> tuple[list[str], float]: started_at = perf_counter() with ThreadPoolExecutor(max_workers=THREADS_COUNT) as executor: categories = list(executor.map(classifier, tasks)) elapsed_seconds = perf_counter() - started_at return categories, elapsed_seconds def main() -> None: workbook = load_workbook(INPUT_FILE) tasks_sheet = workbook["Задачи"] tasks = [tasks_sheet.cell(row=row, column=2).value for row in range(2, 202)] with httpx.Client( headers={ "Authorization": f"Bearer {OPENROUTER}", "Content-Type": "application/json", }, timeout=60, ) as jev_client: jev_categories, jev_seconds = run_parallel( tasks, lambda task: classify_with_jev(task, jev_client), ) with OpenAI( api_key=OPENROUTER, base_url="https://openrouter.ai/api/v1", timeout=60, max_retries=2, ) as deepseek_client: deepseek_categories, deepseek_seconds = run_parallel( tasks, lambda task: classify_with_deepseek(task, deepseek_client), ) for row, (jev_category, deepseek_category) in enumerate( zip(jev_categories, deepseek_categories), start=2, ): tasks_sheet.cell(row=row, column=3, value=jev_category) tasks_sheet.cell(row=row, column=4, value=deepseek_category) if "Benchmark" in workbook.sheetnames: del workbook["Benchmark"] benchmark_sheet = workbook.create_sheet("Benchmark", 0) benchmark_sheet.append( ["Модель", "Задач", "Потоков", "Время, сек.", "Задач в секунду"] ) benchmark_sheet.append( ["Jev 1.13", len(tasks), THREADS_COUNT, jev_seconds, len(tasks) / jev_seconds] ) benchmark_sheet.append( [ "DeepSeek", len(tasks), THREADS_COUNT, deepseek_seconds, len(tasks) / deepseek_seconds, ] ) benchmark_sheet.freeze_panes = "A2" benchmark_sheet.sheet_view.showGridLines = False benchmark_sheet.column_dimensions["A"].width = 18 benchmark_sheet.column_dimensions["B"].width = 12 benchmark_sheet.column_dimensions["C"].width = 12 benchmark_sheet.column_dimensions["D"].width = 18 benchmark_sheet.column_dimensions["E"].width = 22 for cell in benchmark_sheet[1]: cell.fill = PatternFill("solid", fgColor="1F4E78") cell.font = Font(name="Arial", size=10, bold=True, color="FFFFFF") for row in benchmark_sheet.iter_rows(min_row=2, max_row=3): for cell in row: cell.font = Font(name="Arial", size=10, color="1F2937") for cell in benchmark_sheet["D"][1:]: cell.number_format = "0.00" for cell in benchmark_sheet["E"][1:]: cell.number_format = "0.00" workbook.save(OUTPUT_FILE) print(f"Jev: {jev_seconds:.2f} сек., {len(tasks) / jev_seconds:.2f} задач/сек.") print( f"DeepSeek: {deepseek_seconds:.2f} сек., " f"{len(tasks) / deepseek_seconds:.2f} задач/сек." ) print(f"Результат сохранён: {OUTPUT_FILE}") if __name__ == "__main__": main()
Результат
Модель |
Время |
Производительность |
|---|---|---|
Jev 1.13 |
12,46 сек. |
16,05 задач/сек. |
DeepSeek structured output |
32,38 сек. |
6,18 задач/сек. |
В моём тесте Jev оказался примерно в 2,6 раза быстрее DeepSeek со structured output. Оба варианта шли через OpenRouter в 10 потоков. Но здесь стоит сказать, что по моему субьективному мнению Deepseek именно с классификацией справился лучше, поэтому стоит определить для себя, что важнее для решения вашей задачи.
Есть ли что то еще
После тестов больше всего мне понравилась скорость ответов от этой модели и я стал думать, а есть ли что то еще быстрее.
И оказалось, что есть. Здесь мне было не принципиально, на какой архитектуре будет модель. Главное, чтобы она отвечала так же быстро и с примерно такой же точностью. За эти недели появилось огромное количество вариантов, которые были сделаны, как на архитектуре обычных LLM, так и на ModernBert.
Вот некоторые из них:
Модель |
Основа |
Тип |
|---|---|---|
Jev |
закрытая архитектура TypeSafe |
System 1 |
Laya |
ModernBERT |
System 1 |
Decider |
Qwen3.5 |
System 1 |
Nimble |
Qwen3.5–9B |
System 1 |
Kev |
Qwen + LoRA |
System 1 |
Plumb |
Qwen3.5–4B |
System 1 |
SemIf |
Qwen |
System 1 |
CLM |
Qwen3-8B |
System 1 |
Winnow-12B |
Gemma 4 12B |
System 1 + System 2 |
Qwen 3.5 JSON |
Qwen |
System 2 |
ModernBERT NLI |
ModernBERT |
обычный классификатор |
Laya
Первое на что я наткнулся — Laya.
Наверное, Laya — самый популярный аналог Jev, который в каждом абзаце на своем сайте сравнивает себя с Jev. Это открытая decision model на базе ModernBERT‑large. У модели около 421 млн параметров, она не генерирует текст и поддерживает те же основные типы решений: choice, score и noul.
Ключевая особенность — Laya можно запускать локально и дообучать под свои задачи. У авторов также есть пример дообучения: How to Fine‑Tune Laya: RLCD Notebook, Calibration & Custom Heads
Ещё один плюс — низкая задержка при локальном запуске. В опубликованных тестах встречаются значения порядка десятков миллисекунд. У меня на GPU время ответа было около 30 миллисекунд, что еще на порядок быстрее Jev.
Недостаток — из коробки базовая Laya на части задач заметно уступает Jev. Но после fine‑tuning результаты уже сильно зависят от конкретной задачи и данных.
Вот таблица с метрики с сайта Laya:

Чтобы попробовать Laya достаточно установить пакет:
pip install laya
И загрузить веса ( будут загружены при первом запуске)
from laya import Router router = Router(max_loaded=1, device="cpu") questions = { "category": { "type": "choice", "instructions": "Определи категорию сообщения.", "criteria": { "оплата": "Вопросы о платежах, списаниях и возвратах денег.", "доставка": "Вопросы о доставке и местонахождении заказа.", "техническая проблема": "Ошибки и неполадки приложения или сайта.", }, } } result = router.predict( "С моего счёта дважды списали деньги за один заказ.", questions, model="multilingual", ) print(result["answers"]["category"]["choice"])
Decider
Decider построен на Qwen3.5.
Например, Decider-4B использует Qwen3.5–4B, но не генерирует ответ. Модель берёт logits допустимых вариантов и превращает их в probabilities.
То есть внутри остаётся обычная LLM, но инференс уже работает как System 1.

Nimble
Сюда же относится Nimble дообученная Qwen3.5–9B. Она также напрямую оценивает разрешённые варианты без генерации текста. Авторы публикуют веса и recipe обучения, поэтому можно посмотреть, как обычную LLM дообучают именно под decision‑задачи.
https://github.com/bespokelabsai/nimble
Kev
Kev тоже использует Qwen, но добавляет LoRA + отдельный модуль выбора.
В этом случае механизм принятия решения уже отделён от обычной генеративной головы модели.

Winnow-12B
Winnow-12B построена на Gemma 4 12B и умеет работать сразу в двух режимах:
То есть одни и те же веса можно использовать и для коротких решений, и как обычную генеративную модель. По моему субьективному мнению это фаворит среди всех вариантов. Она устроила меня как по скорости и качеству ответов, так и по концепции.

Метрики
На просторах интернета я нашел таблицу с сравнением разных аналогов Jev. Мне она показалась довольно интересной, поэтому я оставлю ссылку на источник и мой Telegram, где также можно посмотреть полную таблицу (на него стоит подписаться, там я публикую больше про AI): https://t.me/Viacheslav_Talks
Model |
Accuracy |
Correct / scored |
Not correct |
Failed output or request |
Quality P50 / P95 |
|---|---|---|---|---|---|
Jev 1.13 |
95.23% |
4 414 / 4 635 |
221 |
22 invalid outputs included |
249 ms / 303 ms |
Winnow 12B |
94.61% |
4 385 / 4 635 |
250 |
00 invalid outputs included |
52 ms / 83 ms |
Decider 4B · v2 |
94.46% |
4 378 / 4 635 |
257 |
55 invalid outputs included |
45 ms / 152 ms |
Nimble 9B |
92.34% |
4 280 / 4 635 |
355 |
00 invalid outputs included |
46 ms / 109 ms |
Plumb 4B |
89.54% |
4 150 / 4 635 |
485 |
00 invalid outputs included |
40 ms / 131 ms |
Qwen 3.5 · JSON |
85.78% |
3 976 / 4 635 |
659 |
00 invalid outputs included |
291 ms / 904 ms |
SemIf 4B |
81.70% |
3 787 / 4 635 |
848 |
00 invalid outputs included |
43 ms / 116 ms |
Laya |
64.83% |
3 005 / 4 635 |
1 630 |
66 invalid outputs included |
18 ms / 73 ms |
Laya · typed |
63.91% |
2 962 / 4 635 |
1 673 |
55 invalid outputs included |
17 ms / 72 ms |
ModernBERT · NLI |
58.73% |
2 722 / 4 635 |
1 913 |
00 invalid outputs included |
17 ms / 48 ms |
Laya · multilingual |
56.31% |
2 610 / 4 635 |
2 025 |
1515 invalid outputs included |
16 ms / 64 ms |
CLM 8B |
36.09% |
1 673 / 4 635 |
2 962 |
00 invalid outputs included |
117 ms / 125 ms |
Источник: I Tested Jev vs 12 Self‑Hosted AI Decision Models... — YouTube
Итоги
Когда я только начал разбираться в Jev, первая мысль была примерно такой: новой задачи здесь нет — классификация, routing и scoring существовали давно. И на самом деле мое первоначальное мнение «некоторые задачи теперь можно выполнять быстрее» в целом осталось тем же.
После экспериментов я бы только добавил, почему Jev и аналоги всё‑таки интересны. В одной модели сошлись несколько полезных свойств:
понимание естественного языка;
ограниченное пространство ответов;
низкая задержка и стоимость; — самое интересное на мой взгляд
возможность задавать несколько отдельных вопросов к одному
stateбез потери в скорости.
Поэтому для меня такие модели (особенно те, что можно дообучить), скорее всего, займут место в real time системах, где раньше я не думал использовал LLM из за их скорости.
Спасибо за прочтение и делитесь своим опытом!
Комментарии (3)

Mavito
01.10.2026 07:59Спасибо за такую разностороннюю публикацию о применении Jev.
Не могли бы вы указать ссылку на источник картинки из раздела про RAG, чтобы детальнее ознакомиться с упомянутым применением Jev в RAG? И, может быть, найдется ссылка на такое же хорошее описание с примерами режимов Jev: Noul, Choice, Score.
На ваш взгляд, может ли Jev (особенно его локальные варианты) использоваться как LLM-as-a-judge, в частности, как альтернатива моделям POLLUX (пример вызова модели / публикация про POLLUX) для оценки ответов LLM по произвольно заданному списку критериев с текстовым описанием правил назначения баллов?
На ваш взгляд, позволяет ли Jev каким-то способом сделать аналог Bert модели — экстрактора инфоконтекстов Яндекса, например, если подавать в Jev текст с указанием номеров строк, и большой набор пересекающихся вариантов для выбора границ (номеров строк) relevant_content в тексте: [0,6],[3,9],…[N-6,N]?
fireSparrow
"who many r in strawberry?"В такой формулировке я бы тоже растерялся
Viacheslav-hub Автор
не заметил ошибку, когда переписывал) Спасибо
Но итог не меняет:
Question: how many r in strawberry? Answers: 0: 0.00% 1: 12.00% 3: 5.00% 2: 83.00% <- selected 4: 0.00%
Интересно было также, что на вопрос с бинарной классификацией по типу "Действительно ли...."
Ответы тоже были разные и сильно отличались при добавлении других вариантов ответа