- BrainTools - https://www.braintools.ru -
Я дал qwen3.5-9B (8-bit) и qwen3-coder-30B (iq2_xxs) одну задачу — исправить падающие тесты в Python-проекте. 9B справился за 3 шага. 30B сделал 24 шага, потерял нить, повторил одни и те же вызовы инструментов и вернул уверенный неправильный ответ.
У 30B больше параметров. Он проиграл.
Причина не в модели — в harness’е. Три месяца я строил агентный CLI для локальных LLM и разбирался, почему маленькая модель с правильным окружением стабильно бьёт большую без него. Вот что нашёл.
Задача: исправить падающие тесты в репозитории.
Модель читает тест. Читает исходник. Придумывает фикс — и возвращает ответ. Уверенно. С объяснением. Тесты при этом всё ещё красные.
Модель не запустила тесты. Она не могла — у неё не было механизма проверить себя. Она просто сгенерировала правдоподобный ответ и остановилась.
Это не глупость. Это архитектура. Без цикла обратной связи модель работает вслепую — как повар, которому не дают пробовать еду. Он может быть хорошим поваром. Просто блюдо с вероятностью 50% окажется пересолённым.
Когда запускаешь агент — 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 [1]) показало: маскировка на 52% дешевле суммаризации и при этом точнее. Идея: не удалять старые сообщения, а заменять тяжёлые выводы инструментов коротким плейсхолдером — “вывод скрыт, файл такой-то, 487 строк”. Рассуждения модели и сами вызовы инструментов остаются нетронутыми. Агент знает что делал и может перечитать нужный файл если нужно.
lema маскирует первым. Суммаризация включается только если маскировки не хватило — и тогда это делает дешёвая вспомогательная модель, не основная.
Самая незаметная причина провалов SLM — schema misalignment. Модель галлюцинирует правдоподобное название инструмента вместо реального, потому что видела похожее в pretraining.
Исследование PA-Tool (arXiv 2510.07248 [2]) измерило это: одно только переименование инструментов под pretraining-конвенции даёт +17% точности и минус 80% ошибок несоответствия. Без изменения модели, без тюнинга.
Поэтому в lema инструменты называются максимально скучно и предсказуемо: read_file, write_file, edit_file, grep, glob, bash. Никаких умных имён — только те, которые модель видела тысячи раз.
Второй принцип — не больше 7±2 инструментов в контексте. Точность выбора деградирует после десяти. Когда нужно больше — dynamic retrieval, показываем только релевантные задаче.
Третий — минимальный вывод. read_file отдаёт запрошенный диапазон, не весь файл. grep — строки с контекстом, не весь файл. Меньше шум = больше места для рассуждений.
Когда модель плохо справляется — инстинкт говорит “дай ей больше думать”. Больше токенов на рассуждение, больше шагов. Это ошибка [3].
Research 2026 (arXiv 2604.10739 [4], 2507.14417 [5]) документирует inverse scaling: при росте thinking-бюджета точность SLM сначала растёт, потом падает. Модель начинает сомневаться в правильных ответах, переусложнять простые задачи, топтаться на одном месте.
Поэтому effort в lema — это не “думай больше”. Это пресет конкретных параметров: сколько шагов, сколько токенов, какой тон инструкции. Четыре уровня: low (быстро, кратко), medium (дефолт), high (планируй, проверяй инструментами), ultra (максимум шагов для цикла верификации).
Ключевой нюанс в ultra: он даёт в три раза больше шагов, но не в три раза больше токенов на мышление [6]. Больше tool actions, не больше thinking. Для SLM запустить тесты ещё раз эффективнее, чем думать о них дольше.
auto выбирает уровень по задаче без LLM-вызова: длинный фикс или рефакторинг → high, короткий вопрос → low, иначе medium.
Стандартная проблема: кладёшь правила в system prompt, модель соблюдает их первые несколько шагов — потом забывает [7].
Это не баг модели. 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-токены начнут накапливаться и жрать окно на каждом следующем шаге.
Классический случай когда баг объясняет поведение [8], которое иначе выглядит как “модель просто плохая”.
Тестировал на 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 шага” — это не размер весов. Это наличие цикла обратной связи, правильных имён у инструментов и памяти [9] между сессиями.
Что удивило больше всего:
Переименование инструментов дало заметный прирост без изменения промптов
Маскировка работает лучше суммаризации — и это контринтуитивно
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 [10]
Если строили что-то похожее или пробовали другие подходы к верификации — интересно сравнить в комментариях.
Автор: ivgl
Источник [11]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/32486
URLs in this post:
[1] arXiv 2508.21433: https://arxiv.org/abs/2508.21433
[2] arXiv 2510.07248: https://arxiv.org/abs/2510.07248
[3] ошибка: http://www.braintools.ru/article/4192
[4] arXiv 2604.10739: https://arxiv.org/abs/2604.10739
[5] 2507.14417: https://arxiv.org/abs/2507.14417
[6] мышление: http://www.braintools.ru/thinking
[7] забывает: http://www.braintools.ru/article/333
[8] поведение: http://www.braintools.ru/article/9372
[9] памяти: http://www.braintools.ru/article/4140
[10] github.com/iivgll/lema: https://github.com/iivgll/lema
[11] Источник: https://habr.com/ru/articles/1054202/?utm_campaign=1054202&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.