87% на бенчмарке с моделью 4B: как это работает

Все привыкли к тому, что высокие результаты на бенчмарках по программированию — удел больших закрытых моделей: GPT-4, Claude Opus, Gemini Ultra. Но что если 4-миллиардная модель, запускаемая на ноутбуке, способна показать 87% на HumanEval? Звучит как кликбейт — но это реальная инженерная задача, которую удалось решить за счёт грамотной архитектуры агента, а не за счёт размера модели.

В этом материале — полный разбор того, как устроен такой агент: какие техники применяются, как правильно выбрать бенчмарк, и почему «маленькая» модель с умным скаффолдингом может обыграть «большую» модель в лоб.


Почему размер модели — не главное

Распространённое заблуждение: чем больше параметров, тем лучше код. На практике это верно лишь при прочих равных. Разница между 4B-моделью в роли тупого “завершителя строки” и 4B-моделью внутри грамотного агента — колоссальная.

LLM-based coding agents стали мощными инструментами для разработки ПО, способными находить баги и генерировать патчи в кодовых базах. Ключевое слово здесь — агент, а не просто модель.

По мере того как агенты переходят из исследовательских прототипов в продакшн-инструменты, именно scaffolding-код вокруг языковой модели — управляющий цикл, определения инструментов, управление состоянием и контекстная стратегия — всё сильнее определяет поведение агента, его ошибки и расход токенов.

Другими словами: архитектура агента важнее, чем параметры модели. Именно это позволяет получить 87% с 4B-моделью там, где «голая» 13B в режиме chat-completion едва дотягивает до 60%.

ℹ Какой бенчмарк имеется в виду?
Под «87%» подразумевается HumanEval (pass@1) — стандартный бенчмарк на генерацию Python-функций по docstring. HumanEval измеряет способность генерировать корректные Python-функции по описанию на естественном языке, запуская код против заранее определённых тестов. Ключевая метрика — pass@1 (процент задач, решённых корректно с первой попытки). Это не SWE-bench, но это честный и воспроизводимый стандарт.

Архитектура: из чего состоит агент

Простейший способ объяснить результат — посмотреть на схему работы агента. Это не просто «модель + промпт». Это многошаговый цикл с инструментами, памятью и самопроверкой.


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 итерации дают ~80% задач решённых корректно, 5 итераций — ~87%. После 7 итераций прирост не превышает 1–2%, но стоимость растёт линейно. Используйте early stopping: если ошибка не изменилась между двумя итерациями — прерывайте цикл.

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-моделью — впечатляющий результат, но важно понимать его контекст.

⚠ Важно: HumanEval ≠ реальная разработка
Разрыв между производительностью на бенчмарке и реальностью — не случайность. Большинство бенчмарков измеряют изолированные задачи по написанию кода. Они проверяют, может ли модель завершить одну функцию или решить алгоритмическую задачу. На реальном репозитории с зависимостями, легаси-кодом и неочевидными требованиями всё будет иначе.

SWE-bench представляет значительный сдвиг в философии оценки — от изолированных самодостаточных задач к сложной реальности разработки ПО. Вместо простого промпта модель получает доступ к целому репозиторию и настоящему описанию проблемы с GitHub. Её задача — действовать как AI-инженер: ориентироваться в кодовой базе, понять проблему и сгенерировать патч, который решит её.

На SWE-bench результаты у 4B-моделей значительно скромнее — и это честно. Agentless бросил вызов тренду на сложность, показав, что простота всё ещё может давать конкурентоспособный результат. Но даже «простые» подходы на SWE-bench требуют моделей с более глубоким пониманием контекста.

📝 Когда 4B-агент подходит

Подходит:

  • Генерация функций и утилит по описанию
  • Написание 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 примерами даёт колоссальный прирост поверх базовой генерации.

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

Что важно вынести:

  1. Выбирайте нужный бенчмарк — HumanEval, SWE-bench и LiveCodeBench измеряют разные вещи
  2. Investing в scaffolding часто даёт больший ROI, чем переход на более дорогую модель
  3. Execution feedback — самая сильная техника: модель видит реальный результат, а не галлюцинирует
  4. Малые модели — рабочий инструмент для конкретного класса задач: генерация функций, тестирование, рефакторинг

Если вы строите внутренний coding-ассистент и не хотите платить за API, 4B-модель с агентной обвязкой — вполне реальная отправная точка.