# Local LLMs on 16 GB VRAM: Gemma 4 vs Qwen 3.6 vs Qwen Coder

# Local LLMs on 16 GB VRAM: Gemma 4 vs Qwen 3.6 vs Qwen Coder

**Author:** Вячеслав (Vyacheslav)
**URL:** https://habr.com/ru/articles/1033808/
**Language:** Russian
**Hardware:** RTX 5070 Ti 16 GB, CachyOS, 32 GB RAM
**Stack:** llama.cpp (built from source for Blackwell) + OpenCode

---

> Gemma 4 обыграла Qwen Coder в задачах программирования, а режим мышления заставил модели хуже следовать инструкциям. Рассказываю почему.

### Зачем я это затеял

Привет, меня зовут Вячеслав. Я интересуюсь локальными LLM и тем, как они ведут себя в реальных задачах — не на синтетических бенчмарках, а когда нужно написать работающий код, отрефакторить файл с багами или вытащить данные из HTML.

Вокруг локальных моделей сложилась странная ситуация. С одной стороны, их постоянно принижают: если это не последняя версия Opus с максимальным режимом размышления, то и пробовать не стоит. С другой - мало кто действительно разбирается, что стоит за запуском локальной модели. Поднять API через llama.cpp - это полдела. А вот как ты её запускаешь, в какой среде, с какими параметрами - эти вещи порой переворачивают результат с ног на голову. Получить плохой результат с локальной моделью на удивление легко. Получить хороший - надо попотеть.

При этом локальные модели нужны. Особенно когда начинаются истории про чувствительные данные, закрытые контуры и ситуации, когда облачный API просто не вариант.

Я посмотрел множество тестов на YouTube - ни один меня не устроил. Общая канва одинаковая: берут модель побольше, запускают без оглядки на оптимальность и дают задание уровня «напиши сортировку пузырьком». Серьёзно?

Я не разработчик и не кодер по профессии, но решил пойти другим путём. Тесты с подковыркой, реальная агентская среда, подбор параметров. И модели я выбрал не «чем больше, тем лучше», а те, которые реально влезают в 16 ГБ видеопамяти домашней видеокарты. Что из этого вышло - дальше по тексту.

### Железо

Цель была простая: что может среднестатистический человек с обычным игровым ПК?

У меня это:

- **GPU:** RTX 5070 Ti, 16 ГБ VRAM
- **RAM:** 32 ГБ
- **OS:** Cachy OS на M.2 SSD

По меркам требований к запуску LLM - это прямо мало, «на тоненького». В этом и был основной вызов: уместить модели, которые формально не влезают в видеопамять, и при этом не потерять в скорости до неюзабельного уровня.

### Модели

Три участницы, все с архитектурой Mixture of Experts (MoE):

| Модель | Файл | Размер |
| --- | --- | --- |
| **Qwen3-Coder** | `Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf` | 17,3 ГБ |
| **Qwen 3.6** | `Qwen_Qwen3.6-35B-A3B-Q4_K_M.gguf` | 19,9 ГБ |
| **Gemma 4** | `gemma-4-26B-A4B-it-MXFP4_MOE.gguf` | 15,5 ГБ |

Почему именно MoE? Потому что эта архитектура даёт два преимущества, критичных для домашнего железа. Во-первых, одновременно активны не все слои - только часть экспертов, что снижает вычислительную нагрузку. Во-вторых, можно выгружать неактивные эксперты в оперативную память, освобождая видеопамять под контекст. Для 16 ГБ VRAM это не просто удобно \- это вообще единственная возможность запустить такие модели.

Отдельно хотелось использовать родной для Blackwell формат MXFP4 - и он принёс свои неожиданные результаты, но об этом чуть далее.

### Среда запуска

**llama.cpp**, собранная из исходников под архитектуру Blackwell. Настойчиво рекомендую этот путь вместо LM Studio или Ollama. Те дают удобство одного клика, но вы теряете до 30% скорости просто за это удобство. Когда у вас 16 ГБ VRAM и модель на грани - каждый процент на счету.

### MoE - что это и почему 26B не равно 35B в привычном смысле

Все три модели в этом тесте построены на архитектуре Mixture of Experts. Если совсем просто: внутри модели спрятано множество «экспертов» - отдельных нейросетевых блоков, каждый из которых специализируется на чём-то своём. Когда приходит запрос, специальный маршрутизатор решает, каких экспертов задействовать для конкретного токена.

В отличие от обычной «плотной» модели, где каждый запрос проходит через все слои, в MoE одновременно работают только несколько экспертов. Отсюда две важные цифры: общее количество параметров и количество *активных* параметров.

| Модель | Всего параметров | Активных | Формат записи |
| --- | --- | --- | --- |
| Gemma 4 | 26B | ~4B | 26B-A4B |
| Qwen 3.6 | 35B | ~3B | 35B-A3B |
| Qwen Coder | 30B | ~3B | 30B-A3B |

Gemma 4 при 26B общих параметров использует 4B активных - больше, чем Qwen-модели с их 3B. Но Qwen при этом имеет 35B и 30B общих весов, которые тоже нужно хранить в памяти, просто не все они участвуют в вычислениях одновременно. Поэтому сравнивать MoE-модели по общему числу параметров - занятие бессмысленное. Важнее смотреть на активные параметры и на то, как модель квантована.

### Квантование: сжимаем, но с умом

Квантование - это просто сжатие весов модели. Вместо 16-битных чисел с плавающей запятой храним 4-битные целые. Памяти нужно в разы меньше, а качество… зависит от того, *как* сжимали.

В моём наборе встретились три разных подхода, и вот тут началось интересное.

#### Q4\_K\_M - проверенная классика

Самый распространённый формат в мире GGUF. Тензоры разбиваются на блоки по 256 элементов, каждому блоку - свой масштаб и смещение. Наиболее важные тензоры (например, внимание) сохраняются в 6 битах, остальные - в 4. Не требует калибровочных данных, работает предсказуемо. Формат от автора **bartowski**, на которого я рекомендую обратить внимание.

#### UD-Q4\_K\_XL - Unsloth Dynamic 2.0

Более современный подход. Перед квантованием модель «прогоняют» через 300 тысяч - 1,5 миллионов токенов реальных диалогов, смотрят, какие слои чувствительны к потере точности, и оставляют их в 8 или 16 битах. Остальные сжимают жёстко до 4 бит. Звучит отлично - «новая технология, калибровка на реальных данных». Теоретически это должно быть лучше. Практически… не всегда.

#### MXFP4\_MOE — родной формат для Blackwell

В отличие от целочисленных форматов (INT4), сохраняет структуру числа с плавающей запятой - знак, экспоненту, мантиссу - в рамках тех же четырёх бит. Веса группируются в блоки с общей экспонентой. Главная фишка: нативная поддержка тензорными ядрами Blackwell. Никакой эмуляции, прямой маппинг инструкций на железо. Для Gemma 4 это был естественный выбор.

### Сравнение форматов

|  | Q4\_K\_M | UD-Q4\_K\_XL | MXFP4\_MOE |
| --- | --- | --- | --- |
| Тип данных | INT4/INT6 | Смешанный 4/8/16 | FP4 блочный |
| Калибровка | Не нужна | 300K–1.5M токенов | Не нужна |
| Аппаратное ускорение | Эмуляция CUDA | Эмуляция CUDA | **Нативные Tensor Cores** |
| Риск на MoE | Низкий | Умеренный | Низкий |

### Почему Qwen без MXFP4

Тут всё коротко и грустно. Команда Unsloth официально вывела MXFP4-слои из форматов Qwen GGUF (Q2\_K\_XL, Q3\_K\_XL, Q4\_K\_XL) из-за аномалий в вычислениях. MXFP4 для Qwen 3.5/3.6 оказался нестабильным - модель может выдавать некорректные ответы или просто падать. Так что выбирать пришлось из оставшихся вариантов.

### Новее ≠ лучше: перплексия не врет

Остался выбор между UD-Q4\_K\_XL и классическим Q4\_K\_M для Qwen. Документация Unsloth позиционирует Dynamic 2.0 как вершину технологии. Но метрики перплексии (PPL) на WikiText-2 рисуют другую картину:

| Формат | Размер | Перплексия | Деградация vs Q8\_0 |
| --- | --- | --- | --- |
| **Q8\_0** (эталон) | ~36,9 ГБ | 6,5342 | 0% |
| **Q4\_K\_M** | ~20,0 ГБ | 6,6688 | **+2,1%** |
| **UD-Q4\_K\_XL** | ~19,0 ГБ | 7,1702 | **+9,7%** |

Q4\_K\_M отклоняется от эталона на 2,1%. UD-Q4\_K\_XL - на 9,7%, причём ещё и экономит всего 1 ГБ на диске. Для архитектуры Qwen динамическое квантование оказалось заметно хуже статического. Похожая картина наблюдается и на Qwen3-Coder.

В профильных обсуждениях отмечают, что Unsloth-кванты исторически проседают именно на MoE-архитектурах со сложной маршрутизацией экспертов. Красивая идея, но не для всех архитектур одинаково полезная.

Итог простой: не гонитесь за «новым» и «продвинутым» форматом. Проверяйте метрики конкретно под свою модель. Иногда старое доброе работает лучше.

### Как впихнуть невпихуемое

Все три модели, даже после жёсткого квантования, не влезают целиком в 16 ГБ видеопамяти. Gemma 4 - 15,5 ГБ, но ей тоже нужно оставить гигабайт на KV-кеш, иначе контекст захлебнётся, хотя на тестах этого не произошло. Тут нам нужен offload - выгрузка части модели в системную оперативку.

В llama.cpp есть два способа это сделать, и они работают по-разному.

#### Подход первый - классический --n-gpu-layers

Ключ в явном виде указывает, сколько слоёв загрузить в видеопамять. Ставишь 999 - просишь загрузить всё, что влезет. Ставишь 45 - 45 слоёв в GPU, остальное в RAM. Подбирается экспериментально: поставил, запустил, посмотрел монитор ресурсов, повторил.

Результаты с классическим offload:

| Модель | Режим | Prompt (t/s) | Генерация (t/s) |
| --- | --- | --- | --- |
| Qwen 3.6 | Fast | 404 | 41,3 |
| Qwen 3.6 | Thinking | 326 | 41,7 |
| Qwen Coder | Fast | 565 | 51,3 |
| Qwen Coder | Thinking | 587 | 53,0 |

#### Подход второй - продвинутый --n-cpu-moe

Здесь начинается интереснее. В MoE-архитектуре видеокарта должна держать в памяти *все* общие параметры - маршрутизатор может выбрать любого эксперта в любой момент. Но вычислительная нагрузка соответствует только активным параметрам (3B или 4B). Поэтому MoE генерирует текст быстро, но требует много памяти для хранения неактивных весов.

llama.cpp позволяет с помощью `--n-cpu-moe` предписать хранить определённое количество экспертных слоёв в RAM, минимизируя нагрузку на шину PCIe. Ключ, как и `--n-gpu-layers`, подбирается опытным путём.

Результаты с продвинутым offload:

| Модель | Режим | Prompt (t/s) | Генерация (t/s) | Прирост |
| --- | --- | --- | --- | --- |
| **Gemma 4** | Thinking | 81 | **60,8** | Полный VRAM |
| **Gemma 4** | Fast | 58 | **61,9** | Полный VRAM |
| Qwen 3.6 | Fast | 391 | **64,0** | +55% |
| Qwen 3.6 | Thinking | 405 | **66,7** | +60% |
| Qwen Coder | Fast | 596 | **57,6** | +12% |
| Qwen Coder | Thinking | 688 | **60,2** | +14% |

Qwen 3.6 получила прирост 55-60% в скорости генерации. Qwen Coder - 12-14%. Gemma 4 помещается целиком в VRAM, так что ей offload не нужен - она и так работает на максимуме.

#### Почему это работает

Механизмы внимания (Attention), которые нужны для *каждого* генерируемого токена, остаются в быстрой видеопамяти для всех слоёв. В медленную шину PCIe отправляются только запросы к неактивным экспертам. Поскольку экспертов задействуется мало, трафик через шину минимальный, и видеокарта не ждёт данные из оперативки.

Результат - прирост скорости более чем на 50% без апгрейда железа. Просто за счёт того, что данные разложены правильно.

### Конфигурации запуска

Расписывать назначение каждого параметра не буду - получится справка, а не статья. Приведу итоговые команды, с которыми модели работали в тестах, и отмечу ключевые моменты.

#### Критичные параметры

Независимо от модели, эти три ключа влияют на производительность сильнее всего:

- `--flash-attn` - включает Flash Attention, ускоряет обработку внимания
- `--cache-type-k q8_0` - тип кеша для ключей, q8\_0 баланс скорости и памяти
- `--cache-type-v q8_0` - тип кеша для значений, аналогично

Без них можно вообще не начинать - потеряете ощутимую часть скорости.

#### Thinking on/off

Для включения и отключения режима мышления:

- `--reasoning on` / `--reasoning off` - основной переключатель
- `--reasoning-budget 0` - дополнительный параметр для Qwen, полностью блокирует thinking-токены

Для Qwen-моделей одного `--reasoning off` оказалось недостаточно - об этом подробнее в разделе с наблюдениями.

#### Gemma 4

```
--model "gemma-4-26B-A4B-it-MXFP4_MOE.gguf" \
--ctx-size 32768 \
--n-gpu-layers 999 \
--n-cpu-moe 8 \
--flash-attn 1 \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
--batch-size 512 \
--ubatch-size 512 \
--reasoning off (или on) \
--jinja \
--temp 0.7 \
--top-p 0.8 \
--top-k 20 \
--min-p 0.0 \
--presence-penalty 0.0 \
--repeat-penalty 1.0 \
--no-mmap
```

Gemma помещается целиком в VRAM, поэтому `--n-gpu-layers 999`. Параметр `--presence-penalty 0.0` - в отличие от Qwen, ей не нужно подавлять повторения.

#### Qwen Coder

```
--model "Qwen3-Coder-30B-A3B-Instruct-Q4_K_M.gguf" \
--ctx-size 32768 \
--n-gpu-layers 99 \
--n-cpu-moe 17 \
--flash-attn 1 \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
--batch-size 4096 \
--ubatch-size 1024 \
--reasoning off (или on) \
--reasoning-budget 0 \
--jinja \
--temp 0.7 \
--top-p 0.8 \
--top-k 20 \
--min-p 0.0 \
--presence-penalty 1.5 \
--repeat-penalty 1.0 \
--parallel 2 \
--no-mmap
```

Отличия от Gemma: `--batch-size 4096` (больше для эффективного процессинга промпта), `--presence-penalty 1.5` (подавляет зацикливание), `--parallel 2` (параллельные запросы).

#### Qwen 3.6

```
--model "Qwen_Qwen3.6-35B-A3B-Q4_K_M.gguf" \
--ctx-size 32768 \
--n-gpu-layers 99 \
--n-cpu-moe 17 \
--flash-attn 1 \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
--batch-size 4096 \
--ubatch-size 1024 \
--reasoning off (или on) \
--reasoning-budget 0 \
--jinja \
--temp 0.7 \
--top-p 0.8 \
--top-k 20 \
--min-p 0.0 \
--presence-penalty 1.5 \
--repeat-penalty 1.0 \
--parallel 2 \
--no-mmap
```

Идентична конфигурации Qwen Coder - та же архитектура, те же требования к памяти. Обе используют `--n-gpu-layers 99` и `--n-cpu-moe 17` для оптимального распределения между VRAM и RAM.

### Почему среда - это половина дела

Одна из главных ошибок при тестировании локальных моделей - не обращать внимания на то, *где* и *как* модель работает. Поднять сервер llama.cpp с API - полдела. А куда вы его подключите и как оболочка будет общаться с моделью, как вызывать инструменты, какой системный промпт зашит - разница может быть катастрофической. От полной неработоспособности до «а что, так можно было?».

Из рабочих вариантов - плагины для VS Code вроде Kilo Kode, Cline, Roo Code, отдельно стоящий OpenCode, ZED и другие IDE. Я выбрал OpenCode как самую стабильную и простую в настройке.

При работе с агентами в терминале критически важна способность модели вызывать инструменты (Tool Calling), понимать структуру проекта и следовать заданным правилам. OpenCode использует файл [`AGENTS.md`](http://agents.md/) (аналог Cursor Rules) для передачи контекста проекта. А для корректного вызова функций через llama.cpp обязательно нужен флаг `--jinja` - без него модель просто не увидит инструменты.

Тестовые задания были с подковыркой - создавал их не сам, а с помощью Claude, он же помог с ведением протокола.

### Модели и режимы

| Модель | Активные параметры | Режимы |
| --- | --- | --- |
| **Gemma 4 26B-A4B-it** | ~4B | Thinking / Fast |
| **Qwen 3.6 35B-A3B** | ~3B | Thinking / Fast |
| **Qwen3-Coder 30B-A3B** | ~3B | Thinking / Fast |

Все модели запускались на одном железе через llama.cpp. Fast-режим для Qwen-моделей потребовал тройной блокировки: `--reasoning off` + `--reasoning-budget 0` в скрипте запуска и `enable_thinking: false` в конфиге OpenCode. Просто `--reasoning off` оказалось недостаточно - модель всё равно думала.

### Тесты

#### Тест 1 - Следование правилам AGENTS.md

В корне проекта лежал [`AGENTS.md`](http://agents.md/) с нестандартными требованиями: использовать префикс `ai_gen_` для имён файлов, строгую типизацию и комментарий `// Одобрено ИИ`. Задача — написать функцию извлечения уникальных email-адресов из текста.

Кажется, простая задача. Но префикс `ai_gen_` - это то, чему модель должна научиться *из контекста*, а не из общего знания.

#### Тест 2 - Tool Calling

Прочитать файл `index.html`, извлечь все `<h1>` -теги, создать `headers.json` с массивом заголовков.

Здесь проверяется не столько способность писать код, сколько умение модели последовательно использовать инструменты: прочитать файл, распарсить контент, записать результат. Модель, которая застрянет в цикле попыток чтения или запишет неверный JSON, провалит тест.

#### Тест 3 - Рефакторинг и Unit-тесты

Дан файл с функцией, в которой спрятано 5 багов:

- off-by-one ошибка
- два деления на ноль
- возврат одного элемента вместо списка
- нечитаемые имена переменных

Нужно отрефакторить код и написать исчерпывающие unit-тесты. Это самый сложный тест - многошаговый, требующий и понимания кода, и способности писать работающие тесты.

### Шкала оценки

Каждый тест оценивался по 4 критериям, по 1 баллу за каждый. Максимум - 4 балла за тест, 12 баллов за всё тестирование. Ничего субъективного: тест либо проходит критерий, либо нет.

### Итоговые результаты

Шесть прогонов - три модели, два режима каждая. Сухие цифры:

| Модель | Режим | Тест 1 | Тест 2 | Тест 3 | **Итого** |
| --- | --- | --- | --- | --- | --- |
| Gemma 4 26B | Thinking | 3/4 | 4/4 | 4/4 | **11/12** |
| Gemma 4 26B | Fast | 4/4 | 4/4 | 4/4 | **12/12** |
| Qwen 3.6 35B | Thinking | 1/4 | 4/4 | 2/4 | **7/12** |
| Qwen 3.6 35B | Fast | 3/4 | 4/4 | 2/4 | **9/12** |
| Qwen Coder 30B | Thinking | 2/4 | 4/4 | 2/4 | **8/12** |
| Qwen Coder 30B | Fast | 2/4 | 2/4 | 2/4 | **6/12** |

Gemma 4 - единственная модель, набравшая максимум. Qwen Coder - единственный случай, когда Thinking-режим оказался лучше Fast. Но обо всём по порядку.

### Тест 1: Следование правилам AGENTS.md

| Критерий | Gemma T | Gemma F | Qwen 3.6 T | Qwen 3.6 F | Qwen Coder T | Qwen Coder F |
| --- | --- | --- | --- | --- | --- | --- |
| Функция без логических ошибок | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Префикс `ai_gen_` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Строгая типизация | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ |
| Комментарий `// Одобрено ИИ` | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| **Итог** | **3/4** | **4/4** | **1/4** | **3/4** | **2/4** | **2/4** |

Gemma в Fast-режиме - единственная, кто выполнил все четыре требования. Qwen 3.6 в Thinking-режиме - худший результат: проигнорировала и префикс, и типизацию, и комментарий. При этом функцию написала корректно - все модели справились с базовой задачей.

### Тест 2: Tool Calling

| Критерий | Gemma T | Gemma F | Qwen 3.6 T | Qwen 3.6 F | Qwen Coder T | Qwen Coder F |
| --- | --- | --- | --- | --- | --- | --- |
| Вызов инструмента чтения | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Все 4 тега `<h1>` извлечены | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Корректный `headers.json` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| Нет зависания в цикле | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **Итог** | **4/4** | **4/4** | **4/4** | **4/4** | **4/4** | **2/4** |

Пять из шести прогонов - идеальный результат. Единственный провал: Qwen Coder в Fast-режиме не извлекла все теги и записала некорректный JSON. Причина - не декодировала HTML-entity `&amp;` в `&`, записав в файл `"Support &amp; Contact"` вместо `"Support & Contact"`. Модель скопировала сырой текст разметки, не интерпретировав его как контент.

### Тест 3: Рефакторинг и Unit-тесты

| Критерий | Gemma T | Gemma F | Qwen 3.6 T | Qwen 3.6 F | Qwen Coder T | Qwen Coder F |
| --- | --- | --- | --- | --- | --- | --- |
| Код стал читабельнее | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Найдено ≥3 из 5 багов | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Тесты корректны и запускаются | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| Нет лишнего boilerplate | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ |
| **Итог** | **4/4** | **4/4** | **2/4** | **2/4** | **2/4** | **2/4** |

Здесь разрыв между Gemma и Qwen-моделями максимальный. Все три модели нашли баги и отрефакторили код. Но написать корректные unit-тесты смогла только Gemma - обе Qwen-модели выдали тесты, которые не запускались или содержали ошибки. Плюс Qwen-модели нагенерировали лишнего boilerplate, который только мешал.

### Анализ и неочевидные находки

#### Gemma 4 - неожиданный лидер

Честно говоря, до тестов я не воспринимал Gemma всерьёз для задач программирования. Казалось, что ей больше подойдут текстовые задачи - суммаризация, перевод, что-то творческое. Но 12/12 в Fast-режиме и 11/12 в Thinking говорят другое.

Особенно впечатляет Тест 3. Gemma - единственная из всех моделей, которая стабильно писала корректные unit-тесты с правильной математикой. При этом у неё больше активных параметров (4B против 3B у Qwen), но меньше общих весов (26B против 30-35B). То есть Google сделали более эффективную архитектуру, а не просто «побольше экспертов».

Единственная слабость: в Thinking-режиме она пропустила префикс `ai_gen_` из [AGENTS.md](http://agents.md/). При повторном контрольном прогоне результат воспроизвёлся - это не случайность, а системное поведение.

#### Парадокс Thinking-режима

Самый интересный результат тестирования: **Thinking-режим во всех моделях показал худшее следование правилам**, чем Fast.

| Модель | Тест 1 Thinking | Тест 1 Fast | Разница |
| --- | --- | --- | --- |
| Gemma 4 | 3/4 | 4/4 | +1 в пользу Fast |
| Qwen 3.6 | 1/4 | 3/4 | **+2 в пользу Fast** |
| Qwen Coder | 2/4 | 2/4 | Без разницы |

Qwen 3.6 в Thinking-режиме - худший результат среди всех прогонов, 1/4. Модель проигнорировала три из четырёх правил [AGENTS.md](http://agents.md/).

Вероятное объяснение: когда модель «думает», она оценивает полученные инструкции и принимает решение, что нестандартные правила вроде искусственного префикса избыточны. Fast-режим следует буквально, без рефлексии. Для практического использования это важно: **если нужно точное следование правилам стиля - Fast-режим надёжнее**.

#### Qwen-модели и проблема длинных сессий

Обе Qwen-модели провалили Тест 3 по одной и той же причине. Типичный сценарий:

1. Модель читает файл, начинает рефакторинг
2. Попытка записи большого файла вызывает ошибку инструмента
3. Контекст сжимается, модель «забывает» что делала
4. Начинает заново или останавливается с вопросом к пользователю

Это системная проблема, не связанная с режимом thinking. **Qwen-модели текущих версий плохо справляются с длинными агентскими сессиями** в OpenCode при работе с большими файлами.

Ещё один артефакт: обе Qwen-модели создавали новый файл `refactored_` [`code.py`](http://code.py/) вместо редактирования существующего `buggy_` [`code.py`](http://code.py/). Qwen 3.6 в Thinking-режиме всё же отредактировала оригинал - видимо, дополнительное «мышление» помогло лучше понять контекст задачи.

#### Единственный провал Tool Calling

Qwen Coder в Fast-режиме - единственная модель, не справившаяся с Тестом 2. Причина интересная: она не декодировала HTML-entity `&amp;` в `&`. В файле `index.html` был заголовок «Support & Contact», который в разметке записан как `Support &amp; Contact`. Модель прочитала файл, увидела сырой текст и записала в JSON именно `&amp;` — технически правильно, но бессмысленно по сути.

Все остальные модели, включая Qwen Coder в Thinking-режиме, корректно интерпретировали HTML и записали `&`. Разница между режимами в одном символе, но она стоила двух баллов.

#### Qwen 3.6 vs Qwen Coder - универсальная победила специализированную

Специализированная модель для кода прошлого поколения не превзошла универсальную текущего:

|  | Qwen 3.6 | Qwen Coder |
| --- | --- | --- |
| Thinking | 7/12 | 8/12 |
| **Fast** | **9/12** | 6/12 |
| **Среднее** | **8/12** | 7/12 |

Qwen 3.6 выигрывает в Fast-режиме с отрывом в три балла. Qwen Coder немного лучше в Thinking, но разница минимальна и нивелируется тем, что Coder провалил Tool Calling в Fast - это странно для «кодерской» модели, которая должна уметь работать с файлами.

Раньше я использовал Qwen Coder для небольших задач под Windows и она отлично справлялась. Возможно, здесь она пострадала от неоптимального запуска, а возможно - специализированные модели просто не так универсальны, как кажется.

### Рекомендации по применению

Если коротко - что брать для каких задач:

| Задача | Рекомендация |
| --- | --- |
| Агентская разработка в IDE (OpenCode, Cursor) | **Gemma 4 Fast** - лучший баланс скорости, точности и следования правилам |
| Сложный рефакторинг с тестами | **Gemma 4 Thinking** - единственная надёжно пишет корректные тесты |
| Быстрые правки и короткие скрипты | **Gemma 4 Fast** или **Qwen 3.6 Fast** |
| Строгое следование стайлгайду | Любая модель в **Fast-режиме** - Thinking обходит правила |
| Длинные многошаговые сессии | Избегайте Qwen-моделей на текущих версиях llama.cpp |

### Технические наблюдения, которые стоит запомнить

**Тройная блокировка thinking в Qwen 3.** Флаг `--reasoning off` в llama.cpp оказался недостаточным при работе через OpenCode. Надёжное решение — комбинация трёх мер: `--reasoning off` + `--reasoning-budget 0` на уровне сервера и `enable_thinking: false` через `extra_` [`body.chat`](http://body.chat/) `_template_kwargs` в конфиге OpenCode. Если выставить не всё - модель будет думать, даже когда вы этого не хотите.

Индикатор «Thinking…» в интерфейсе OpenCode - это UI-статус обработки запроса, а не признак генерации thinking-токенов. Не пугайтесь, это нормально.

**Галлюцинация о собственных действиях.** Qwen 3.6 Fast в финальном резюме Теста 3 сообщила, что добавила `ai_gen_` префикс - которого в коде не было. Небольшой, но важный сигнал для production: выходной текст модели не является надёжным отчётом о реально выполненных действиях. Проверяйте код, а не слова модели.

### Итого

Локальные модели, даже сильно ужатые для запуска на бытовом железе, вполне жизнеспособны. Нет, они не заменят тяжёлые API от лидеров. Но значительную долю бытовых задач решают без проблем: написать скрипт, обработать табличку, перебросить данные из одного API в другое, сделать рефакторинг.

Плата за это - порог входа. Нужно попотеть, чтобы подобрать всё: конкретный квант (не все одинаково хороши), среду запуска, оболочку для работы, параметры. Особенно если нужна приемлемая скорость. Все «лентяйки» вроде LM Studio или Ollama облегчают запуск, но срезают углы - вы жертвуете скоростью. На моём железе это были десятки токенов в секунду.

OpenCode могу рекомендовать отдельно. Помимо возможности запуска локальных моделей, у него есть встроенный набор (ZEN) протестированных моделей по очень гуманной подписке, включая бесплатные. Одна из них, кстати, помогала мне приводить в порядок этот текст.

Отдельное предупреждение: будьте осторожнее с модными скилами вроде Context 7 или Superpowers. Они отлично работают на больших моделях и отлично забивают контекст на маленьких. Не подсовывайте их бездумно в каждый проект. А вот caveman вполне будет в кассу.

Самая слабая сторона маленьких локальных моделей - контекстное окно. Чем больше вы в него поместите, а тут надо не забывать про системный промпт от оболочки, скилы и особенно MCP, тем быстрее модель начнёт не справляться. Нужно всегда помнить об ограниченности ресурсов. Такой подход, кстати, и большие модели убивает - просто у них запас побольше.

Тема запуска LLM локалок, особенно в условиях ограниченности ресурсов, очень широкая, буду рад увидеть в комментариях ваш опыт или мои ошибки в изысканиях.

### Дополнение: расширяем контекстное окно до максимума (после прочтения комментариев)

После прочтения комментариев захотелось разобраться с ещё одной практической проблемой: насколько большой контекст реально можно выжать из тех же моделей на тех же 16 ГБ.

По умолчанию во всех скриптах стоял `--ctx-size 32768`. Это 32К токенов - не так мало, но для агентской работы с большими кодовыми базами или длинными документами хочется больше. Модели в своих метаданных заявляют поддержку 262 144 токенов (256К). Разрыв между «заявлено» и «влезает в 16 ГБ» надо было как-то перекрыть.

#### Почему контекст съедает много памяти

Каждый токен в контексте требует места под KV-кэш - хранилище промежуточных результатов для механизма внимания. Размер этого кэша зависит от архитектуры модели:

```
KV на токен = 2 × кол-во_KV-голов × размер_головы × кол-во_слоёв × байт/элемент
```

У наших моделей цифры получаются очень разные:

| Модель | KV-головы | Размер головы | Слоёв | q4\_0 (0,5 байт) |
| --- | --- | --- | --- | --- |
| Qwen3.6-35B | 2 | 128 | 40 | ~10 КБ/токен |
| Gemma4-26B | ~7 в среднем\* | 176 | 30 | ~36 КБ/токен |

\*У Gemma4 нестандартная схема: большинство слоёв имеют 8 KV-голов, каждый шестой - только 2. Итого 210 голов суммарно по 30 слоям.

Gemma4 обходится почти в 4 раза дороже по памяти на каждый токен контекста. Это важно держать в голове.

#### Что пошло в жертву: уменьшил формат KV-кэша

Исходные скрипты использовали `--cache-type-k q8_0 --cache-type-v q8_0` — 1 байт на элемент. Переключение на `q4_0` (0,5 байта на элемент) ровно вдвое снижает стоимость кэша без заметной потери качества на практике.

Это сразу даёт эффект: если переключить с q8\_0 на q4\_0 и одновременно удвоить контекст - потребление VRAM останется примерно таким же. Бесплатный апгрейд с 32К до 65К токенов.

Дальше подключается уже знакомый `--n-cpu-moe`: чем больше экспертов уходит в оперативку, тем больше VRAM освобождается под KV-кэш для расширенного контекста.

#### Методология подбора

Алгоритм простой и повторялся для каждого шага:

1. Запустить сервер с новыми параметрами
2. Записать потребление VRAM в простое
3. Отправить тестовый запрос с большим входом (несколько тысяч строк исходников llama.cpp)
4. Зафиксировать пиковое потребление через nvtop
5. Если пик > 15,3 ГБ - поднять `--n-cpu-moe`, повторить
6. Если пик < 14,0 ГБ - снизить `--n-cpu-moe`, повторить

Целевой ориентир: ~1-1,5 ГБ свободно при пиковой нагрузке.

#### Результаты: Qwen3.6-35B

Стартовал с 14,8 ГБ в простое при 32К контексте (q8\_0). Финальные показатели:

| Контекст | n-cpu-moe | Простой | Пик при ~238К токенов |
| --- | --- | --- | --- |
| 32К (исходный, q8\_0) | 17 | 14,8 ГБ | — |
| 128К (q4\_0) | 20 | 14,0 ГБ | 14,1 ГБ |
| **256К (q4\_0)** | **23** | **14,1 ГБ** | **14,7 ГБ** |

256К токенов - это максимум по метаданным модели, и он реально влез. Тест с 238К токенов на входе прошёл без проблем, пик VRAM остался в комфортных пределах.

#### Результаты: Gemma4-26B

Здесь интереснее: из-за более дорогого KV-кэша каждый шаг требует чуть большего n-cpu-moe.

| Контекст | n-cpu-moe | Простой | Пик при ~95К токенов |
| --- | --- | --- | --- |
| 32К (исходный, q8\_0) | 8 | 14,8 ГБ | — |
| 65К (q4\_0) | 8 | 14,6 ГБ | 14,8 ГБ |
| 98К (q4\_0) | 9 | 14,3 ГБ | 14,7 ГБ |
| 131К (q4\_0) | 10 | 14,1 ГБ | 14,5 ГБ |
| **262К (q4\_0)** | **12** | **14,4 ГБ** | **15,3 ГБ** |

При 262К тест с 245К токенов на входе прошёл, пик 15,3 ГБ - запас есть, но небольшой. Попытка снизить n-cpu-moe до 11 при этом контексте приводила к OOM.

#### Про скорость

Расплата за большой контекст — скорость генерации немного падает с ростом n-cpu-moe, потому что больше экспертов обрабатывается через PCIe. Для Gemma4 разница между n-cpu-moe=8 и n-cpu-moe=12 на практике незначительная - модель и так была compute-bound. Для Qwen3.6 снижение заметнее, но в контексте «256К vs 32К» - это разумный обмен.

#### Итог

Заявленные 262К токенов контекста на 16 ГБ карте оба максимума реально достижимы. Главное - переключиться с q8\_0 на q4\_0 для KV-кэша и правильно откалибровать n-cpu-moe. Для Qwen3.6 это стоит 6 дополнительных единиц n-cpu-moe по сравнению с базовой конфигурацией, для Gemma4 - 4 единицы.

Конфигурации для всех вариантов контекста которые я использовал:

```
# Gemma4-26B — быстрые ответы, 262K контекст (максимум модели)
# VRAM: idle 14.38 GB, пик при 245K токенов 15.31 GB
# Скорость: prompt 1100 t/s, generation 30.94 t/s

  --ctx-size 262144 \
  --n-gpu-layers 999 \
  --n-cpu-moe 12 \
  --flash-attn 1 \
  --cache-type-k q4_0 \
  --cache-type-v q4_0 \
  --batch-size 512 \
  --ubatch-size 512 \
  --reasoning off \
  --jinja \
  --temp 0.7 \
  --top-p 0.8 \
  --top-k 20 \
  --min-p 0.0 \
  --presence-penalty 0.0 \
  --repeat-penalty 1.0 \
  --no-mmap
```
```
# Qwen3.6-35B — быстрые ответы, 256K контекст (максимум модели)
# VRAM: idle 14.1 GB, пик при 238K токенов 14.7 GB
# Скорость: быстрее Gemma4 на коротких задачах

  --ctx-size 262144 \
  --n-gpu-layers 999 \
  --n-cpu-moe 12 \
  --flash-attn 1 \
  --cache-type-k q4_0 \
  --cache-type-v q4_0 \
  --batch-size 512 \
  --ubatch-size 512 \
  --reasoning off \
  --jinja \
  --temp 0.7 \
  --top-p 0.8 \
  --top-k 20 \
  --min-p 0.0 \
  --presence-penalty 0.0 \
  --repeat-penalty 1.0 \
  --no-mmap
```

Хабы:
- [Искусственный интеллект](https://habr.com/ru/hubs/artificial_intelligence/)
