Это исходный отчёт фазы как есть — со всеми таблицами и методикой. Живая версия: 💬 Языковые модели.
Фаза 5: LLM — typed output, вызов инструментов и качество на русском
Прогон: 19–20 октября 2026
Хост: UGREEN DXP4800 Pro, i3-1315U, 32 GB, iGPU Intel RPL-U (Xe-LP, 64 EU)
Что меряли: насколько надёжно локальные LLM возвращают JSON заданной структуры, вызывают команды Home Assistant и справляются с русским текстом. Отдельно проверено, дают ли модели Вихря выигрыш против своих базовых моделей.
Коротко
- Ответ в заданной структуре JSON стабильно получается у всех моделей, если делать две вещи: передавать схему через
response_format: json_schemaи писать вpattern[0-9]вместо\d.- В таком режиме все 14 моделей дали 100% ответов, проходящих JSON Schema.
- Без замены
\dграмматика llama.cpp молча не проверяет форматы. Маленькие модели тогда пишут «89641508629» вместо «+79641508629» и «08.02.2026» вместо «2026-02-08», валидно только 71–75% ответов.
- По точности извлечения лидеры почти идеальны: 98.5–99.3% полей, 92–96% документов без единой ошибки.
- Лучше всех Gemma 4: 96% документов без ошибок (см. ниже про флаги).
- Затем Qwen3.6-35B-A3B, gpt-oss-20b, Qwen3-8B и Mistral-Small 24B: 99.3% полей, 92% документов.
- На документ они тратят 18–22 с, Mistral 49 с.
- Быстрее при чуть меньшей точности: Ling 3.0 Tiny (98.1%, 6 с) и Qwen3.5-4B (97.8%, 14 с).
- Выдумывают значения вместо
nullредко: 0–4 из 11 полей.
- Команды Home Assistant лучше всех выполняет Qwen3.5-4B: 40 из 40 верно, медиана ответа 6.6 с, 4.7 GiB.
- Qwen3-4B, Qwen3-8B и QVikhr-3-8B ошиблись в одной команде из 40.
- Ling 3.0 Tiny отвечает быстрее всех (2.6 с), но понял «Погаси свет» как «включи».
- gpt-oss в двойных командах выполняет только первую половину.
- Mistral-Small переспрашивает там, где можно действовать.
- Лучшее качество на русском у Qwen3.6-35B-A3B: Belebele 94%, MMLU 89%, MGSM 90%, 8.7 tok/s, 17.8 GiB.
- Рядом Gemma 4 (94 / 85 / 85) и gpt-oss-20b (88 / 84 / 85).
- Mistral-Small и Vistral (24B, dense) дают 92 / 80–81 / 82–88, но генерируют всего 2.5 tok/s.
- Вихрь не даёт выигрыша против своих базовых моделей.
- QVikhr-3 1.7B, 4B и 8B на извлечении JSON на 1.5–3.3 п.п. хуже Qwen3. На командах HA на 0–10 п.п. хуже, на MGSM на 2–5 п.п.
- Заметный плюс только на MMLU у маленьких моделей: +11 у 1.7B и +7 у 4B. У 8B уже −2.
- Vistral-24B по знаниям равен Mistral-Small-3.2 (MMLU +1, MGSM +5, то есть две задачи из 40), но не вызывает инструменты вообще: 10% против 85% у базовой модели. На команды он отвечает текстом «Включаю свет на кухне», а вызова нет.
- Gemma 4 26B-A4B точнее всех разбирает документы, но на этой сборке llama.cpp и этом iGPU работает только с обходными флагами.
- 96% документов без единой ошибки, 100% валидного JSON на JSONSchemaBench, 39 из 40 команд HA, Belebele 94%, MMLU 85%.
- Без
--swa-fullона выдаёт мусор, а запросы с грамматикой вешают iGPU. - С
--swa-fullломается переиспользование кэша промпта, и приходится отключать его совсем. Тогда каждая команда HA заново обрабатывает 2K токенов и занимает ~30 с.
Как мерили
- Модели: 14 GGUF-файлов.
- 6 из прошлого
llm-bench. - 4 модели Вихря и их базовые модели в той же квантизации Q4_K_M:
- QVikhr-3 1.7B / 4B / 8B против Qwen3 1.7B / 4B / 8B;
- Vistral-24B против Mistral-Small-3.2-24B.
- 6 из прошлого
- Сервер: llama.cpp
server-vulkan, build 11718 (sha256:7f8b8664…).- Параметры как в
llm-bench:-c 8192 -np 1 -ngl 99 -fa off -t 2 --jinja. - Плюс
--cache-ram 0(см. «Заметки для фреймворка»). - Рассуждение выключено (
--reasoning off) там, где модель это позволяет:- gpt-oss выключить рассуждение не даёт, поэтому
reasoning_effort: lowи запас 1024 токена; - у LFM2.5 стоит
--reasoning-budget 0; - у Gemma 4 добавлен
--swa-full, без него запросы с грамматикой вешают iGPU.
- gpt-oss выключить рассуждение не даёт, поэтому
- Параметры как в
- Запросы:
temperature 0,seed 42, по одному. -
Typed output. 24 синтетических документа (
make_extract.py) с известным правильным JSON и JSON Schema. Ловушки:- «1 250,50 ₽» и «8,5 млн руб.»;
- «1 ч 15 мин» → 75;
- «18 октября 2026 г.» →
2026-10-18; - телефоны →
+7XXXXXXXXXX; - русские описания → английские enum («течёт смеситель» →
plumbing); - 11 полей, которых в тексте нет и где нужен
null.
Два режима: - A — схема только в промпте; - B —
response_format: json_schema, грамматика llama.cpp. - Вызов инструментов. 40 русских голосовых команд (make_ha.py) и 13 инструментов в формате HA LLM API. Среди команд: - двойные («выключи свет на кухне и в коридоре»); - с параметрами (яркость, температура, громкость, таймер); - 4 фразы, где вызывать ничего не нужно, включая несуществующий «свет в ванной».Проверяется только первый ответ модели, сами инструменты не выполняются. - JSONSchemaBench. 12 реальных схем из JSONSchemaBench (MIT), задача — сгенерировать валидный пример. - Качество на русском: - Belebele
rus_Cyrl— 100 вопросов на понимание прочитанного; - Global-MMLUru— 100 вопросов по 11 предметам; - MGSMru— 40 задач. - Воспроизведение: - данные:make_extract.py,make_ha.py,prepare_public.py; - на NAS:run_llm.sh <модель> [наборы] [N],run_matrix.sh; - клиентllm_client.py, оценкаscore_llm.py; - сырые ответы лежат вraw/, таблицы генерируетpython3 make_report.py.
Typed output: извлечение в JSON
24 синтетических русских документа (счета, заявки, письма о встречах, рецепты, объявления, отзывы) с известным правильным JSON. A — схема только в промпте, B — response_format: json_schema (грамматика llama.cpp), C — то же, но в pattern вместо \d написано [0-9] (грамматика llama.cpp \d молча пропускает). «Валидно» — ответ разобран и проходит JSON Schema; «поля» — доля верных полей (числа ±0.01, даты, enum и телефоны точно, имена нечётко); «документ» — доля документов без единой ошибки; «выдумано» — сколько из 11 полей, которых в тексте нет, модель заполнила вместо null (режим C).
| Модель | Валидно A / B / C, % | Поля A / B / C, % | Документ A / B / C, % | Выдумано C | tok/s | симв/с | Ответ, с |
|---|---|---|---|---|---|---|---|
| Qwen3-1.7B | 71 / 75 / 100 | 93.7 / 94.1 / 95.5 | 54 / 58 / 67 | 2 из 11 | 20.2 | 46 | 5.6 |
| QVikhr-3-1.7B (Вихрь) | 71 / 75 / 100 | 90.4 / 90.7 / 92.2 | 46 / 50 / 54 | 1 из 11 | 20.2 | 46 | 5.3 |
| Qwen3-4B | 100 / 100 / 100 | 96.3 / 96.3 / 96.3 | 67 / 67 / 67 | 4 из 11 | 11.0 | 25 | 10.9 |
| QVikhr-3-4B (Вихрь) | 96 / 96 / 100 | 94.8 / 94.8 / 94.8 | 62 / 62 / 62 | 4 из 11 | 11.0 | 25 | 11.5 |
| Qwen3-8B | 96 / 96 / 100 | 98.9 / 98.9 / 99.3 | 88 / 88 / 92 | 1 из 11 | 6.7 | 15 | 18.4 |
| QVikhr-3-8B (Вихрь) | 96 / 96 / 100 | 97.0 / 97.0 / 97.4 | 83 / 83 / 88 | 1 из 11 | 6.7 | 15 | 16.9 |
| Mistral-Small-3.2-24B | 100 / 100 / 100 | 99.3 / 99.3 / 99.3 | 92 / 92 / 92 | 1 из 11 | 2.5 | 6 | 49.4 |
| Vistral-24B (Вихрь) | 100 / 100 / 100 | 99.3 / 99.3 / 99.3 | 92 / 92 / 92 | 1 из 11 | 2.5 | 6 | 49.4 |
| Qwen3.5-4B | 96 / 96 / 100 | 97.4 / 97.4 / 97.8 | 71 / 71 / 75 | 0 из 11 | 10.1 | 22 | 14.1 |
| Ling 3.0 Tiny | 83 / 92 / 100 | 97.0 / 97.4 / 98.1 | 75 / 79 / 79 | 0 из 11 | 23.0 | 52 | 5.9 |
| LFM2.5-24B-A2B | 83 / 83 / 100 | 94.9 / 95.6 / 95.6 | 71 / 75 / 75 | 0 из 11 | 14.9 | 38 | 9.2 |
| gpt-oss-20b | 100 / 100 / 100 | 99.3 / 99.3 / 99.3 | 92 / 92 / 92 | 0 из 11 | 9.3 | 13 | 19.2 |
| Gemma 4 26B-A4B | 100 / 100 / 100 | 98.5 / 98.5 / 98.5 | 96 / 96 / 96 | 0 из 11 | 7.8 | 16 | 21.5 |
| Qwen3.6-35B-A3B | 100 / 100 / 100 | 99.3 / 99.3 / 99.3 | 92 / 92 / 92 | 1 из 11 | 8.7 | 19 | 18.0 |
Вызов инструментов: команды Home Assistant
40 русских команд, 13 инструментов в формате HA LLM API (HassTurnOn, HassLightSet, HassClimateSetTemperature, ...). «Верно» — набор вызовов совпал с ожидаемым (инструмент, устройство или комната, параметры); «инструмент» — хотя бы выбран нужный инструмент; «лишние вызовы» — сколько из 4 фраз без команды (спасибо, как дела, несуществующий свет в ванной...) модель всё равно что-то вызвала. «Ответ» — медиана времени ответа на команду (обработка промпта ~2K токенов с описанием дома + генерация).
| Модель | Верно, % | Инструмент, % | Лишние вызовы | Ответ, с |
|---|---|---|---|---|
| Qwen3-1.7B | 72 | 80 | 1/4 | 2.6 |
| QVikhr-3-1.7B (Вихрь) | 62 | 75 | 1/4 | 2.5 |
| Qwen3-4B | 98 | 100 | 0/4 | 4.7 |
| QVikhr-3-4B (Вихрь) | 92 | 100 | 0/4 | 4.8 |
| Qwen3-8B | 98 | 100 | 0/4 | 7.5 |
| QVikhr-3-8B (Вихрь) | 98 | 100 | 0/4 | 7.6 |
| Mistral-Small-3.2-24B | 85 | 85 | 0/4 | 16.0 |
| Vistral-24B (Вихрь) | 10 | 10 | 0/4 | 7.8 |
| Qwen3.5-4B | 100 | 100 | 0/4 | 6.6 |
| Ling 3.0 Tiny | 90 | 90 | 1/4 | 2.6 |
| LFM2.5-24B-A2B | 92 | 98 | 0/4 | 5.3 |
| gpt-oss-20b | 92 | 92 | 0/4 | 9.9 |
| Gemma 4 26B-A4B | 98 | 98 | 0/4 | 30.1 |
| Qwen3.6-35B-A3B | 95 | 98 | 1/4 | 9.1 |
JSONSchemaBench: реальные схемы
12 схем (6 Glaiveai2K, 6 Github_medium), задача — сгенерировать валидный пример. B — с грамматикой; «не поддержано» — llama.cpp отказался превращать схему в грамматику.
| Модель | Валидно A, % | Валидно B, % | Не поддержано B |
|---|---|---|---|
| Qwen3-1.7B | 83 | 92 | 0 |
| QVikhr-3-1.7B (Вихрь) | 83 | 92 | 0 |
| Qwen3-4B | 83 | 92 | 0 |
| QVikhr-3-4B (Вихрь) | 75 | 92 | 0 |
| Qwen3-8B | 83 | 92 | 0 |
| QVikhr-3-8B (Вихрь) | 83 | 92 | 0 |
| Mistral-Small-3.2-24B | 92 | 92 | 0 |
| Vistral-24B (Вихрь) | 100 | 100 | 0 |
| Qwen3.5-4B | 83 | 100 | 0 |
| Ling 3.0 Tiny | 92 | 92 | 0 |
| LFM2.5-24B-A2B | 58 | 100 | 0 |
| gpt-oss-20b | 92 | 100 | 0 |
| Gemma 4 26B-A4B | 100 | 100 | 0 |
| Qwen3.6-35B-A3B | 100 | 100 | 0 |
Качество на русском
Belebele — понимание прочитанного (100 вопросов, 4 варианта, случайный ответ = 25%), Global-MMLU — знания по школьным и вузовским предметам (100), MGSM — арифметические задачи с рассуждением (40). Для Belebele и MMLU два числа: с грамматикой, которая разрешает ответить только буквой (это знание), и свободный ответ на просьбу «одной буквой» (это ещё и послушность формату; ответ засчитывается, только если начинается с буквы или после «Ответ:»). н/п — сервер не принял грамматику для этой модели (LFM2.5: «Unexpected empty grammar», gpt-oss: ответ не совпал с грамматикой из-за формата рассуждения). Рассуждение выключено везде, где модель это позволяет. tok/s — генерация на MGSM, симв/с — видимого ответа; сравнивать удобнее символы, токенизаторы у моделей разные. RAM — пик за весь прогон.
| Модель | Формат | Belebele, % (буква / свободно) | MMLU, % (буква / свободно) | MGSM, % | tok/s | симв/с | RAM, GiB |
|---|---|---|---|---|---|---|---|
| Qwen3-1.7B | Q4_K_M | 66 / 67 | 34 / 21 | 50 | 20.9 | 42 | 3.0 |
| QVikhr-3-1.7B (Вихрь) | Q4_K_M, на базе Qwen3-1.7B | 65 / 68 | 45 / 51 | 45 | 20.9 | 40 | 3.0 |
| Qwen3-4B | Q4_K_M | 90 / 90 | 54 / 54 | 70 | 11.4 | 22 | 4.9 |
| QVikhr-3-4B (Вихрь) | Q4_K_M, на базе Qwen3-4B | 91 / 90 | 61 / 61 | 68 | 11.4 | 24 | 5.0 |
| Qwen3-8B | Q4_K_M | 74 / 77 | 66 / 67 | 82 | 6.8 | 14 | 6.8 |
| QVikhr-3-8B (Вихрь) | Q4_K_M, на базе Qwen3-8B | 64 / 76 | 64 / 65 | 78 | 6.9 | 14 | 6.7 |
| Mistral-Small-3.2-24B | Q4_K_M, dense | 92 / 92 | 80 / 80 | 82 | 2.5 | 5 | 15.6 |
| Vistral-24B (Вихрь) | Q4_K_M, dense, на базе Mistral-Small-3.2 | 92 / 92 | 81 / 81 | 88 | 2.5 | 5 | 15.6 |
| Qwen3.5-4B | Q4_K_M | 82 / 82 | 74 / 74 | 82 | 10.2 | 26 | 4.7 |
| Ling 3.0 Tiny | Q4_K_M, MoE | 88 / 88 | 70 / 70 | 75 | 23.7 | 44 | 5.3 |
| LFM2.5-24B-A2B | Q4_0, MoE | н/п / 67 | н/п / 61 | 70 | 15.1 | 38 | 13.9 |
| gpt-oss-20b | MXFP4, MoE, рассуждает всегда | н/п / 88 | н/п / 84 | 85 | 9.5 | 13 | 13.0 |
| Gemma 4 26B-A4B | IQ4_XS, MoE | 94 / 94 | 85 / 84 | 85 | 8.0 | 20 | 17.3 |
| Qwen3.6-35B-A3B | IQ4_XS, MoE | 94 / 94 | 89 / 89 | 90 | 8.7 | 22 | 17.8 |
Вихрь против базовой модели
Разница «Вихрь − база» в процентных пунктах на тех же задачах и в той же квантизации. Плюс — Вихрь лучше.
| Пара | Поля C | HA верно | Belebele (буква) | MMLU (буква) | MGSM | симв/с |
|---|---|---|---|---|---|---|
| QVikhr-3-1.7B (Вихрь) vs Qwen3-1.7B | -3.3 | -10 | -1 | +11 | -5 | -2 |
| QVikhr-3-4B (Вихрь) vs Qwen3-4B | -1.5 | -5 | +1 | +7 | -2 | +2 |
| QVikhr-3-8B (Вихрь) vs Qwen3-8B | -1.9 | +0 | -10 | -2 | -5 | -0 |
| Vistral-24B (Вихрь) vs Mistral-Small-3.2-24B | +0.0 | -75 | +0 | +1 | +5 | +0 |
Выводы и рекомендации
Что ставить
| Задача | Рекомендация | Почему |
|---|---|---|
| Голосовые команды Home Assistant | Qwen3.5-4B | 40 из 40 команд, 6.6 с на ответ, 4.7 GiB. Запасной вариант — Qwen3-4B (39 из 40, 4.7 с) |
| Разбор документов в JSON | Qwen3.6-35B-A3B или gpt-oss-20b, json_schema + [0-9] |
99.3% полей, 92% документов без ошибок, 100% валидного JSON; ~18–19 с на документ. Gemma 4 точнее (96% документов), но только с --swa-full и без кэша промпта |
| То же, но быстро | Ling 3.0 Tiny | 98.1% полей за 6 с, 23 tok/s. Для команд опаснее: путает «погаси» и «включи» |
| Ответы на вопросы, тексты на русском | Qwen3.6-35B-A3B | лучший по Belebele, MMLU и MGSM при 8.7 tok/s; Gemma 4 рядом (94 / 85 / 85) |
| Не брать | 1.7B-модели, Vistral для инструментов, плотные 24B | 62–72% команд; Vistral не вызывает инструменты; 2.5 tok/s и 49 с на документ |
Что видно в цифрах
- Грамматика гарантирует синтаксис, но не форматы. Режим B (
json_schema) по сравнению с A (схема в промпте) поднимает валидность маленьких и MoE-моделей (Ling 83 → 92%), но пока вpatternстоит\d, телефоны и даты не проверяются.- С
[0-9](режим C) валидно 100% ответов у всех, и точность полей тоже немного растёт (Qwen3-1.7B 94.1 → 95.5%). - У больших моделей ответы A, B и C совпадают: при жадной генерации они и так пишут правильный JSON.
- С
- Грамматика может загнать модель в бесконечную строку. В одной схеме JSONSchemaBench есть строковое поле без ограничения длины. Под грамматикой 8 моделей из 14 (все Qwen3/QVikhr-3, Mistral-Small, Ling) зациклились на нём и выдали вроде «jZ2dna2NjZ2d…» до лимита токенов. Qwen3.5, Qwen3.6, gpt-oss, Gemma 4, LFM2.5 и Vistral закончили нормально. В своих схемах стоит задавать
maxLength. - Типичные ошибки извлечения:
- лишние элементы в списках («торг уместен» в преимуществах квартиры);
- выдуманная дата визита или время окончания встречи, которых в тексте нет;
- неверная срочность заявки (
normalвместоlow); - у LFM2.5 лишние цифры в телефонах («+7907129015400») и цена «4990» вместо 4 990 000.
- Команды HA. Ошибки делятся на три вида:
- опасные: не то действие (Ling: «погаси» → включить), не то устройство (LFM: «включи кондиционер» → обогреватель), несуществующее устройство (Qwen3.6: «свет в ванной»);
- неполные: gpt-oss в ответ на двойную команду делает один вызов. В реальном цикле агента он, скорее всего, сделал бы второй после результата первого, но здесь проверялся только первый ответ;
- осторожные: Mistral спрашивает «в какой комнате включить кондиционер?», хотя кондиционер один.
- Свободный ответ и знание — разные вещи. Qwen3-1.7B на просьбу «ответь одной буквой» почти всегда начинает рассуждать, и в свободном режиме набирает 21% на MMLU. С грамматикой, которая разрешает только букву, он набирает 34%. У Qwen3-8B в свободном режиме перекос к ответу «D» (47 из 100 при 28 правильных).
- Скорость.
- Генерация: MoE-модели 8–24 tok/s, 4B около 10–11 tok/s, плотные 24B 2.5 tok/s.
- На команду HA уходит 2.5–10 с (у плотных 24B 8–16 с), если системный промпт (~2K токенов с описанием дома) уже в кэше слота. Без кэша на 4B только обработка промпта занимает ~22 с.
Вихрь против базовых моделей
- QVikhr-3 1.7B / 4B / 8B. Скорость и память те же, что у Qwen3. Извлечение JSON и вызов инструментов у них немного хуже, MGSM на 2–5 п.п. хуже. MMLU у маленьких лучше (+11 и +7), у 8B нет. На Belebele у 8B с грамматикой −10, в свободном режиме −1.
- Vistral-24B. Знания и извлечение на уровне Mistral-Small-3.2: 19 из 24 ответов JSON побуквенно совпали. Вызов инструментов сломан: через
--jinjaс шаблоном из GGUF модель отвечает текстом и ни разу не вызывает инструмент. - Ограничение сравнения. GGUF Вихря взяты из репозиториев Vikhrmodels, базовые модели — от Qwen, unsloth (Qwen3-1.7B, Mistral-Small). Номинально квантизация одна (Q4_K_M), но детали квантования (imatrix) могут отличаться.
Ограничения
- Синтетические документы однотипны внутри каждого вида (4 на вид). Точность полей отражает, как модель обращается с такими ловушками, а не со всеми возможными документами.
- Команды HA проверены на одном шаге. Инструменты не выполнялись, и модель не видела их результата.
- Выборки маленькие: 100 вопросов Belebele и MMLU, 40 задач MGSM. Разница меньше ~5 п.п. на 100 вопросах и ~10 п.п. на MGSM в пределах шума.
- Рассуждение выключено. gpt-oss рассуждает всегда (
reasoning_effort: low), у остальных с рассуждением ответы были бы точнее, но медленнее.
Заметки для фреймворка
--cache-ram 0. С кэшем промптов в RAM, который включён по умолчанию (8 GiB), каждый запрос с другим промптом на этом iGPU получал ~10 с лишней задержки: сервер считал 1–3 с, клиент ждал 11–13 с. С--cache-ram 0задержка равна времени сервера. Переиспользование префикса внутри слота работает и без него: вторая команда HA с тем же системным промптом обработала 2062 из 2082 токенов из кэша.- Gemma 4 на этой сборке (11718) и Intel iGPU.
- Без
--swa-full: ответы — мусор («de de») или пустые, а запросы с грамматикой вешают GPU. В журнале ядра «i915: context reset due to GPU hang», в llama-serverErrorDeviceLost, после чего сервер падает. Уменьшение-ubне помогает. - С
--swa-full: первый запрос нормальный, но после переиспользования длинного кэшированного префикса все следующие ответы пустые (модель сразу выдаёт конец ответа). Помогает толькоcache_prompt: falseв каждом запросе, ценой полной обработки промпта каждый раз. - В
llm-bench(build 11706, без--cache-ram 0и без грамматики) Gemma отвечала нормально. Что именно ломается здесь — сборка или сочетание флагов, — не выяснял; стоит проверить на более новой сборке.
- Без
- У LFM2.5 и gpt-oss нельзя ограничить ответ грамматикой
root ::= [ABCD]. Сервер отвечает HTTP 500: у LFM «Unexpected empty grammar», у gpt-oss ответ не совпадает с грамматикой из-за канала рассуждения.json_schemaу них при этом работает. - llama.cpp молча пропускает
\dвpattern. В логе «unsupported escape: \d, accepting any string», то есть грамматика такое поле не проверяет. В схемах для llama.cpp нужно писать[0-9], а ответ всё равно валидировать в коде.