Skip to content

askads/llm-bench

Repository files navigation

llm-bench

🇷🇺 Русский · 🇬🇧 English

Самостоятельный харнесс, чтобы гонять модели против MCP-тулз askads (Яндекс Директ / VK Ads / Метрика) и бенчить их между собой на нашем домене (русская рекламная аналитика + многошаговый tool-use), а не на чужих кодинг-бенчах.

Два режима:

  • fixed — детерминированные фикстуры (замороженный фейк-кабинет как tool_result). Воспроизводимое сравнение моделей, гоняется в CI без сети/ключей кабинета.
  • live — спавн РЕАЛЬНЫХ MCP-серверов по stdio (mcp-yandex-direct и т.д.) с токенами из env. Интеграционные тесты самих тулов на живом кабинете.

Движок и MCP-клиент развязаны от askads (вынесены в llmbench/), репозиторий автономен.

Термины

Как читать таблицу результатов:

Метрика Что Кто считает
Accuracy точность чисел (CTR/CPC/CPA/расход) + верная привязка к кампании (entity-анкоринг), без выдумок Оценивается кодом
Tools Use нужные тулы вызваны успешно, в нужном порядке, без лишних/запрещённых Оценивается кодом
Edge Cases поведение в краевых случаях (пустой отчёт, отказ менять ставку, уточнение) Оценивается LLM-судьями
Lang quality естественность и ясность русского Оценивается LLM-судьями
Score сводный балл прогона = среднее доступных компонент (см. «Как считается Score»)
Cost per Answer средняя стоимость одного ответа, USD
Score per USD «качество на доллар» (Score ÷ цена); выше = выгоднее
Stability 5 − разброс Score между повторами кейса; выше = стабильнее
лучший баланс «качество/цена»: нет варианта, который был бы и качественнее, и дешевле одновременно

Как считается Score

Score одного прогона — среднее доступных компонент (не всех четырёх колонок сразу):

  • Tools Use — всегда;
  • Accuracy — если у кейса есть golden-факты (числовые кейсы);
  • Edge Cases и Lang quality — если работали судьи.

Состав компонент зависит от кейса, поэтому:

  • Score сравним между вариантами (кейсы у всех одни), но не равен среднему четырёх колонок отчёта;
  • упавшие прогоны (ошибки API, обрезка лимитом токенов) в метрики не входят — они в отдельной колонке Err;
  • Stability = 5 − средний разброс (σ) Score между повторами одного кейса — ловит шум модели, а не разницу сложности кейсов.

Результаты последнего прогона

results/2026-07-03/ (+ англ. results.en.md) — свежий полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном скоринге.

Топ-3 по балансу «качество/цена» (⭐):

LLM Thinking Score Cost/ответ Чем берёт
GLM-4.6 нет 4.62 $0.003 лучший «качество/цена», ~27× дешевле Opus
GPT-4.1 нет 4.93 $0.013 неожиданно силён и дёшев
Opus 4.8 adaptive/high 4.96 $0.081 потолок качества

Sonnet 4.6 (текущий прод) просел на краевых кейсах (Edge ~3.3 — выдумывает причины на пустом срезе, не уточняет неясный вопрос). Прогон results/2026-06-29/ — исторический, до правок скоринга.

Структура

llmbench/
  core.py       # allowlists, конвертеры тулов, обрезка, ставки, system-prompt, retry, таймауты
  fixtures.py   # замороженный кабинет + FIXTURE_VERSION (единый источник golden-фактов)
  mcp.py        # live stdio-клиент + реестр серверов + fake-сессия + preflight_live
  engines.py    # агентные loop'ы: run_anthropic (Claude/GLM), run_openai (GPT); ретраи, таймауты
  scoring.py    # numeric (entity-анкоринг) + toolcheck (учёт is_error) + cost
  judges.py     # панель {Claude, GPT, GLM, (опц.) Gemini}, нейтральность, детерминизм
  cases.py      # кейсы (вопрос + trace-спека + golden_facts с entity + рубрика)
  report.py     # агрегация записей, Stability/Score/Pareto, сборка markdown (тестируется в CI)
  runner.py     # сетка вариантов × кейсы × repeat, JSONL-персист, отчёт
tests/          # офлайн self-test: скоринг + агрегация + конвейер ранера (всё в CI)
results/        # per-date папки: results.ru.md + results.en.md + сырой runs.jsonl (всё под VCS для fixed)

Запуск

Нужен Python ≥ 3.10 (в CI — 3.11).

Офлайн self-test (без сети/денег; CI):

pip install -r requirements-dev.txt
pytest -q

Детерминированный model-бенч (нужны ключи моделей):

RUN_BENCH=1 ANTHROPIC_API_KEY=… ZAI_API_KEY=… OPENAI_API_KEY=… \
  python -m llmbench.runner --mode fixed --repeat 2

Опц. GOOGLE_API_KEY — добавляет Gemini-судью (нейтрального, когда GPT — кандидат).

Против РЕАЛЬНЫХ тулов (нужны npm-серверы + токены кабинета):

npm install                       # ставит mcp-yandex-direct и др.
RUN_BENCH=1 ANTHROPIC_API_KEY=… YANDEX_DIRECT_TOKEN=… \
  python -m llmbench.runner --mode live --variants "GLM-4.6 disabled" --judges off

Env токенов кабинета (для --mode live):

Env Назначение
YANDEX_DIRECT_TOKEN (+ опц. YANDEX_DIRECT_LOGIN) токен Яндекс Директа — обязателен для live; LOGIN нужен, если токен агентский
YANDEX_METRIKA_TOKEN токен Метрики (для metrika-кейсов)
VK_ADS_TOKEN токен VK Ads
MCP_PATH_YANDEX_DIRECT опц. — переопределить путь к MCP-серверу (/path/to/dist/index.js)

Live-режим делает префлайт (токены + наличие серверов) ДО первого платного вызова.

Флаги ранера:

Флаг Что делает
--mode fixed|live фикстуры (дефолт) или реальные MCP-серверы
--variants <подстроки…> какие варианты гнать (опечатка в фильтре — ошибка, а не молчаливый полный грид)
--cases <id…> какие кейсы гнать (та же защита от опечатки)
--repeat N повторов на кейс (дефолт 2)
--judges panel|neutral|off панель судей / только нейтральные / без судей
--concurrency N параллельных прогонов внутри варианта (дефолт 4)
--dry-run показать смету (число прогонов/судейских) без ключей и трат
--out <dir> каталог отчёта (дефолт results/<date>/)
--report-from <jsonl> пересобрать отчёт (ru+en) из runs.jsonl бесплатно
--resume <jsonl> догнать прерванный прогон (только недостающие/упавшие ключи)

Список вариантов (модель × thinking/effort/reasoning) — в llmbench/runner.py; добавить модель = одна строка (не забудь тариф в core.MODEL_RATES, иначе ранер предупредит).

Артефакты и пере-скоринг

Каждый прогон складывается в дата-папку results/<date>/: сырой runs.jsonl (по записи на прогон — ответ, tool-трейс, usage, все оценки, ошибки) и двуязычный авто-отчёт results.ru.md + results.en.md. runs.jsonl — источник правды: отчёт (обе версии) пересобирается из него бесплатно, без повторных вызовов моделей —

python -m llmbench.runner --report-from results/2026-07-03/runs.jsonl

runs.jsonl под VCS для fixed-прогонов (фейк-кабинет, приватных данных нет; ~230 КБ в паке). ⚠️ Live-прогоны содержат данные реального кабинета — их runs.jsonl не коммить (локально/приватный S3). Отчёты .md — под VCS. Топ-3, прозу и выводы дописывают руками поверх сгенерированного грида в той же дата-папке. Второй прогон за день не затирает первый (при коллизии по дате к папке добавляется время).

Прервался (обнулился баланс, Ctrl-C)? Готовые прогоны уже в runs.jsonl (пишется построчно по мере готовности) — долей баланс и догони остаток, платя только за него:

RUN_BENCH=1 …ключи… python -m llmbench.runner --resume results/2026-07-03/runs.jsonl

--resume пропускает уже успешные (вариант, кейс, повтор), гоняет только упавшие/недостающие, дописывает в тот же файл и пересобирает отчёт (при дублях успех побеждает). Можно и добавить ключ (напр. OPENAI_API_KEY) и догнать варианты, пропущенные в исходном прогоне.

Известные ограничения

  • Потолок кейсов: топ-модели упираются в 5.0 по Tool/Numeric → «паритет» здесь = «оба отлично решают ЭТИ задачи». Для строгого различения качества нужны более трудные кейсы.
  • Судьи вторичны: без нейтрального вендора первичный мягкий балл = среднее панели (advisory, возможна self-preference). Вес сравнения — на ключевых метриках.
  • Ставки/кэш-множители моделей — по прайс-листам; сверить с биллингом.
  • glm-5/gpt-5: доступность ≠ идентичность ожидаемой модели — сверить.
  • --repeat — грубый флаг шума; «неверное число == пропущенное» в numeric — упрощение.
  • fixed-режим не ловит robustness на грязном API-выводе (для этого --mode live).
  • system-prompt в core.py — доменный (аналитик Директа); замени под свой кейс.

About

Benchmark LLMs (Claude / GLM / GPT) on multi-step MCP tool-use over a Russian ad-analytics domain — deterministic fixtures, code + LLM-judge scoring, cost and Pareto reports.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages