From 621b9de2229098a78307f29cff8b7845306e8ae4 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalko Date: Fri, 3 Jul 2026 16:53:43 +0200 Subject: [PATCH 1/4] =?UTF-8?q?README=20(ru+en):=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20=E2=80=94=20?= =?UTF-8?q?=D0=A2=D0=B5=D1=80=D0=BC=D0=B8=D0=BD=D1=8B=20+=20=D0=A0=D0=B5?= =?UTF-8?q?=D0=B7=D1=83=D0=BB=D1=8C=D1=82=D0=B0=D1=82=D1=8B=20=D0=BD=D0=B0?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=85,=20=D1=82=D0=B0=D0=B1=D0=BB=D0=B8?= =?UTF-8?q?=D1=86=D1=8B,=20=D1=87=D0=B8=D1=82=D0=B0=D0=B5=D0=BC=D1=8B?= =?UTF-8?q?=D0=B9=20Score?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Новая секция 'Термины' (таблица метрик + кто считает) перед результатами. - 'Результаты последнего прогона' поднято наверх (было внизу). - Удалена строка-ссылка на REVIEW.md. - Env токенов кабинета и Флаги ранера переведены из plain-text в таблицы. - 'Как считается Score' переписан: определение + буллеты вместо плотного абзаца. - 'Известные ограничения' без изменений. Обе языковые версии синхронны. --- README.en.md | 88 ++++++++++++++++++++++++++++++++++++---------------- README.md | 81 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 121 insertions(+), 48 deletions(-) diff --git a/README.en.md b/README.en.md index e0696fa..1e5ec1c 100644 --- a/README.en.md +++ b/README.en.md @@ -15,7 +15,30 @@ Two modes: The engine and MCP client are **decoupled from askads** (extracted into `llmbench/`), so the repository is self-contained. -> Full review of the harness and the list of fixed issues — see [`REVIEW.md`](REVIEW.md). +## Terms + +How to read the results table: + +| Metric | What | Computed by | +|---|---|---| +| **Accuracy** | number correctness (CTR/CPC/CPA/spend) + right campaign attribution (entity anchoring), nothing invented | code | +| **Tools Use** | right tools called successfully, in the right order, nothing extra/forbidden | code | +| **Edge Cases** | behavior in edge cases (empty report, refusing a bid change, clarifying) | judges | +| **Lang quality** | naturalness and clarity of the Russian | judges | +| **Score** | a run's overall score = mean of the available components (see "How Score is computed") | — | +| **Cost per Answer** | mean cost of one answer, USD | — | +| **Score per USD** | "quality per dollar" (Score ÷ cost); higher = better value | — | +| **Stability** | `5 − spread of Score between repeats of a case`; higher = more stable | — | +| **⭐ (Pareto)** | best quality/price balance — a variant that can't be both beaten and undercut | — | + +## Latest run results + +**[`results/2026-07-03/`](results/2026-07-03/results.en.md)** (+ Russian `results.ru.md`) — a +fresh full grid (16 variants × 9 cases × 3 repeats = 432 runs, 0 errors, ≈ $18) on the fixed +scoring: Top-3 + Pareto frontier + takeaways. In short: **GLM-4.6 without thinking** is the best +quality/price, **GPT-4.1** is surprisingly strong and cheap, **Opus 4.8 (adaptive/high)** is the +quality ceiling; **Sonnet (production)** drops on edge cases. The `results/2026-06-29/` run is +historical, before the scoring fixes. ## What is scored, and by whom @@ -65,17 +88,36 @@ npm install # installs mcp-yandex-direct, etc. RUN_BENCH=1 ANTHROPIC_API_KEY=… YANDEX_DIRECT_TOKEN=… \ python -m llmbench.runner --mode live --variants "GLM-4.6 disabled" --judges off ``` -Account-token env: `YANDEX_DIRECT_TOKEN` (+ optional `YANDEX_DIRECT_LOGIN`), -`YANDEX_METRIKA_TOKEN`, `VK_ADS_TOKEN`. Override a server path with -`MCP_PATH_YANDEX_DIRECT=/path/to/dist/index.js`. Live mode preflights (tokens + server -presence) BEFORE the first paid call. - -Flags: `--variants`, `--cases` (a typo in the filter is an error, not a silent full grid), -`--repeat`, `--judges panel|neutral|off`, `--concurrency N` (parallel runs within a variant), -`--dry-run` (shows the estimate without keys), `--out`, `--report-from `, -`--resume ` (catch up an interrupted run). The variant -list (model x thinking/effort/reasoning) lives in `llmbench/runner.py`; adding a model is one -line (don't forget the rate in `core.MODEL_RATES`, or the runner warns). + +Account-token env (for `--mode live`): + +| Env | Purpose | +|---|---| +| `YANDEX_DIRECT_TOKEN` | Yandex Direct token (required for live) | +| `YANDEX_DIRECT_LOGIN` | optional — account login if the token is an agency token | +| `YANDEX_METRIKA_TOKEN` | Metrica token (for metrika cases) | +| `VK_ADS_TOKEN` | VK Ads token | +| `MCP_PATH_YANDEX_DIRECT` | optional — override the MCP server path (`/path/to/dist/index.js`) | + +Live mode preflights (tokens + server presence) BEFORE the first paid call. + +Runner flags: + +| Flag | What it does | +|---|---| +| `--mode fixed\|live` | fixtures (default) or real MCP servers | +| `--variants ` | which variants to run (a typo in the filter is an error, not a silent full grid) | +| `--cases ` | which cases to run (same typo guard) | +| `--repeat N` | repeats per case (default 2) | +| `--judges panel\|neutral\|off` | judge panel / neutral only / no judges | +| `--concurrency N` | parallel runs within a variant (default 4) | +| `--dry-run` | show the estimate (run/judge counts) without keys or spend | +| `--out ` | report directory (default `results//`) | +| `--report-from ` | rebuild the report (ru+en) from `runs.jsonl` for free | +| `--resume ` | catch up an interrupted run (only missing/failed keys) | + +The variant list (model x thinking/effort/reasoning) lives in `llmbench/runner.py`; adding a +model is one line (don't forget the rate in `core.MODEL_RATES`, or the runner warns). ## Artifacts and re-scoring @@ -103,22 +145,16 @@ also add a key (e.g. `OPENAI_API_KEY`) and backfill variants skipped in the orig ## How Score is computed -A run's `Score` is the mean of the **available** components: Tools Use (always), Accuracy (if -the case has golden facts), Edge Cases and Lang quality (if judges ran). The set depends on -the case, so Score is comparable across variants (everyone runs the same cases) but is NOT -equal to the mean of the four report columns. Failed runs (API errors, token-limit -truncation) are excluded from metrics and shown in a separate `Err` column. -`Stability = 5 − mean spread of Score between repeats of the same case` (not across cases of -differing difficulty). +A run's `Score` is the **mean of the available components** (not all four columns at once): -## Latest run results +- **Tools Use** — always; +- **Accuracy** — if the case has golden facts (numeric cases); +- **Edge Cases** and **Lang quality** — if judges ran. -**[`results/2026-07-03/`](results/2026-07-03/results.en.md)** (+ Russian `results.ru.md`) — a -fresh full grid (16 variants × 9 cases × 3 repeats = 432 runs, 0 errors, ≈ $18) on the fixed -scoring: Top-3 + Pareto frontier + takeaways. In short: **GLM-4.6 without thinking** is the best -quality/price, **GPT-4.1** is surprisingly strong and cheap, **Opus 4.8 (adaptive/high)** is the -quality ceiling; **Sonnet (production)** drops on edge cases. The `results/2026-06-29/` run is -historical, before the scoring fixes. +The component set depends on the case, so: +- Score is **comparable** across variants (everyone runs the same cases) but is **not equal** to the mean of the four report columns; +- **failed runs** (API errors, token-limit truncation) are excluded from metrics — they show in a separate `Err` column; +- **`Stability = 5 − mean spread (σ) of Score between repeats of the same case`** — it captures model noise, not case difficulty. ## Known limitations diff --git a/README.md b/README.md index 62000f3..85e019a 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,29 @@ VK Ads / Метрика) и **бенчить их между собой** на Движок и MCP-клиент **развязаны от askads** (вынесены в `llmbench/`), репозиторий автономен. -> Полное ревью харнесса и список исправленных проблем — в [`REVIEW.md`](REVIEW.md). +## Термины + +Как читать таблицу результатов: + +| Метрика | Что | Кто считает | +|---|---|---| +| **Accuracy** | точность чисел (CTR/CPC/CPA/расход) + верная привязка к кампании (entity-анкоринг), без выдумок | код | +| **Tools Use** | нужные тулы вызваны успешно, в нужном порядке, без лишних/запрещённых | код | +| **Edge Cases** | поведение в краевых случаях (пустой отчёт, отказ менять ставку, уточнение) | судьи | +| **Lang quality** | естественность и ясность русского | судьи | +| **Score** | сводный балл прогона = среднее доступных компонент (см. «Как считается Score») | — | +| **Cost per Answer** | средняя стоимость одного ответа, USD | — | +| **Score per USD** | «качество на доллар» (Score ÷ цена); выше = выгоднее | — | +| **Stability** | `5 − разброс Score между повторами кейса`; выше = стабильнее | — | +| **⭐ (Pareto)** | лучший баланс «качество/цена» — вариант, который нельзя и превзойти, и удешевить | — | + +## Результаты последнего прогона + +**[`results/2026-07-03/`](results/2026-07-03/results.ru.md)** (+ англ. `results.en.md`) — свежий +полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном +скоринге: Топ-3 + Pareto-фронт + выводы. Кратко: **GLM-4.6 без thinking** — лучший «качество/цена», +**GPT-4.1** неожиданно силён и дёшев, **Opus 4.8 (adaptive/high)** — потолок качества; **Sonnet +(прод)** просел на краевых кейсах. Прогон `results/2026-06-29/` — исторический, до правок скоринга. ## Что и кто считает @@ -64,15 +86,34 @@ 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 токенов кабинета: `YANDEX_DIRECT_TOKEN` (+ опц. `YANDEX_DIRECT_LOGIN`), -`YANDEX_METRIKA_TOKEN`, `VK_ADS_TOKEN`. Путь к серверу можно переопределить -`MCP_PATH_YANDEX_DIRECT=/path/to/dist/index.js`. Live-режим делает префлайт (токены + -наличие серверов) ДО первого платного вызова. - -Флаги: `--variants`, `--cases` (опечатка в фильтре — ошибка, а не молчаливый полный грид), -`--repeat`, `--judges panel|neutral|off`, `--concurrency N` (параллельные прогоны внутри -варианта), `--dry-run` (показывает смету без ключей), `--out`, `--report-from `, -`--resume ` (догнать прерванный прогон). + +Env токенов кабинета (для `--mode live`): + +| Env | Назначение | +|---|---| +| `YANDEX_DIRECT_TOKEN` | токен Яндекс Директа (обязателен для live) | +| `YANDEX_DIRECT_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 ` | какие кейсы гнать (та же защита от опечатки) | +| `--repeat N` | повторов на кейс (дефолт 2) | +| `--judges panel\|neutral\|off` | панель судей / только нейтральные / без судей | +| `--concurrency N` | параллельных прогонов внутри варианта (дефолт 4) | +| `--dry-run` | показать смету (число прогонов/судейских) без ключей и трат | +| `--out ` | каталог отчёта (дефолт `results//`) | +| `--report-from ` | пересобрать отчёт (ru+en) из `runs.jsonl` бесплатно | +| `--resume ` | догнать прерванный прогон (только недостающие/упавшие ключи) | + Список вариантов (модель × thinking/effort/reasoning) — в `llmbench/runner.py`; добавить модель = одна строка (не забудь тариф в `core.MODEL_RATES`, иначе ранер предупредит). @@ -101,20 +142,16 @@ RUN_BENCH=1 …ключи… python -m llmbench.runner --resume results/2026-07- ## Как считается Score -`Score` прогона = среднее **доступных** компонент: Tools Use (всегда), Accuracy (если у -кейса есть golden-факты), Edge Cases и Lang quality (если работали судьи). Состав зависит от -кейса, поэтому Score сравним между вариантами (кейсы у всех одни), но НЕ равен среднему -четырёх колонок отчёта. Упавшие прогоны (ошибки API, обрезка лимитом токенов) в метрики не -входят и видны отдельной колонкой `Err`. `Stability = 5 − средний разброс Score между -повторами одного кейса` (а не между кейсами разной сложности). +`Score` одного прогона — **среднее доступных компонент** (не всех четырёх колонок сразу): -## Результаты последнего прогона +- **Tools Use** — всегда; +- **Accuracy** — если у кейса есть golden-факты (числовые кейсы); +- **Edge Cases** и **Lang quality** — если работали судьи. -**[`results/2026-07-03/`](results/2026-07-03/results.ru.md)** (+ англ. `results.en.md`) — свежий -полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном -скоринге: Топ-3 + Pareto-фронт + выводы. Кратко: **GLM-4.6 без thinking** — лучший «качество/цена», -**GPT-4.1** неожиданно силён и дёшев, **Opus 4.8 (adaptive/high)** — потолок качества; **Sonnet -(прод)** просел на краевых кейсах. Прогон `results/2026-06-29/` — исторический, до правок скоринга. +Состав компонент зависит от кейса, поэтому: +- Score **сравним** между вариантами (кейсы у всех одни), но **не равен** среднему четырёх колонок отчёта; +- **упавшие прогоны** (ошибки API, обрезка лимитом токенов) в метрики не входят — они в отдельной колонке `Err`; +- **`Stability = 5 − средний разброс (σ) Score между повторами одного кейса`** — ловит шум модели, а не разницу сложности кейсов. ## Известные ограничения From d9b5f1a61eb8b405800538649c1ab3f7cbc08a07 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalko Date: Fri, 3 Jul 2026 16:57:42 +0200 Subject: [PATCH 2/4] =?UTF-8?q?fix(README):=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BD=D0=BE=D1=81,=20?= =?UTF-8?q?=D0=B8=D0=B7-=D0=B7=D0=B0=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=20'+=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=D1=88?= =?UTF-8?q?=D0=B0=D0=B3=D0=BE=D0=B2=D1=8B=D0=B9=E2=80=A6'=20=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B4=D0=B5=D1=80=D0=B8=D0=BB=D1=81=D1=8F=20=D0=BA=D0=B0?= =?UTF-8?q?=D0=BA=20=D0=BF=D1=83=D0=BD=D0=BA=D1=82=20=D1=81=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Строка, начинающаяся с '+ ', в markdown = буллет и рвёт абзац. Перенёс '+' в конец предыдущей строки. Проверил все .md — других строк, начинающихся с +/*, нет. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85e019a..cc24856 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ **🇷🇺 Русский** · [🇬🇧 English](README.en.md) Самостоятельный харнесс, чтобы **гонять модели против MCP-тулз askads** (Яндекс Директ / -VK Ads / Метрика) и **бенчить их между собой** на нашем домене (русская рекламная аналитика -+ многошаговый tool-use), а не на чужих кодинг-бенчах. +VK Ads / Метрика) и **бенчить их между собой** на нашем домене (русская рекламная аналитика + +многошаговый tool-use), а не на чужих кодинг-бенчах. Два режима: - **`fixed`** — детерминированные фикстуры (замороженный фейк-кабинет как `tool_result`). From 879b9145b8c3a8a0523903baf7e43164b2ac0129 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalko Date: Fri, 3 Jul 2026 17:07:37 +0200 Subject: [PATCH 3/4] =?UTF-8?q?style(md):=20=D1=83=D0=B1=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D1=85=D0=B0=D1=80=D0=B4=D0=B2=D1=80=D0=B0=D0=BF=20?= =?UTF-8?q?=E2=80=94=20=D0=B0=D0=B1=D0=B7=D0=B0=D1=86=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=83=D0=BD=D0=BA=D1=82=D1=8B=20=D1=81=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=B2=20=D0=BE=D0=B4=D0=BD=D1=83=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BA=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ручные переносы на ~100 символов рвали абзацы и порождали баги (тот '+'). Прозу и пункты списков схлопнул в одну строку (markdown переносит сам); таблицы, код-блоки, заголовки и структуру списков не трогал. Затронуто: README.md, README.en.md, results/2026-07-03/*.md. Проверено: нет ложных списков/разорванных ссылок/инлайн-кода, таблицы целы. --- README.en.md | 49 +++++++++----------------------- README.md | 42 +++++++-------------------- results/2026-07-03/results.en.md | 37 +++++++----------------- results/2026-07-03/results.ru.md | 32 +++++++-------------- 4 files changed, 45 insertions(+), 115 deletions(-) diff --git a/README.en.md b/README.en.md index 1e5ec1c..cde7c83 100644 --- a/README.en.md +++ b/README.en.md @@ -2,18 +2,13 @@ [🇷🇺 Русский](README.md) · **🇬🇧 English** -A standalone harness to **run models against askads' MCP tools** (Yandex Direct / VK Ads / -Metrica) and **benchmark them against each other** on our domain (Russian ad analytics + -multi-step tool use) rather than on unrelated coding benchmarks. +A standalone harness to **run models against askads' MCP tools** (Yandex Direct / VK Ads / Metrica) and **benchmark them against each other** on our domain (Russian ad analytics + multi-step tool use) rather than on unrelated coding benchmarks. Two modes: -- **`fixed`** — deterministic fixtures (a frozen fake ad account served as `tool_result`). - Reproducible model comparison; runs in CI without network or account tokens. -- **`live`** — spawns REAL MCP servers over stdio (`mcp-yandex-direct`, etc.) with tokens - from env. Integration tests of the tools themselves against a live account. +- **`fixed`** — deterministic fixtures (a frozen fake ad account served as `tool_result`). Reproducible model comparison; runs in CI without network or account tokens. +- **`live`** — spawns REAL MCP servers over stdio (`mcp-yandex-direct`, etc.) with tokens from env. Integration tests of the tools themselves against a live account. -The engine and MCP client are **decoupled from askads** (extracted into `llmbench/`), so the -repository is self-contained. +The engine and MCP client are **decoupled from askads** (extracted into `llmbench/`), so the repository is self-contained. ## Terms @@ -33,12 +28,7 @@ How to read the results table: ## Latest run results -**[`results/2026-07-03/`](results/2026-07-03/results.en.md)** (+ Russian `results.ru.md`) — a -fresh full grid (16 variants × 9 cases × 3 repeats = 432 runs, 0 errors, ≈ $18) on the fixed -scoring: Top-3 + Pareto frontier + takeaways. In short: **GLM-4.6 without thinking** is the best -quality/price, **GPT-4.1** is surprisingly strong and cheap, **Opus 4.8 (adaptive/high)** is the -quality ceiling; **Sonnet (production)** drops on edge cases. The `results/2026-06-29/` run is -historical, before the scoring fixes. +**[`results/2026-07-03/`](results/2026-07-03/results.en.md)** (+ Russian `results.ru.md`) — a fresh full grid (16 variants × 9 cases × 3 repeats = 432 runs, 0 errors, ≈ $18) on the fixed scoring: Top-3 + Pareto frontier + takeaways. In short: **GLM-4.6 without thinking** is the best quality/price, **GPT-4.1** is surprisingly strong and cheap, **Opus 4.8 (adaptive/high)** is the quality ceiling; **Sonnet (production)** drops on edge cases. The `results/2026-06-29/` run is historical, before the scoring fixes. ## What is scored, and by whom @@ -116,32 +106,21 @@ Runner flags: | `--report-from ` | rebuild the report (ru+en) from `runs.jsonl` for free | | `--resume ` | catch up an interrupted run (only missing/failed keys) | -The variant list (model x thinking/effort/reasoning) lives in `llmbench/runner.py`; adding a -model is one line (don't forget the rate in `core.MODEL_RATES`, or the runner warns). +The variant list (model x thinking/effort/reasoning) lives in `llmbench/runner.py`; adding a model is one line (don't forget the rate in `core.MODEL_RATES`, or the runner warns). ## Artifacts and re-scoring -Every run lands in a dated folder **`results//`**: the raw `runs.jsonl` (one record per -run — answer, tool trace, usage, all scores, errors) and the bilingual generated report -`results.ru.md` + `results.en.md`. `runs.jsonl` is the source of truth: both report versions -are rebuilt from it for free, with no repeat model calls — +Every run lands in a dated folder **`results//`**: the raw `runs.jsonl` (one record per run — answer, tool trace, usage, all scores, errors) and the bilingual generated report `results.ru.md` + `results.en.md`. `runs.jsonl` is the source of truth: both report versions are rebuilt from it for free, with no repeat model calls — ```bash python -m llmbench.runner --report-from results/2026-07-03/runs.jsonl ``` -`runs.jsonl` is **committed for fixed runs** (fake account, no private data; ~230 KB packed). -⚠️ **Live runs contain real account data — don't commit their `runs.jsonl`** (keep local / in a -private S3). The `.md` reports are in VCS. Top-3, prose and takeaways are added by hand on top of -the generated grid in the same dated folder. A second run on the same day doesn't clobber the -first (a time suffix is added on a date collision). - -**Interrupted (out of credits, Ctrl-C)?** Completed runs are already in `runs.jsonl` (written -line-by-line as they finish) — top up and catch up the rest, paying only for what's left: +`runs.jsonl` is **committed for fixed runs** (fake account, no private data; ~230 KB packed). ⚠️ **Live runs contain real account data — don't commit their `runs.jsonl`** (keep local / in a private S3). The `.md` reports are in VCS. Top-3, prose and takeaways are added by hand on top of the generated grid in the same dated folder. A second run on the same day doesn't clobber the first (a time suffix is added on a date collision). + +**Interrupted (out of credits, Ctrl-C)?** Completed runs are already in `runs.jsonl` (written line-by-line as they finish) — top up and catch up the rest, paying only for what's left: ```bash RUN_BENCH=1 …keys… python -m llmbench.runner --resume results/2026-07-03/runs.jsonl ``` -`--resume` skips already-successful `(variant, case, repeat)` keys, runs only the failed/missing -ones, appends to the same file, and rebuilds the report (on duplicates, success wins). You can -also add a key (e.g. `OPENAI_API_KEY`) and backfill variants skipped in the original run. +`--resume` skips already-successful `(variant, case, repeat)` keys, runs only the failed/missing ones, appends to the same file, and rebuilds the report (on duplicates, success wins). You can also add a key (e.g. `OPENAI_API_KEY`) and backfill variants skipped in the original run. ## How Score is computed @@ -158,10 +137,8 @@ The component set depends on the case, so: ## Known limitations -- **Case ceiling:** top models max out at 5.0 on Tool/Numeric → "parity" here means "both ace - THESE tasks". Harder cases are needed to truly separate quality. -- **Judges are secondary:** without a neutral vendor, the primary soft score is the panel mean - (advisory, self-preference possible). The comparison rests on the key metrics. +- **Case ceiling:** top models max out at 5.0 on Tool/Numeric → "parity" here means "both ace THESE tasks". Harder cases are needed to truly separate quality. +- **Judges are secondary:** without a neutral vendor, the primary soft score is the panel mean (advisory, self-preference possible). The comparison rests on the key metrics. - Model rates and cache multipliers are from price lists; verify against billing. - `glm-5`/`gpt-5`: availability ≠ identity of the expected model — verify. - `--repeat` is a coarse noise flag; "wrong number == missing" in numeric is a simplification. diff --git a/README.md b/README.md index cc24856..4e84f07 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,11 @@ **🇷🇺 Русский** · [🇬🇧 English](README.en.md) -Самостоятельный харнесс, чтобы **гонять модели против MCP-тулз askads** (Яндекс Директ / -VK Ads / Метрика) и **бенчить их между собой** на нашем домене (русская рекламная аналитика + -многошаговый tool-use), а не на чужих кодинг-бенчах. +Самостоятельный харнесс, чтобы **гонять модели против MCP-тулз askads** (Яндекс Директ / VK Ads / Метрика) и **бенчить их между собой** на нашем домене (русская рекламная аналитика + многошаговый tool-use), а не на чужих кодинг-бенчах. Два режима: -- **`fixed`** — детерминированные фикстуры (замороженный фейк-кабинет как `tool_result`). - Воспроизводимое сравнение моделей, гоняется в CI без сети/ключей кабинета. -- **`live`** — спавн РЕАЛЬНЫХ MCP-серверов по stdio (`mcp-yandex-direct` и т.д.) с токенами - из env. Интеграционные тесты самих тулов на живом кабинете. +- **`fixed`** — детерминированные фикстуры (замороженный фейк-кабинет как `tool_result`). Воспроизводимое сравнение моделей, гоняется в CI без сети/ключей кабинета. +- **`live`** — спавн РЕАЛЬНЫХ MCP-серверов по stdio (`mcp-yandex-direct` и т.д.) с токенами из env. Интеграционные тесты самих тулов на живом кабинете. Движок и MCP-клиент **развязаны от askads** (вынесены в `llmbench/`), репозиторий автономен. @@ -32,11 +28,7 @@ VK Ads / Метрика) и **бенчить их между собой** на ## Результаты последнего прогона -**[`results/2026-07-03/`](results/2026-07-03/results.ru.md)** (+ англ. `results.en.md`) — свежий -полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном -скоринге: Топ-3 + Pareto-фронт + выводы. Кратко: **GLM-4.6 без thinking** — лучший «качество/цена», -**GPT-4.1** неожиданно силён и дёшев, **Opus 4.8 (adaptive/high)** — потолок качества; **Sonnet -(прод)** просел на краевых кейсах. Прогон `results/2026-06-29/` — исторический, до правок скоринга. +**[`results/2026-07-03/`](results/2026-07-03/results.ru.md)** (+ англ. `results.en.md`) — свежий полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном скоринге: Топ-3 + Pareto-фронт + выводы. Кратко: **GLM-4.6 без thinking** — лучший «качество/цена», **GPT-4.1** неожиданно силён и дёшев, **Opus 4.8 (adaptive/high)** — потолок качества; **Sonnet (прод)** просел на краевых кейсах. Прогон `results/2026-06-29/` — исторический, до правок скоринга. ## Что и кто считает @@ -114,31 +106,21 @@ Live-режим делает префлайт (токены + наличие с | `--report-from ` | пересобрать отчёт (ru+en) из `runs.jsonl` бесплатно | | `--resume ` | догнать прерванный прогон (только недостающие/упавшие ключи) | -Список вариантов (модель × thinking/effort/reasoning) — в `llmbench/runner.py`; добавить -модель = одна строка (не забудь тариф в `core.MODEL_RATES`, иначе ранер предупредит). +Список вариантов (модель × thinking/effort/reasoning) — в `llmbench/runner.py`; добавить модель = одна строка (не забудь тариф в `core.MODEL_RATES`, иначе ранер предупредит). ## Артефакты и пере-скоринг -Каждый прогон складывается в дата-папку **`results//`**: сырой `runs.jsonl` (по записи -на прогон — ответ, tool-трейс, usage, все оценки, ошибки) и двуязычный авто-отчёт -`results.ru.md` + `results.en.md`. `runs.jsonl` — источник правды: отчёт (обе версии) -пересобирается из него бесплатно, без повторных вызовов моделей — +Каждый прогон складывается в дата-папку **`results//`**: сырой `runs.jsonl` (по записи на прогон — ответ, tool-трейс, usage, все оценки, ошибки) и двуязычный авто-отчёт `results.ru.md` + `results.en.md`. `runs.jsonl` — источник правды: отчёт (обе версии) пересобирается из него бесплатно, без повторных вызовов моделей — ```bash python -m llmbench.runner --report-from results/2026-07-03/runs.jsonl ``` -`runs.jsonl` **под VCS для fixed-прогонов** (фейк-кабинет, приватных данных нет; ~230 КБ в паке). -⚠️ **Live-прогоны содержат данные реального кабинета — их `runs.jsonl` не коммить** (локально/приватный -S3). Отчёты `.md` — под VCS. Топ-3, прозу и выводы дописывают руками поверх сгенерированного грида в -той же дата-папке. Второй прогон за день не затирает первый (при коллизии по дате к папке добавляется время). +`runs.jsonl` **под VCS для fixed-прогонов** (фейк-кабинет, приватных данных нет; ~230 КБ в паке). ⚠️ **Live-прогоны содержат данные реального кабинета — их `runs.jsonl` не коммить** (локально/приватный S3). Отчёты `.md` — под VCS. Топ-3, прозу и выводы дописывают руками поверх сгенерированного грида в той же дата-папке. Второй прогон за день не затирает первый (при коллизии по дате к папке добавляется время). -**Прервался (обнулился баланс, Ctrl-C)?** Готовые прогоны уже в `runs.jsonl` (пишется построчно -по мере готовности) — долей баланс и догони остаток, платя только за него: +**Прервался (обнулился баланс, Ctrl-C)?** Готовые прогоны уже в `runs.jsonl` (пишется построчно по мере готовности) — долей баланс и догони остаток, платя только за него: ```bash RUN_BENCH=1 …ключи… python -m llmbench.runner --resume results/2026-07-03/runs.jsonl ``` -`--resume` пропускает уже успешные `(вариант, кейс, повтор)`, гоняет только упавшие/недостающие, -дописывает в тот же файл и пересобирает отчёт (при дублях успех побеждает). Можно и добавить -ключ (напр. `OPENAI_API_KEY`) и догнать варианты, пропущенные в исходном прогоне. +`--resume` пропускает уже успешные `(вариант, кейс, повтор)`, гоняет только упавшие/недостающие, дописывает в тот же файл и пересобирает отчёт (при дублях успех побеждает). Можно и добавить ключ (напр. `OPENAI_API_KEY`) и догнать варианты, пропущенные в исходном прогоне. ## Как считается Score @@ -155,10 +137,8 @@ RUN_BENCH=1 …ключи… python -m llmbench.runner --resume results/2026-07- ## Известные ограничения -- **Потолок кейсов:** топ-модели упираются в 5.0 по Tool/Numeric → «паритет» здесь = «оба - отлично решают ЭТИ задачи». Для строгого различения качества нужны более трудные кейсы. -- **Судьи вторичны:** без нейтрального вендора первичный мягкий балл = среднее панели - (advisory, возможна self-preference). Вес сравнения — на ключевых метриках. +- **Потолок кейсов:** топ-модели упираются в 5.0 по Tool/Numeric → «паритет» здесь = «оба отлично решают ЭТИ задачи». Для строгого различения качества нужны более трудные кейсы. +- **Судьи вторичны:** без нейтрального вендора первичный мягкий балл = среднее панели (advisory, возможна self-preference). Вес сравнения — на ключевых метриках. - Ставки/кэш-множители моделей — по прайс-листам; сверить с биллингом. - `glm-5`/`gpt-5`: доступность ≠ идентичность ожидаемой модели — сверить. - `--repeat` — грубый флаг шума; «неверное число == пропущенное» в numeric — упрощение. diff --git a/results/2026-07-03/results.en.md b/results/2026-07-03/results.en.md index 69b7b44..cd410df 100644 --- a/results/2026-07-03/results.en.md +++ b/results/2026-07-03/results.en.md @@ -8,32 +8,17 @@ _Run from 2026-07-03 12:39 UTC × **16 variants** (model × thinking/effort) × ## Terms (how to read the table) -- **Accuracy** (0–5) — numeric correctness: are CTR/CPC/CPA/spend computed right, nothing made - up, and are the numbers attributed to the right campaign (entity anchoring). **In code** - (deterministic). -- **Tools Use** (0–5) — tool correctness: called the right tools (successfully) in the right - order, nothing extra/forbidden. **Code**. -- **Edge Cases** (0–5) — behavior in edge cases (empty report, refusing to change a bid, - clarifying). **LLM judges** — they also score runs with tool violations. +- **Accuracy** (0–5) — numeric correctness: are CTR/CPC/CPA/spend computed right, nothing made up, and are the numbers attributed to the right campaign (entity anchoring). **In code** (deterministic). +- **Tools Use** (0–5) — tool correctness: called the right tools (successfully) in the right order, nothing extra/forbidden. **Code**. +- **Edge Cases** (0–5) — behavior in edge cases (empty report, refusing to change a bid, clarifying). **LLM judges** — they also score runs with tool violations. - **Lang quality** (0–5) — naturalness and clarity of the Russian. Judges. -- **Score** (0–5) — a run's overall score = mean of the available components: Tools Use - (always), Accuracy (if the case has golden facts), Edge Cases/Lang quality (if judges ran). - The component set depends on the case, so Score is comparable across variants (everyone runs - the same cases) but is NOT equal to the mean of the four left columns. Failed runs are - excluded from Score — see Err. -- **Cost per Answer** — mean cost of a successful run (USD); **Score per USD (s/m)** — "quality - per dollar" (Score ÷ cost) for single-/multi-step dialogs; higher = better value. -- **Stability** (0–5) — `5 − mean spread (σ) of Score between repeats of the same case`: - higher = more stable. Meaningful at repeat ≥ 2. -- **Err** — `failed/all runs` (API errors, token-limit truncation); suffix `·NR` — N runs - succeeded only after a retry with the same config. Failed runs are excluded from all metrics, - but their cost is included in the total run cost. -- **Thinking** — whether the model thinks before answering: `adaptive` (Claude/GLM), - `reasoning` (GPT-5), `no`. -- **Effort** — the "effort" budget per answer (`low/medium/high/max`); separate from thinking - (weak effect when thinking is off). Not configurable for GLM (`—`). -- **⭐** — **best quality/price balance**: a variant that can't be beaten — no other is both - better and cheaper. _(In optimization — the "Pareto frontier".)_ +- **Score** (0–5) — a run's overall score = mean of the available components: Tools Use (always), Accuracy (if the case has golden facts), Edge Cases/Lang quality (if judges ran). The component set depends on the case, so Score is comparable across variants (everyone runs the same cases) but is NOT equal to the mean of the four left columns. Failed runs are excluded from Score — see Err. +- **Cost per Answer** — mean cost of a successful run (USD); **Score per USD (s/m)** — "quality per dollar" (Score ÷ cost) for single-/multi-step dialogs; higher = better value. +- **Stability** (0–5) — `5 − mean spread (σ) of Score between repeats of the same case`: higher = more stable. Meaningful at repeat ≥ 2. +- **Err** — `failed/all runs` (API errors, token-limit truncation); suffix `·NR` — N runs succeeded only after a retry with the same config. Failed runs are excluded from all metrics, but their cost is included in the total run cost. +- **Thinking** — whether the model thinks before answering: `adaptive` (Claude/GLM), `reasoning` (GPT-5), `no`. +- **Effort** — the "effort" budget per answer (`low/medium/high/max`); separate from thinking (weak effect when thinking is off). Not configurable for GLM (`—`). +- **⭐** — **best quality/price balance**: a variant that can't be beaten — no other is both better and cheaper. _(In optimization — the "Pareto frontier".)_ ## Top-3 (best quality/price balance) @@ -95,4 +80,4 @@ Same caveat as before: these tasks are still easy for the top models (Accuracy/T - **No independent judge**: answers are scored by the same companies whose models are compared — possible self-model inflation; judge scores are auxiliary, weight is on the key metrics Tools Use/Accuracy (computed in code). - Mode `fixed`: models see clean test data (fixtures), not the "messy" real API output (use `--mode live` for that). -_Raw per-run data: `results/2026-07-03/runs.jsonl` — the report is rebuilt from it with `python -m llmbench.runner --report-from `._ \ No newline at end of file +_Raw per-run data: `results/2026-07-03/runs.jsonl` — the report is rebuilt from it with `python -m llmbench.runner --report-from `._ diff --git a/results/2026-07-03/results.ru.md b/results/2026-07-03/results.ru.md index d7809fb..fa16d54 100644 --- a/results/2026-07-03/results.ru.md +++ b/results/2026-07-03/results.ru.md @@ -8,29 +8,17 @@ _Запуск от 2026-07-03 12:39 UTC × **16 вариантов** (модел ## Термины (как читать таблицу) -- **Accuracy** (0–5) — точность чисел: верно ли посчитаны CTR/CPC/CPA/расход, не выдуманы ли - цифры и той ли кампании они приписаны (entity-анкоринг). **В коде** (детерминированно). -- **Tools Use** (0–5) — корректность инструментов: вызвал нужные тулы (успешно) в нужном - порядке, без лишних/запрещённых. **Код**. -- **Edge Cases** (0–5) — поведение в краевых случаях (пустой отчёт, отказ менять ставку, - уточнение). **LLM-судьи** — оценивают и прогоны с нарушениями по тулам. +- **Accuracy** (0–5) — точность чисел: верно ли посчитаны CTR/CPC/CPA/расход, не выдуманы ли цифры и той ли кампании они приписаны (entity-анкоринг). **В коде** (детерминированно). +- **Tools Use** (0–5) — корректность инструментов: вызвал нужные тулы (успешно) в нужном порядке, без лишних/запрещённых. **Код**. +- **Edge Cases** (0–5) — поведение в краевых случаях (пустой отчёт, отказ менять ставку, уточнение). **LLM-судьи** — оценивают и прогоны с нарушениями по тулам. - **Lang quality** (0–5) — естественность и ясность русского. Судьи. -- **Score** (0–5) — сводный балл прогона = среднее доступных компонент: Tools Use (всегда), - Accuracy (если у кейса есть golden-факты), Edge Cases/Lang quality (если судьи работали). - Состав компонент зависит от кейса, поэтому Score сравним между вариантами (кейсы у всех - одни), но НЕ равен среднему четырёх колонок слева. Упавшие прогоны в Score не входят — см. Err. -- **Cost per Answer** — средняя стоимость успешного прогона (USD); **Score per USD (s/m)** — - «качество на доллар» (Score ÷ цена) для одно-/многошаговых диалогов; выше = выгоднее. -- **Stability** (0–5) — `5 − средний разброс (σ) Score между повторами одного кейса`: - выше = стабильнее. Осмысленна при repeat ≥ 2. -- **Err** — `упавшие/все прогоны` (ошибки API, обрезка лимитом токенов); суффикс `·NR` — - N прогонов удались только после повтора тем же конфигом. Упавшие прогоны исключены из - всех метрик, но их стоимость входит в полную стоимость прогона. +- **Score** (0–5) — сводный балл прогона = среднее доступных компонент: Tools Use (всегда), Accuracy (если у кейса есть golden-факты), Edge Cases/Lang quality (если судьи работали). Состав компонент зависит от кейса, поэтому Score сравним между вариантами (кейсы у всех одни), но НЕ равен среднему четырёх колонок слева. Упавшие прогоны в Score не входят — см. Err. +- **Cost per Answer** — средняя стоимость успешного прогона (USD); **Score per USD (s/m)** — «качество на доллар» (Score ÷ цена) для одно-/многошаговых диалогов; выше = выгоднее. +- **Stability** (0–5) — `5 − средний разброс (σ) Score между повторами одного кейса`: выше = стабильнее. Осмысленна при repeat ≥ 2. +- **Err** — `упавшие/все прогоны` (ошибки API, обрезка лимитом токенов); суффикс `·NR` — N прогонов удались только после повтора тем же конфигом. Упавшие прогоны исключены из всех метрик, но их стоимость входит в полную стоимость прогона. - **Thinking** — думает ли модель перед ответом: `adaptive` (Claude/GLM), `reasoning` (GPT-5), `нет`. -- **Effort** — бюджет «усилий» на ответ (`low/medium/high/max`); отдельная от thinking - настройка (при выключенном thinking влияет слабо). У GLM не настраивается (`—`). -- **⭐** — **лучший баланс «качество/цена»**: вариант, который нельзя «побить» — нет другого, - который и качественнее, и дешевле. _(В оптимизации — «Pareto-фронт».)_ +- **Effort** — бюджет «усилий» на ответ (`low/medium/high/max`); отдельная от thinking настройка (при выключенном thinking влияет слабо). У GLM не настраивается (`—`). +- **⭐** — **лучший баланс «качество/цена»**: вариант, который нельзя «побить» — нет другого, который и качественнее, и дешевле. _(В оптимизации — «Pareto-фронт».)_ ## Топ-3 (лучший баланс «качество/цена») @@ -92,4 +80,4 @@ _Для ориентира: текущий прод askads — Sonnet 4.6 (think - **Независимого судьи нет**: ответы оценивают те же компании, чьи модели и сравниваются — возможно завышение «своей» модели; оценки судей вспомогательные, вес на ключевых метриках Tools Use/Accuracy (их считает код). - Режим `fixed`: модели видят аккуратные тестовые данные (фикстуры), а не «грязный» реальный вывод API (для этого `--mode live`). -_Сырые per-run данные: `results/2026-07-03/runs.jsonl` — отчёт пересобирается из них командой `python -m llmbench.runner --report-from <файл>`._ \ No newline at end of file +_Сырые per-run данные: `results/2026-07-03/runs.jsonl` — отчёт пересобирается из них командой `python -m llmbench.runner --report-from <файл>`._ From ff9ed3cad4eebd01fd3fd26e1af269c6dcf63db5 Mon Sep 17 00:00:00 2001 From: Aleksandr Kovalko Date: Fri, 3 Jul 2026 17:19:51 +0200 Subject: [PATCH 4/4] =?UTF-8?q?docs(README):=20=D1=82=D0=BE=D0=BF-3=20?= =?UTF-8?q?=D1=82=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D0=B0,=20Score=20=D0=BF?= =?UTF-8?q?=D0=BE=D1=81=D0=BB=D0=B5=20=D0=A2=D0=B5=D1=80=D0=BC=D0=B8=D0=BD?= =?UTF-8?q?=D1=8B,=20=D1=87=D0=B8=D1=81=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Термины: 'код'→'Оценивается кодом', 'судьи'→'Оценивается LLM-судьями'; ⭐ без жаргона 'Pareto'. - 'Как считается Score' перенесён сразу после 'Термины'. - 'Результаты последнего прогона' + таблица топ-3 (GLM-4.6 / GPT-4.1 / Opus 4.8) с Score и ценой. - Удалён раздел 'Что и кто считает' (дублировал таблицу Термины). - Env-таблица: YANDEX_DIRECT_LOGIN на первой строке рядом с токеном. --- README.en.md | 55 ++++++++++++++++++++++++++-------------------------- README.md | 55 ++++++++++++++++++++++++++-------------------------- 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/README.en.md b/README.en.md index cde7c83..e323ee6 100644 --- a/README.en.md +++ b/README.en.md @@ -16,29 +16,42 @@ How to read the results table: | Metric | What | Computed by | |---|---|---| -| **Accuracy** | number correctness (CTR/CPC/CPA/spend) + right campaign attribution (entity anchoring), nothing invented | code | -| **Tools Use** | right tools called successfully, in the right order, nothing extra/forbidden | code | -| **Edge Cases** | behavior in edge cases (empty report, refusing a bid change, clarifying) | judges | -| **Lang quality** | naturalness and clarity of the Russian | judges | +| **Accuracy** | number correctness (CTR/CPC/CPA/spend) + right campaign attribution (entity anchoring), nothing invented | Scored by code | +| **Tools Use** | right tools called successfully, in the right order, nothing extra/forbidden | Scored by code | +| **Edge Cases** | behavior in edge cases (empty report, refusing a bid change, clarifying) | Scored by LLM judges | +| **Lang quality** | naturalness and clarity of the Russian | Scored by LLM judges | | **Score** | a run's overall score = mean of the available components (see "How Score is computed") | — | | **Cost per Answer** | mean cost of one answer, USD | — | | **Score per USD** | "quality per dollar" (Score ÷ cost); higher = better value | — | | **Stability** | `5 − spread of Score between repeats of a case`; higher = more stable | — | -| **⭐ (Pareto)** | best quality/price balance — a variant that can't be both beaten and undercut | — | +| **⭐** | best quality/price balance: no other variant is both better and cheaper | — | + +## How Score is computed + +A run's `Score` is the **mean of the available components** (not all four columns at once): + +- **Tools Use** — always; +- **Accuracy** — if the case has golden facts (numeric cases); +- **Edge Cases** and **Lang quality** — if judges ran. + +The component set depends on the case, so: +- Score is **comparable** across variants (everyone runs the same cases) but is **not equal** to the mean of the four report columns; +- **failed runs** (API errors, token-limit truncation) are excluded from metrics — they show in a separate `Err` column; +- **`Stability = 5 − mean spread (σ) of Score between repeats of the same case`** — it captures model noise, not case difficulty. ## Latest run results -**[`results/2026-07-03/`](results/2026-07-03/results.en.md)** (+ Russian `results.ru.md`) — a fresh full grid (16 variants × 9 cases × 3 repeats = 432 runs, 0 errors, ≈ $18) on the fixed scoring: Top-3 + Pareto frontier + takeaways. In short: **GLM-4.6 without thinking** is the best quality/price, **GPT-4.1** is surprisingly strong and cheap, **Opus 4.8 (adaptive/high)** is the quality ceiling; **Sonnet (production)** drops on edge cases. The `results/2026-06-29/` run is historical, before the scoring fixes. +**[`results/2026-07-03/`](results/2026-07-03/results.en.md)** (+ Russian `results.ru.md`) — a fresh full grid (16 variants × 9 cases × 3 repeats = 432 runs, 0 errors, ≈ $18) on the fixed scoring. -## What is scored, and by whom +Top-3 by quality/price balance (⭐): -| Dimension | By | Where | -|---|---|---| -| **Tool-Use** (right tools succeed / order / call cap) | code | `scoring.score_tooluse` | -| **Numeric-Accuracy** (number correctness + entity anchoring, "don't invent CPA") | code | `scoring.score_numeric` | -| Interpretation / Russian / Edge handling | judge panel | `judges.py` | +| LLM | Thinking | Score | Cost/answer | Why it wins | +|---|---|--:|--:|---| +| **GLM-4.6** | no | 4.62 | $0.003 | best quality/price, ~27× cheaper than Opus | +| **GPT-4.1** | no | 4.93 | $0.013 | surprisingly strong and cheap | +| **Opus 4.8** | adaptive/high | 4.96 | $0.081 | quality ceiling | -Key metrics are the backbone of the comparison; judges are strictly secondary. +**Sonnet 4.6 (current production)** drops on edge cases (Edge ~3.3 — invents causes on an empty slice, doesn't clarify an ambiguous question). The `results/2026-06-29/` run is historical, before the scoring fixes. ## Layout @@ -83,8 +96,7 @@ Account-token env (for `--mode live`): | Env | Purpose | |---|---| -| `YANDEX_DIRECT_TOKEN` | Yandex Direct token (required for live) | -| `YANDEX_DIRECT_LOGIN` | optional — account login if the token is an agency token | +| `YANDEX_DIRECT_TOKEN` (+ opt. `YANDEX_DIRECT_LOGIN`) | Yandex Direct token — required for live; LOGIN if the token is an agency token | | `YANDEX_METRIKA_TOKEN` | Metrica token (for metrika cases) | | `VK_ADS_TOKEN` | VK Ads token | | `MCP_PATH_YANDEX_DIRECT` | optional — override the MCP server path (`/path/to/dist/index.js`) | @@ -122,19 +134,6 @@ RUN_BENCH=1 …keys… python -m llmbench.runner --resume results/2026-07-03/run ``` `--resume` skips already-successful `(variant, case, repeat)` keys, runs only the failed/missing ones, appends to the same file, and rebuilds the report (on duplicates, success wins). You can also add a key (e.g. `OPENAI_API_KEY`) and backfill variants skipped in the original run. -## How Score is computed - -A run's `Score` is the **mean of the available components** (not all four columns at once): - -- **Tools Use** — always; -- **Accuracy** — if the case has golden facts (numeric cases); -- **Edge Cases** and **Lang quality** — if judges ran. - -The component set depends on the case, so: -- Score is **comparable** across variants (everyone runs the same cases) but is **not equal** to the mean of the four report columns; -- **failed runs** (API errors, token-limit truncation) are excluded from metrics — they show in a separate `Err` column; -- **`Stability = 5 − mean spread (σ) of Score between repeats of the same case`** — it captures model noise, not case difficulty. - ## Known limitations - **Case ceiling:** top models max out at 5.0 on Tool/Numeric → "parity" here means "both ace THESE tasks". Harder cases are needed to truly separate quality. diff --git a/README.md b/README.md index 4e84f07..99b0741 100644 --- a/README.md +++ b/README.md @@ -16,29 +16,42 @@ | Метрика | Что | Кто считает | |---|---|---| -| **Accuracy** | точность чисел (CTR/CPC/CPA/расход) + верная привязка к кампании (entity-анкоринг), без выдумок | код | -| **Tools Use** | нужные тулы вызваны успешно, в нужном порядке, без лишних/запрещённых | код | -| **Edge Cases** | поведение в краевых случаях (пустой отчёт, отказ менять ставку, уточнение) | судьи | -| **Lang quality** | естественность и ясность русского | судьи | +| **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 между повторами кейса`; выше = стабильнее | — | -| **⭐ (Pareto)** | лучший баланс «качество/цена» — вариант, который нельзя и превзойти, и удешевить | — | +| **⭐** | лучший баланс «качество/цена»: нет варианта, который был бы и качественнее, и дешевле одновременно | — | + +## Как считается Score + +`Score` одного прогона — **среднее доступных компонент** (не всех четырёх колонок сразу): + +- **Tools Use** — всегда; +- **Accuracy** — если у кейса есть golden-факты (числовые кейсы); +- **Edge Cases** и **Lang quality** — если работали судьи. + +Состав компонент зависит от кейса, поэтому: +- Score **сравним** между вариантами (кейсы у всех одни), но **не равен** среднему четырёх колонок отчёта; +- **упавшие прогоны** (ошибки API, обрезка лимитом токенов) в метрики не входят — они в отдельной колонке `Err`; +- **`Stability = 5 − средний разброс (σ) Score между повторами одного кейса`** — ловит шум модели, а не разницу сложности кейсов. ## Результаты последнего прогона -**[`results/2026-07-03/`](results/2026-07-03/results.ru.md)** (+ англ. `results.en.md`) — свежий полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном скоринге: Топ-3 + Pareto-фронт + выводы. Кратко: **GLM-4.6 без thinking** — лучший «качество/цена», **GPT-4.1** неожиданно силён и дёшев, **Opus 4.8 (adaptive/high)** — потолок качества; **Sonnet (прод)** просел на краевых кейсах. Прогон `results/2026-06-29/` — исторический, до правок скоринга. +**[`results/2026-07-03/`](results/2026-07-03/results.ru.md)** (+ англ. `results.en.md`) — свежий полный грид (16 вариантов × 9 кейсов × 3 повтора = 432 прогона, 0 ошибок, ≈ $18) на исправленном скоринге. -## Что и кто считает +Топ-3 по балансу «качество/цена» (⭐): -| Измерение | Кто | Где | -|---|---|---| -| **Tool-Use** (нужные тулы успешно/порядок/кап) | код | `scoring.score_tooluse` | -| **Numeric-Accuracy** (точность чисел + entity-анкоринг, «не выдумывать CPA») | код | `scoring.score_numeric` | -| Интерпретация / Русский / Краевое | панель судей | `judges.py` | +| 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/` — исторический, до правок скоринга. ## Структура @@ -83,8 +96,7 @@ Env токенов кабинета (для `--mode live`): | Env | Назначение | |---|---| -| `YANDEX_DIRECT_TOKEN` | токен Яндекс Директа (обязателен для live) | -| `YANDEX_DIRECT_LOGIN` | опц. — логин кабинета, если токен агентский | +| `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`) | @@ -122,19 +134,6 @@ RUN_BENCH=1 …ключи… python -m llmbench.runner --resume results/2026-07- ``` `--resume` пропускает уже успешные `(вариант, кейс, повтор)`, гоняет только упавшие/недостающие, дописывает в тот же файл и пересобирает отчёт (при дублях успех побеждает). Можно и добавить ключ (напр. `OPENAI_API_KEY`) и догнать варианты, пропущенные в исходном прогоне. -## Как считается Score - -`Score` одного прогона — **среднее доступных компонент** (не всех четырёх колонок сразу): - -- **Tools Use** — всегда; -- **Accuracy** — если у кейса есть golden-факты (числовые кейсы); -- **Edge Cases** и **Lang quality** — если работали судьи. - -Состав компонент зависит от кейса, поэтому: -- Score **сравним** между вариантами (кейсы у всех одни), но **не равен** среднему четырёх колонок отчёта; -- **упавшие прогоны** (ошибки API, обрезка лимитом токенов) в метрики не входят — они в отдельной колонке `Err`; -- **`Stability = 5 − средний разброс (σ) Score между повторами одного кейса`** — ловит шум модели, а не разницу сложности кейсов. - ## Известные ограничения - **Потолок кейсов:** топ-модели упираются в 5.0 по Tool/Numeric → «паритет» здесь = «оба отлично решают ЭТИ задачи». Для строгого различения качества нужны более трудные кейсы.