Как я заставил 9B обгонять 30B: три месяца с local LLM агентом. LM Studio.. LM Studio. Node.JS.. LM Studio. Node.JS. slm.. LM Studio. Node.JS. slm. агентный CLI.. LM Studio. Node.JS. slm. агентный CLI. локальные llm.. LM Studio. Node.JS. slm. агентный CLI. локальные llm. локальный агент.. LM Studio. Node.JS. slm. агентный CLI. локальные llm. локальный агент. управление контекстом.. LM Studio. Node.JS. slm. агентный CLI. локальные llm. локальный агент. управление контекстом. эмбеддинги.

Я дал qwen3.5-9B (8-bit) и qwen3-coder-30B (iq2_xxs) одну задачу — исправить падающие тесты в Python-проекте. 9B справился за 3 шага. 30B сделал 24 шага, потерял нить, повторил одни и те же вызовы инструментов и вернул уверенный неправильный ответ.

У 30B больше параметров. Он проиграл.

Причина не в модели — в harness’е. Три месяца я строил агентный CLI для локальных LLM и разбирался, почему маленькая модель с правильным окружением стабильно бьёт большую без него. Вот что нашёл.

Типичный сценарий провала

Задача: исправить падающие тесты в репозитории.

Модель читает тест. Читает исходник. Придумывает фикс — и возвращает ответ. Уверенно. С объяснением. Тесты при этом всё ещё красные.

Модель не запустила тесты. Она не могла — у неё не было механизма проверить себя. Она просто сгенерировала правдоподобный ответ и остановилась.

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

Что такое harness и почему он важнее модели

Когда запускаешь агент — Claude Code, Cursor, любой другой — большая часть работы происходит не внутри модели. Harness решает: какие файлы показать, запустить ли тесты, что помнить между сессиями, как не потерять нить на длинной задаче.

Умная модель в плохом harness’е работает хуже, чем средняя модель в хорошем. Это контринтуитивно — мы привыкли думать что качество = размер. Но на практике разрыв между “9B не справляется” и “9B справился за 3 шага” — это не веса, это инфраструктура вокруг них.

Я написал lema — опенсорсный агентный CLI для локальных LLM. Расскажу что внутри и какие решения оказались нетривиальными.

Цикл верификации: модель не должна верить себе на слово

Главная идея простая: когда модель говорит “я сделал” — harness не верит ей. Он запускает тесты сам.

Если тесты красные — вывод об ошибках уходит обратно в контекст, и модель продолжает работу. Если зелёные — принимаем результат. Модель физически не может сказать “готово” пока тесты не пройдут.

Это меняет всё. Модель перестаёт гадать и начинает знать. Вместо “кажется правильно” — “проверил, работает”. Одна эта штука убрала большую часть ошибок в тестах.

Небольшая деталь из практики: если модель сама запускает тесты через bash — harness это видит и учитывает при своей проверке. Двойного запуска не происходит. Но и ложного “зелёного” тоже.

Память: чтобы не начинать с нуля каждый раз

Когда задача завершается циклом провал → успех (тесты были красными, потом стали зелёными), harness сохраняет урок. Не весь транскрипт — короткую выжимку: что за задача, какая команда упала, что помогло.

На следующей похожей задаче уроки достаются через поиск по эмбеддингам и ложатся в контекст перед началом работы. Модель читает их как подсказки от себя прошлой.

DRY-принцип, но на уровне агентских сессий. Модель не изобретает одно и то же решение дважды.

Есть и ручной режим: /remember в TUI позволяет сохранить что угодно — соглашения проекта, особенности API, что-то что модель должна помнить всегда.

Контекст: маскировка дешевле суммаризации

Длинные задачи переполняют контекстное окно. Большинство агентов в этот момент либо крашатся, либо начинают галлюцинировать.

Классический ответ — попросить модель написать резюме разговора. Проблема в двух частях: это дополнительный LLM-вызов (медленно), и маленькая модель пишет плохие резюме — дропает детали, которые понадобятся на следующем шаге.

Исследование JetBrains Research (arXiv 2508.21433) показало: маскировка на 52% дешевле суммаризации и при этом точнее. Идея: не удалять старые сообщения, а заменять тяжёлые выводы инструментов коротким плейсхолдером — “вывод скрыт, файл такой-то, 487 строк”. Рассуждения модели и сами вызовы инструментов остаются нетронутыми. Агент знает что делал и может перечитать нужный файл если нужно.

lema маскирует первым. Суммаризация включается только если маскировки не хватило — и тогда это делает дешёвая вспомогательная модель, не основная.

Инструменты: называй как модель ожидает

Самая незаметная причина провалов SLM — schema misalignment. Модель галлюцинирует правдоподобное название инструмента вместо реального, потому что видела похожее в pretraining.

Исследование PA-Tool (arXiv 2510.07248) измерило это: одно только переименование инструментов под pretraining-конвенции даёт +17% точности и минус 80% ошибок несоответствия. Без изменения модели, без тюнинга.

Поэтому в lema инструменты называются максимально скучно и предсказуемо: read_file, write_file, edit_file, grep, glob, bash. Никаких умных имён — только те, которые модель видела тысячи раз.

Второй принцип — не больше 7±2 инструментов в контексте. Точность выбора деградирует после десяти. Когда нужно больше — dynamic retrieval, показываем только релевантные задаче.

Третий — минимальный вывод. read_file отдаёт запрошенный диапазон, не весь файл. grep — строки с контекстом, не весь файл. Меньше шум = больше места для рассуждений.

Effort dial: для SLM overthinking опаснее, чем underthinking

Когда модель плохо справляется — инстинкт говорит “дай ей больше думать”. Больше токенов на рассуждение, больше шагов. Это ошибка.

Research 2026 (arXiv 2604.10739, 2507.14417) документирует inverse scaling: при росте thinking-бюджета точность SLM сначала растёт, потом падает. Модель начинает сомневаться в правильных ответах, переусложнять простые задачи, топтаться на одном месте.

Поэтому effort в lema — это не “думай больше”. Это пресет конкретных параметров: сколько шагов, сколько токенов, какой тон инструкции. Четыре уровня: low (быстро, кратко), medium (дефолт), high (планируй, проверяй инструментами), ultra (максимум шагов для цикла верификации).

Ключевой нюанс в ultra: он даёт в три раза больше шагов, но не в три раза больше токенов на мышление. Больше tool actions, не больше thinking. Для SLM запустить тесты ещё раз эффективнее, чем думать о них дольше.

auto выбирает уровень по задаче без LLM-вызова: длинный фикс или рефакторинг → high, короткий вопрос → low, иначе medium.

AGENTS.md: правила, которые не забываются

Стандартная проблема: кладёшь правила в system prompt, модель соблюдает их первые несколько шагов — потом забывает.

Это не баг модели. LLM хорошо помнит начало и конец контекста, слепнет к середине. На длинной задаче правила уходят именно туда.

lema решает это re-injection’ом: правила инжектируются дважды — в начало контекста и в конец перед каждым вызовом модели. В конце — конденсированная версия, только ключевые строки. Конденсация детерминированная, без LLM-вызова.

Поддерживаются AGENTS.md (open standard, принят OpenAI, Google, Cursor, Aider, Gemini CLI), CLAUDE.md и .lema/rules.md. Кладёшь файл в корень проекта — lema читает автоматически.

Баг, который было приятно найти

При тестировании qwen3.5-9b модель возвращала пустой ответ при явно ненулевом числе completion-токенов. Выглядело как зависание.

Прямой curl к LM Studio показал: с thinking-моделями сервер возвращает content: "" и кладёт всё мышление в отдельное поле reasoning_content. Наш тип сообщения это поле не знал — оно дропалось при разборе ответа.

Фикс несложный: добавить поле в тип, а если content пустой при непустом reasoning_content — запросить финальный ответ явно. И важная деталь: reasoning_content не должен попадать в историю контекста — иначе thinking-токены начнут накапливаться и жрать окно на каждом следующем шаге.

Классический случай когда баг объясняет поведение, которое иначе выглядит как “модель просто плохая”.

Сравнение: 9B vs 30B на одних задачах

Тестировал на pomodoro-таймере на Python (~4 файла, реальные тесты):

Модель

Квант

Шагов на задачу

Результат

qwen3-coder-30B

iq2_xxs

20-26

средний, часто false success

qwen3.5-9B

8-bit

2-4

высокий, верифицированный

30B технически мощнее. Но агрессивный квант (iq2_xxs — это очень низко) убивает качество рассуждений до такой степени, что модель теряет нить, повторяет вызовы инструментов и не замечает противоречий в своих действиях.

9B с нормальным квантом и harness’ом: читает файл → делает правку → верификация зелёная → готово.

Вывод простой: качество кванта важнее размера модели, когда harness делает свою работу.

Итог

Три месяца экспериментов свелись к одному выводу: локальные модели не дурнее, чем кажутся. Дурнее окружение.

Разница между “9B не справляется” и “9B справился за 3 шага” — это не размер весов. Это наличие цикла обратной связи, правильных имён у инструментов и памяти между сессиями.

Что удивило больше всего:

  • Переименование инструментов дало заметный прирост без изменения промптов

  • Маскировка работает лучше суммаризации — и это контринтуитивно

  • Inverse scaling реален: “думай дольше” иногда ухудшает результат

  • 9B с 8-bit стабильно бьёт 30B с iq2_xxs при правильном harness’е

Честные ограничения: большие кодовые базы (500+ файлов), задачи без явного verifier, всё что требует глобального понимания архитектуры — пока не его история.

lema — опенсорсный проект, MIT, TypeScript, zero runtime deps. Нужен LM Studio с загруженной моделью:

npm install -g @iivgll4/lema
lema ping
lema "fix the failing tests"

Исходники: github.com/iivgll/lema

Если строили что-то похожее или пробовали другие подходы к верификации — интересно сравнить в комментариях.

Автор: ivgl

Источник