Михаил Базаров Разработка на 1С-Битрикс Михаил Базаров

Как собрать свой прогонщик для локальных LLM: меряем модели на своих задачах

Просмотров: 20 В прошлой заметке я устроил турнир своим локальным моделям: пять сложных PHP-задач, автоматическая проверка кода тестами и таблица результатов. Там же обещал рассказать, как собрать такой прогонщик самому. Делаю это. Ниже весь рецепт: как придумать задачи, как написать к ним тесты, как автоматизировать прогон по всем моделям и на какие грабли я наступил, пока это работало.

У заметки есть предыстория: результаты турнира локальных моделей на пяти PHP-задачах - там таблица баллов и выводы, какая модель для чего годится.

Зачем свой прогонщик, если есть готовые бенчмарки

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

Плюс локальный прогонщик решает три бытовые проблемы:

  • Свои задачи. Никто не мерит модели на разборе цен с неразрывными пробелами и деревьях категорий с циклами, а я это пишу каждый день.
  • Объективность. Автотесты не устают и не жалеют знакомую модель. Глазами я бы такое не оценил честно.
  • Повторяемость. Вышла новая модель - добавил строку в конфиг и прогнал заново. Через полгода можно сравнить результаты на тех же задачах.

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

Что понадобится

  • Локальный сервер с любой моделью и OpenAI-совместимым API (у меня llama.cpp на порту 8080).
  • Интерпретатор языка, на котором пишете. У меня PHP, поэтому тесты я прогоняю через php из командной строки.
  • Python 3 для прогонщика (удобно дергать API и разбирать ответы).
  • Час времени на сам прогон, если моделей много и они думающие.

Структура прогона

Всё живёт в одной папке, у меня это phpbench:

phpbench/
  prompts/    # тексты задач (по файлу на задачу)
  tests/      # тесты (по файлу на задачу)
  ref/        # эталонные решения, по которым проверяются сами тесты
  results/    # ответы моделей, извлечённый код и итоговая таблица
  run_bench.sh

Логика простая: прогонщик берёт задачу из prompts, отправляет её модели, вытаскивает из ответа код, проверяет синтаксис и прогоняет через тест из tests. Результат складывает в results. Эталоны из ref нужны, чтобы убедиться: тесты корректны и задача вообще решаема.

Шаг 1. Придумать задачи

Три правила, которые я вывел для себя.

  • Задача должна быть проверяемой автоматически. Если результат нельзя сравнить с эталоном, вы снова скатываетесь к оценке на глаз. Формулировка должна предполагать конкретную функцию с конкретной сигнатурой.
  • Задача должна повторять реальную практику. Не алгоритм сортировки из учебника, а то, что вы правда пишете: разбор цен, агрегаты по дереву разделов, кэш, чистка пользовательского HTML, вычисление выражения без eval.
  • В задаче должны быть крайние случаи. Именно на них видно разницу между моделями. Неразрывные пробелы в цене, цикл в дереве категорий, протухший TTL, javascript: в ссылке, деление на ноль.

Мои пять задач выглядели так:

parsePriceToRub(string $input): ?array
aggregateCategoryTree(array $categories, array $products): array
LruTtlCache(int $capacity, \Closure $clock)
sanitizeHtml(string $html): string
calcExpression(string $expr): float|int

Сигнатуры я указываю прямо в промпте, иначе модель назовёт функцию по-своему и тест не найдёт её.

Шаг 2. Написать тесты и эталоны

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

<?php
require $argv[1];

$pass = 0;
$total = 0;
function chk($cond, $msg) {
    global $pass, $total;
    $total++;
    if ($cond) { $pass++; } else { echo "  FAIL: $msg\n"; }
}

chk(parsePriceToRub('1 234,56 руб.')['amount'] === 1234.56, 'неразрывный пробел');
chk(parsePriceToRub('1234') === null, 'без валюты должен быть null');
// ... остальные проверки

echo "PASS $pass/$total\n";

Эталонные решения обязательны. Сначала я написал тесты, потом сам решил задачи и прогнал их через тесты. И знаете что? Три эталона сначала не прошли свои же тесты. Где-то в тесте было неверное ожидание, где-то в эталоне баг. Это нормальный этап, и пропускать его нельзя: если эталон не даёт сто процентов, тесты врут, а вы потом делаете неверные выводы о моделях.

Ещё момент: не жалейте проверок. У меня на пять задач получилось 62 проверки. Чем больше крайних случаев, тем меньше шансов, что модель пройдёт тест случайно.

Шаг 3. Собрать прогонщик

Прогонщик делает по каждой модели одно и то же. У меня это bash, который дергает Python для запросов к API.

for model in "${MODELS[@]}"; do
    # 1. остановить предыдущую и запустить нужную
    llm.sh stop; llm.sh start "$model"

    # 2. дождаться готовности сервера
    until curl -s http://127.0.0.1:8080/health | grep -q ok; do sleep 4; done

    # 3. по каждой задаче: запрос, извлечение кода, тесты
    for task in 1 2 3 4 5; do
        python3 ask.py "$model" "$task"     # сохраняет ответ и код
        php "tests/test$task.php" "results/$model/task$task.php"
    done
done

Внутри ask.py три вещи: запрос к API, извлечение кода из ответа и запись метрик.

body = {
    'messages': [{'role': 'user', 'content': prompt}],
    'max_tokens': 8000,
    'temperature': 0.2,
}
# ... запрос ...
content = data['choices'][0]['message']['content']
code = re.search(r'```(?:php)?\s*(.*?)```', content, re.S)
if not code:
    # блок не закрыт - берём всё после открывающего ```
    code = re.search(r'```(?:php)?\s*(.*)$', content, re.S)

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

Шаг 4. Считать метрики, а не только баллы

Баллы без времени обманчивы. У меня модель, которая выиграла турнир по качеству, работала в пять раз медленнее остальных и потратила на пять задач 36 минут. Для ежедневной работы это неприемлемо, и никакая таблица баллов этого не покажет, если не записывать время.

Поэтому в итоговую строку я пишу: модель, задачу, скорость генерации, общее время, количество токенов, результат теста. Из этих строк уже собирается сводная таблица.

model	task	tg_tps	wall_s	tokens	result
Tiel-Coder-35B-A3B-Q8	1	41.0	68.4	354	PASS 15/15

Грабли, на которые я наступил

  • Модели возвращают код без открывающего тега. Почти все ответили функцией без <?php. Если такой файл отдать PHP, он считается обычным HTML, функция не определяется, и все тесты падают. Прогонщик должен сам добавлять тег, если его нет.
  • Думающие модели съедают лимит токенов на размышления. На сложных задачах блок размышлений раздувается так, что на код места не остаётся: ответ приходит пустым. Я сначала поставил 3000 токенов и получил нули у всех моделей, потом поднял до 8000 и только тогда увидел реальные результаты. Если модель можно запустить без размышлений, стоит прогнать и так.
  • Тесты нужно запускать с таймаутом. Одна модель сгенерировала парсер, который зациклился на части выражений. Без таймаута прогон просто зависает, и вы не понимаете, что происходит. Обязательно ловите это и записывайте как провал, а не как зависший процесс.
  • Проверяйте тесты на эталонах, прежде чем винить модели. Половина моих первых «нулевых» результатов оказалась не виной моделей, а багами в прогонщике и тестах.
  • Держите одинаковые условия для всех. Одинаковый промпт, одинаковая температура, одинаковый лимит токенов, одинаковый контекст. Иначе сравнение превращается в лотерею.
  • Помните, что именно вы измерили. Мои задачи одиночные, без инструментов и многошаговых циклов. Модель, которая слабо решает их, может раскрыться в агентском режиме, и наоборот. Это не повод для глобальных выводов, это повод для следующего теста.

Что показывает такая таблица

Практическая польза не в «кто первый», а в понимании профилей. У меня по итогам вышло так:

  • одна модель лучшая по качеству (92 процента), но настолько медленная, что годится только для сложных задач;
  • две модели дают 81-82 процента при скорости около 40 токенов в секунду, и это лучший баланс для ежедневной работы;
  • маленькая модель оказалась лучшей в одной конкретной задаче (чистка HTML) и худшей в другой;
  • расцензуренные версии в среднем теряют около трети баллов на алгоритмических задачах.

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

Итог

Свой прогонщик - это не про спортивную честность, а про практическую пользу. Задачи из моей работы, проверка автотестами, скорость рядом с баллами. Полдня на сборку и потом годы пользы: вышла новая модель, добавил строку в конфиг, прогнал, сравнил с прошлыми результатами.

Если тема зайдёт, в следующей заметке разберу, как измерять модели в агентском режиме - с инструментами, файлами и многошаговыми задачами. Там уже понадобится другая методика: считать не только правильность ответа, но и число шагов, стоимость и умение не сломать то, что уже работает.

С чего всё началось и что из этого получилось - в отдельной заметке Турнир локальных LLM: 5 сложных PHP-задач, 9 моделей, 62 проверки. Там сводная таблица по всем моделям, разбор кто в чём силён и выводы, кого звать на какую работу. А здесь - инструмент, которым это собрано. Сделаете свой набор задач, и сможете так же проверять любую новую модель на своих условиях.

И да: чужие бенчмарки читать всё равно полезно. Просто своя линейка отвечает на ваш вопрос, а чужая - на чей-то ещё.

Услуги Стоимость разработки на 1С-Битрикс

Стоимость разработки сайта зависит от объёма и сложности проекта. Ниже приведены ориентировочные цены, как правило не выходят за обозначенные рамки. Срок разработки зависит от сложности проекта: как правило называю сроки с запасом.
Финальная стоимость и сроки разработки обговариваются на этапе обсуждения. Скачайте опросник на разработку, заполните как можно подробнее и вышлите удобным способом. После ознакомления смогу задать уточняющие вопросы и оценить проект.
Поддержка и доработки проектов
от 3 000 руб. от 1 часа

Выполнение доработок любой сложности. Поддержка, модернизация и расширение функционала существующих проектов. Решение задач: от мелких правок вёрстки до разработки новых модулей.

Подробнее →
Сайт на готовом решении 1С-Битрикс
от 70 000 руб. от 5-ти дней

Вариант для тех, кто не хочет тратить много средств на индивидуальный проект, и не имеет серьезных требований к сайту. Магазин, быстро запускается на базе одного из 200-та готовых решений.

Подробнее →
Индивидуальная разработка магазина
от 400 000 руб. от 5-ти недель

Разработка магазина на 1С-Битрикс с нуля. Дизайн, сборка и оптимизация производительности под конкретный проект и требования. Реализация любого функционала без ограничений готовых решений.

Подробнее →
Мобильное приложение
от 300 000 руб. от 4-х недель

Разработка кроссплатформенного мобильного приложения, которое не уступает нативным решениям как в производительности, так и пользовательском опыте. Публикуется в AppStore, GooglePlay и RuStore

Подробнее →
Инфоресурс
от 170 000 руб. от 3-х недель

Информационный ресурс любой сложности. Сайт для СМИ, городской портал или многопользовательская доска объявлений. Внутренние форумы, блоги- по необходимости.

Подробнее →
Сайт компании
от 150 000 руб. от 2-х недель

Корпоративный сайт с информационными разделами, каталогом товаров или услуг. Включает формы обратной связи карточек каталога, любое количество статичных и динамичных разделов.

Подробнее →

Включено в стоимость разработки:

  • Лицензия на 1С-Битрикс необходимой редакции, дополнительные модули, для реализации функционала и видео-инструкции по работе с готовым проектом
  • Оптимизация программной части проекта и конфигурации сервера под максимальную скорость работы. Базовая СЕО оптимизация и добавление сайта в поисковые системы.

Блог-note Заметки по 1С-Битрикс