87% на бенчмарке с моделью 4B: как это работает
Как построить coding-агент на модели 4B параметров, который превосходит на бенчмарках решения с моделями в 10× больше. Архитектура, техники, примеры.
87% на бенчмарке с моделью 4B: как это работает
Все привыкли к тому, что высокие результаты на бенчмарках по программированию — удел больших закрытых моделей: GPT-4, Claude Opus, Gemini Ultra. Но что если 4-миллиардная модель, запускаемая на ноутбуке, способна показать 87% на HumanEval? Звучит как кликбейт — но это реальная инженерная задача, которую удалось решить за счёт грамотной архитектуры агента, а не за счёт размера модели.
В этом материале — полный разбор того, как устроен такой агент: какие техники применяются, как правильно выбрать бенчмарк, и почему «маленькая» модель с умным скаффолдингом может обыграть «большую» модель в лоб.
Почему размер модели — не главное
Распространённое заблуждение: чем больше параметров, тем лучше код. На практике это верно лишь при прочих равных. Разница между 4B-моделью в роли тупого “завершителя строки” и 4B-моделью внутри грамотного агента — колоссальная.
LLM-based coding agents стали мощными инструментами для разработки ПО, способными находить баги и генерировать патчи в кодовых базах. Ключевое слово здесь — агент, а не просто модель.
По мере того как агенты переходят из исследовательских прототипов в продакшн-инструменты, именно scaffolding-код вокруг языковой модели — управляющий цикл, определения инструментов, управление состоянием и контекстная стратегия — всё сильнее определяет поведение агента, его ошибки и расход токенов.
Другими словами: архитектура агента важнее, чем параметры модели. Именно это позволяет получить 87% с 4B-моделью там, где «голая» 13B в режиме chat-completion едва дотягивает до 60%.
Архитектура: из чего состоит агент
Простейший способ объяснить результат — посмотреть на схему работы агента. Это не просто «модель + промпт». Это многошаговый цикл с инструментами, памятью и самопроверкой.
graph TD
A[Задача / задание на код] --> B[Планировщик: декомпозиция]
B --> C[Генератор кода - LLM 4B]
C --> D[Выполнение в sandbox]
D --> E{Тесты прошли?}
E -->|Да| F[Финальный патч / ответ]
E -->|Нет, итерация ≤ N| G[Анализ ошибки]
G --> H[Рефлексия: корректировка промпта]
H --> C
E -->|Исчерпаны попытки| I[Лучший из вариантов]
Каждый блок схемы — это отдельный инженерный выбор:
1. Планировщик (Task Decomposition)
Перед тем как отдать задачу в модель, агент разбивает её на подзадачи. AgentCoder предложил мульти-агентный фреймворк со специализированными ролями для программирования, дизайна тестов и выполнения — продемонстрировав эффективность ролевой специализации.
Для 4B-модели это критически важно: маленькая модель хорошо справляется с одной конкретной задачей, но теряется в многоаспектных инструкциях. Разбиение на шаги — это способ «дать модели подышать».
2. Генератор кода
Здесь и живёт 4B-модель. Gemma 3 4B IT показывает GSM8K 89.2% и HumanEval 71.3% с всего лишь 4 миллиардами параметров — и это без агентной обвязки, то есть в режиме чистой генерации. С агентом цифра вырастает до 87%+.
3. Sandbox-выполнение и обратная связь
Это один из главных «секретов». Agent-based решения берут интерактивный подход: снабжённый описанием задачи, набором доступных инструментов и конкретным условием задачи, агент взаимодействует с исполняемой средой в течение нескольких ходов, вносит изменения в исходный код и автономно решает, когда остановиться.
Каждый раз, когда сгенерированный код не проходит тесты, агент получает stdout/stderr и использует их как дополнительный контекст для следующей итерации.
Пять техник, которые дали 87%
1. Structured Output + Grammar Sampling
Маленькие модели часто «ломают» формат: незакрытые скобки, неверные отступы, лишний текст после кода. Решение — форсировать грамматику через constrained decoding (например, llama.cpp с GBNF-грамматиками или Outlines).
from outlines import models, generate
from outlines.grammars import python
model = models.llamacpp("qwen2.5-coder-4b.gguf", ...)
generator = generate.grammar(model, python)
code = generator(prompt)
Это гарантирует, что модель выдаст синтаксически корректный Python — до запуска тестов.
2. Execution-Driven Iteration (EDI)
Агент не просто генерирует код один раз. Он запускает его, смотрит на ошибку и итерирует. Для 4B-модели оптимально 3–5 итераций: дальше модель начинает «ходить по кругу».
3. Few-Shot в промпте (domain-specific)
Общие few-shot примеры работают хуже, чем примеры из того же домена (алгоритмы, работа со строками, математика). Кластеризуйте задачи HumanEval и подбирайте релевантные примеры динамически.
def get_few_shots(task: str, k: int = 3) -> list[dict]:
# Эмбеддинг задачи → поиск похожих примеров
embedding = embed(task)
similar = vector_store.search(embedding, top_k=k)
return [example_to_prompt(ex) for ex in similar]
4. Self-Reflection через Chain-of-Thought
Перед генерацией кода модель сначала «думает вслух» — описывает алгоритм в 2–3 предложениях. Для маленьких моделей это существенный буст: результаты экспериментов показывают, что агентный подход даёт значительный прирост от 2.0 до 15.8 по сравнению с прямой генерацией из LLM.
[THINK]
Нужно найти все простые числа до N.
Алгоритм: решето Эратосфена.
Крайний случай: N < 2 → вернуть [].
[/THINK]
def primes_up_to(n: int) -> list[int]:
...
5. Test-Aware Prompting
Если агент видит сигнатуру теста (без ответа), он генерирует код, который с большей вероятностью пройдёт именно этот тест. Этот приём легально применим при работе с HumanEval, где docstring сам по себе содержит примеры входных данных.
Сравнение подходов: модель vs агент
Посмотрим, как разные конфигурации соотносятся по результату на HumanEval:
| Конфигурация | Параметры | Pass@1 HumanEval | Стоимость запуска |
|---|---|---|---|
| Gemma 3 4B (базовая генерация) | 4B | ~71% | Бесплатно (локально) |
| Qwen2.5-Coder-7B (базовая) | 7B | ~82% | Бесплатно (локально) |
| 4B + агент (EDI + CoT + few-shot) | 4B | ~87% | Бесплатно (локально) |
| GPT-4o (API, без агента) | ~200B+ | ~90% | $5–15 / 1M токенов |
| Claude 3.5 Sonnet (API, без агента) | ~70B+ | ~92% | $3–15 / 1M токенов |
Разница между 4B с агентом и GPT-4o без агента — всего 3%. Разница в цене — на порядок.
Топовые модели сейчас превышают 90% pass@1 на HumanEval. Но эти модели стоят денег, требуют API и не запускаются офлайн. Агентная архитектура на 4B — это рабочая альтернатива для большинства задач.
Честный взгляд на ограничения
Превысить 87% на HumanEval с 4B-моделью — впечатляющий результат, но важно понимать его контекст.
SWE-bench представляет значительный сдвиг в философии оценки — от изолированных самодостаточных задач к сложной реальности разработки ПО. Вместо простого промпта модель получает доступ к целому репозиторию и настоящему описанию проблемы с GitHub. Её задача — действовать как AI-инженер: ориентироваться в кодовой базе, понять проблему и сгенерировать патч, который решит её.
На SWE-bench результаты у 4B-моделей значительно скромнее — и это честно. Agentless бросил вызов тренду на сложность, показав, что простота всё ещё может давать конкурентоспособный результат. Но даже «простые» подходы на SWE-bench требуют моделей с более глубоким пониманием контекста.
Подходит:
- Генерация функций и утилит по описанию
- Написание unit-тестов
- Рефакторинг небольших модулей
- Офлайн-использование (нет доступа к API)
- Встраивание в CI/CD для lint-level проверок
Не подходит:
- Навигация по большому репозиторию (>50k строк)
- Исправление сложных архитектурных багов
- Задачи, требующие понимания бизнес-логики из нескольких файлов
Как воспроизвести: минимальный стек
Для запуска подобного агента локально нужен минимальный стек:
# Установка зависимостей
pip install outlines llama-cpp-python chromadb
# Скачать модель
huggingface-cli download bartowski/Qwen2.5-Coder-4B-Instruct-GGUF \
--include "*Q4_K_M*" --local-dir ./models
import subprocess
from outlines import models, generate
MODEL_PATH = "./models/Qwen2.5-Coder-4B-Instruct-Q4_K_M.gguf"
def run_agent(task: str, max_iterations: int = 5) -> str:
model = models.llamacpp(MODEL_PATH, n_ctx=4096)
history = []
code = ""
for i in range(max_iterations):
prompt = build_prompt(task, history, code)
code = generate.text(model)(prompt)
code = extract_python(code)
result = execute_in_sandbox(code)
if result.success:
return code
history.append({"error": result.stderr, "code": code})
return best_candidate(history)
def execute_in_sandbox(code: str) -> object:
"""Запуск кода в изолированном subprocess с таймаутом"""
try:
result = subprocess.run(
["python", "-c", code],
capture_output=True, text=True, timeout=5
)
return type("R", (), {
"success": result.returncode == 0,
"stderr": result.stderr
})()
except subprocess.TimeoutExpired:
return type("R", (), {"success": False, "stderr": "timeout"})()
Ключевая методология включает три компонента: дистилляцию траекторий из сильных LLM, корректировку с обратной связью от супервизора и агентно-ориентированную тонкую настройку LoRA — что обеспечивает эффективную специализацию при малом объёме памяти. Это продвинутый путь — если хочется выжать максимум, можно дообучить 4B-модель на синтетических траекториях, сгенерированных более крупной моделью.
Заключение
История с «87% на 4B» — это не магия и не обман. Это демонстрация простого принципа: интеллект системы ≠ интеллект отдельной модели. Грамотный агентный цикл с выполнением кода, итерацией по ошибкам, chain-of-thought и динамическими few-shot примерами даёт колоссальный прирост поверх базовой генерации.
Результаты демонстрируют, что аккуратная агентно-ориентированная тонкая настройка высвобождает высококачественные возможности мульти-агентного кодирования в малой языковой модели.
Что важно вынести:
- Выбирайте нужный бенчмарк — HumanEval, SWE-bench и LiveCodeBench измеряют разные вещи
- Investing в scaffolding часто даёт больший ROI, чем переход на более дорогую модель
- Execution feedback — самая сильная техника: модель видит реальный результат, а не галлюцинирует
- Малые модели — рабочий инструмент для конкретного класса задач: генерация функций, тестирование, рефакторинг
Если вы строите внутренний coding-ассистент и не хотите платить за API, 4B-модель с агентной обвязкой — вполне реальная отправная точка.