{
  "title": "Бенчмарк LLM: format() без format()",
  "headline": "format() без format()",
  "date": "29-10-2026",
  "tests_total": 502,
  "perf_total": 2,
  "oracle": "встроенная format() CPython 3.12",
  "oracle_note": "CPython 3.12.7",
  "lede": "Модели пишут на Python функцию `fmt(value, spec)`, которая для целых и дробных чисел символ в символ повторяет встроенную `format()`, но без неё самой, без f-строк, `%` и `repr`. Задание короткое, а ловушек много: округление по точному двоичному значению, заполнение нулями вместе с разделителями разрядов, флаг `z`, NaN, бесконечности и разбор строки формата. Ответ сравнивается с настоящим CPython на 502 случаях и двумя замерами скорости.",
  "segments": {
    "hand": "ручных ловушек",
    "rand": "случайных форматов",
    "err": "форматов с ошибкой"
  },
  "task": {
    "name": "функция fmt(value, spec), повторяющая format() для чисел",
    "file": "task/task.md",
    "summary": "Нужно повторить мини-язык формата Python для `int` и `float`: заполнение и выравнивание, знак, флаги `z`, `#` и `0`, ширину, разделители `,` и `_`, точность и типы `d`, `b`, `o`, `x`, `X`, `e`, `E`, `f`, `F`, `g`, `G`, `%`. Где `format` бросает `ValueError`, функция тоже должна его бросить. Встроенное форматирование запрещено, разрешены `math`, `fractions`, `float.as_integer_ratio()` и `str()` от целого.",
    "checks": [
      "85 ручных ловушек. Среди них округление половины к чётному по точному значению (`2.675` с `.2f` даёт `2.67`), перенос порядка при округлении, денормализованные числа с точностью до 1100 знаков, граница записи у `g`, заполнение нулями с разделителями шире заданной ширины (`fmt(1234, '08,')` даёт `0,001,234`), `#` как символ заполнения, флаг `z` у нуля, но не у `-inf`, NaN без знака, `%`, который умножает во float и поэтому превращает `1e308` в `inf%`, и целые, которые для дробных типов сначала переводятся во float.",
      "400 случайных форматов из генератора с фиксированным seed: случайные заполнение, выравнивание, знак, флаги, ширина, разделитель, точность и тип на целых до 10^40 и на дробных, включая числа рядом с половинами при округлении и точные двоичные дроби.",
      "17 строк формата, которые обязаны закончиться `ValueError`: точность у целого, запятая с `x` и `b`, оба разделителя, `z` у целого типа, флаги не по порядку, неизвестный тип, точка без точности, разделитель после точности и лишние символы.",
      "2 замера скорости, на каждый 1 секунда: 20 000 вызовов со случайными форматами и 600 чисел с точностью около 1000 знаков. Своя реализация стенда без `format()` укладывается в 0,06 и 0,01 секунды.",
      "Ограничения проверяются по коду решения (f-строки, вызовы `format` и `repr`, `%` для строк, запрещённые модули), а `builtins.format` на время прогона подменяется заглушкой. Нарушение даёт ноль.",
      "Сам стенд проверен дважды: обёрткой над настоящей `format()` и своей реализацией без неё, обе дают 502 из 502 и проходят замеры."
    ]
  },
  "conditions": [
    "Участвуют те же модели, что в прошлых тестах. Каждая получила задание слово в слово, одним сообщением, без системного промпта и без инструментов, и не могла запустить свой код.",
    "Параметры рассуждения такие же, как раньше: у Qwen включён `enable_thinking`, у GPT `reasoning_effort: high`, Opus через API получал `thinking: adaptive` и `effort: max` через нативный `/messages` партнёрского шлюза, Kimi и DeepSeek работали в режиме по умолчанию.",
    "Opus 5.5 дополнительно решал задачу агентом Claude Code с чистым контекстом, дважды: с уровнем рассуждения по умолчанию и с `effort: max`. Протокол один: одно чтение задания и одна запись решения, без запуска кода.",
    "Задание заканчивается фразой «Сначала продумай решение, затем ответь модулем», которая не отключает размышление у адаптивных моделей.",
    "В зачёт идёт код как есть. Вариант с правкой считается отдельно, только для понимания."
  ],
  "nuances": [
    "Стенд проверен двумя способами: обёрткой над настоящей `format()` и своей реализацией без неё. Обе дают 502 из 502, своя укладывается в замеры за 0,06 и 0,01 секунды. При её написании мы сами попались на две ловушки, `\"\" in \"boxX\"` и `z` у `-inf`, обе вошли в ручные тесты.",
    "Замер скорости засчитывается, только если все ответы в нём верны. Медленных решений не было: все правильные укладываются в сотые доли секунды, а замеры проваливаются из-за неверных ответов.",
    "Запрет на встроенное форматирование проверяется по коду и подменой `builtins.format`, а `str()` от float проверен отдельно подменой `str` на 3000 случаях: так не делает ни одно решение.",
    "Первая попытка GPT-6.1 Sol оборвалась на шлюзе через 298 секунд без ответа, засчитана вторая.",
    "Время и токены Opus-агентов оценочные: время по журналу агента, выходные токены по счётчикам API, рассуждение как выходные токены за вычетом кода.",
    "Kimi K3.1 Pro исключён: Moonshot был перегружен с 10:05 до последней проверки в 12:21."
  ],
  "findings": [
    [
      "Без единой ошибки справился только Opus агентом.",
      "Оба агентских прогона, с уровнем рассуждения по умолчанию и с `effort: max`, прошли 502 случая из 502 и оба замера. Ближе всех из моделей через API DeepSeek Flash с 478, у GPT-6.1 Sol 422, у Qwen3.8-Max 402."
    ],
    [
      "Через шлюз Opus думает в разы меньше и нарушил запрет.",
      "Opus через API с `effort: max` рассуждал 9,5 тысячи токенов против 55 и 240 тысяч у агентов на той же модели и отформатировал порядок числа f-строкой, которую задание запрещает. В зачёт идёт ноль, с правкой было бы 447."
    ],
    [
      "Главные ловушки: нули с разделителями, ноль при явном выравнивании и проценты.",
      "`fmt(1234, '08,')` в Python даёт `0,001,234`, шире заданной ширины, и это не угадала ни одна модель через API. `<05` у всех, кроме агентов, дал пробелы вместо нулей. DeepSeek и GPT умножают на 100 точно, а CPython умножает во float, поэтому `1e308` с `%` даёт `inf%`."
    ],
    [
      "Короткое задание различает модели лучше длинного.",
      "Спецификация занимает одну страницу, но результаты разошлись от 402 до 502. В прошлых тестах сильные модели почти все проходили 100%, и места решала скорость кода."
    ],
    [
      "Пустая строка входит в любую строку.",
      "Qwen проверял тип как `typ in \"eEfFgG%\"`, а для пустого типа это истинно, и целое без типа уходило в ветку для float. Ровно на это же мы наткнулись в своей реализации для стенда."
    ]
  ],
  "files": [
    [
      "README.md",
      "отчёт в формате Markdown"
    ],
    [
      "report.html",
      "страница отчёта с графиками"
    ],
    [
      "bench.json",
      "метаданные: участники, условия, нюансы и выводы, из них собирается отчёт"
    ],
    [
      "task/task.md",
      "задание, которое получили модели"
    ],
    [
      "harness/test_fmt.py",
      "скрытые тесты: ручные ловушки, генератор, проверка ограничений, замеры"
    ],
    [
      "harness/own/solution.py",
      "своя реализация без format(), проверка, что задача решается"
    ],
    [
      "harness/oracle/solution.py",
      "обёртка над настоящей format() для проверки стенда"
    ],
    [
      "answers/<id>.json",
      "метаданные ответа: время и токены"
    ],
    [
      "solutions/<id>/solution.py",
      "код, который написала модель"
    ],
    [
      "grades/<id>.txt",
      "результаты тестов и список падений"
    ]
  ],
  "participants": [
    {
      "id": "opusagent",
      "name": "Claude Opus 5.5 (агент)",
      "vendor": "Anthropic",
      "model": "claude-opus-5-5",
      "access": "субагент Claude Code, чистый контекст, без запуска кода",
      "max_tokens": null,
      "answer": null,
      "grade": "grades/opusagent.txt",
      "solution": "solutions/opusagent/solution.py",
      "seconds": 530.6,
      "out_tokens": 58800,
      "reasoning_tokens": 54800,
      "reasoning_estimated": true,
      "verdict": "502 из 502 и оба замера скорости, 0,06 и 0,01 секунды. Единственный, кто прошёл все ловушки с первого раза: точное округление, нули с разделителями, `z`, NaN, `%` во float и разбор строки формата. Работал вслепую 9 минут, около 55 тысяч токенов рассуждения, код 489 строк. Протокол соблюдён: одно чтение задания и одна запись решения."
    },
    {
      "id": "opusagentmax",
      "name": "Claude Opus 5.5 (агент, effort max)",
      "vendor": "Anthropic",
      "model": "claude-opus-5-5",
      "access": "субагент Claude Code, `effort: max`, чистый контекст, без запуска кода",
      "max_tokens": null,
      "answer": null,
      "grade": "grades/opusagentmax.txt",
      "solution": "solutions/opusagentmax/solution.py",
      "verdict": "Тоже 502 из 502 и оба замера, 0,05 и 0,01 секунды. С `effort: max` агент думал вчетверо дольше, 35 минут и около 240 тысяч токенов, и написал код короче, 456 строк, но результат тот же: задача уже решалась полностью и на обычном уровне. Протокол соблюдён.",
      "seconds": 2124.3,
      "out_tokens": 244900,
      "reasoning_tokens": 241300,
      "reasoning_estimated": true
    },
    {
      "id": "kimi",
      "name": "Kimi K3.1 Pro",
      "vendor": "Moonshot AI",
      "model": "kimi-k3.1-pro",
      "access": "API api.moonshot.ai (через VPN роутера)",
      "max_tokens": 128000,
      "answer": null,
      "grade": null,
      "solution": null,
      "verdict": "Ответа нет: с 10:05 до 12:21 Moonshot отвечал на любой запрос к Kimi K3.1 Pro ошибкой 429 `engine_overloaded`, даже на сообщение из одного слова.",
      "excluded": "Провайдер перегружен: с 10:05 до 12:21 Moonshot отвечал ошибкой 429 `engine_overloaded` на все 10 попыток отправить задание и на все пробные запросы из одного слова. Это сбой доступа, а не модели. Попытки сохранены в `answers/kimi-busy*.json`."
    },
    {
      "id": "qwen",
      "name": "Qwen3.8-Max",
      "vendor": "Alibaba",
      "model": "qwen3.8-max",
      "access": "API token-plan.maas.qwencloudapi.com",
      "max_tokens": 131072,
      "answer": "answers/qwen.json",
      "grade": "grades/qwen.txt",
      "solution": "solutions/qwen/solution.py",
      "verdict": "402 из 502 и один замер из двух. Думал дольше всех, 40 минут и 93 тысячи токенов рассуждения, и попался на ловушку, на которой споткнулись и мы при написании стенда: проверка `typ in \"eEfFgG%\"` для пустого типа истинна, поэтому целое без типа уходит в ветку для float и падает. С правкой этой строки было бы 416. Остальные ошибки настоящие: флаг `z` не работает, у `-inf` с `z` получается `-0.0`, нули с разделителями и заполнение нулями у `inf` неверны.",
      "fix": {
        "passed": 416,
        "perf": "1/2",
        "description": "`if typ in _FLOAT_TYPES` заменено на `if typ and typ in _FLOAT_TYPES`"
      }
    },
    {
      "id": "gpt",
      "name": "GPT-6.1 Sol",
      "vendor": "OpenAI",
      "model": "gpt-6.1-sol",
      "reasoning_hidden": true,
      "access": "партнёрский шлюз, `reasoning_effort: high`",
      "max_tokens": 128000,
      "answer": "answers/gpt.json",
      "grade": "grades/gpt.txt",
      "solution": "solutions/gpt/solution.py",
      "verdict": "422 из 502, оба замера провалены из-за неверных ответов. Как и DeepSeek, считает `%` точно, а не во float. Печатает `-nan` с минусом, теряет `%` у `nan`, ставит на один ноль больше при заполнении нулями с разделителями, не группирует `_` у шестнадцатеричных и на `.0g` даёт `2e+02` вместо `1e+02`. Первая попытка оборвалась на шлюзе через 298 секунд без ответа, вторая заняла 11,7 минуты, код начался только на 624-й секунде."
    },
    {
      "id": "dsflash",
      "name": "DeepSeek Flash (V4.1)",
      "vendor": "DeepSeek",
      "model": "deepseek-flash",
      "access": "API api.deepseek.com",
      "max_tokens": 384000,
      "answer": "answers/dsflash.json",
      "grade": "grades/dsflash.txt",
      "solution": "solutions/dsflash/solution.py",
      "verdict": "478 из 502, лучший результат среди моделей через API, но оба замера скорости провалены: замер засчитывается, только если все ответы в нём верны. `%` умножает число на 100 точно, а не во float, поэтому `1e308` даёт 300 цифр вместо `inf%`, и крупные числа в процентах расходятся в последних знаках. Не учтён `0` при явном выравнивании (`<05` даёт `5    ` вместо `50000`), а при выравнивании `=` нули не ставятся. Ответ за 7,4 минуты, 70 тысяч токенов рассуждения."
    },
    {
      "id": "opusmax",
      "name": "Claude Opus 5.5 (effort max)",
      "vendor": "Anthropic",
      "model": "claude-opus-5-5",
      "access": "партнёрский шлюз, нативный `/v1/messages`, `thinking: adaptive`, `effort: max`",
      "max_tokens": 128000,
      "answer": "answers/opusmax.json",
      "grade": "grades/opusmax.txt",
      "solution": "solutions/opusmax/solution.py",
      "fix": {
        "passed": 447,
        "description": "три f-строки заменены на `str()` и `rjust`, одна из них форматировала порядок числа",
        "perf": "0/2"
      },
      "verdict": "0 из 502: решение нарушает запрет задания. Порядок числа форматируется f-строкой `f\"{abs(exp):02d}\"`, то есть встроенным форматированием, ещё две f-строки стоят в текстах ошибок. Если заменить их, было бы 447 из 502: ломаются наименьшее денормализованное число (деление на ноль), флаг `z`, `+nan`, нули с разделителями и `0` при явном выравнивании. Через шлюз Opus с `effort: max` рассуждал всего 9,5 тысячи токенов и ответил за 2,6 минуты, а агентом на той же модели думал 55 и 240 тысяч."
    }
  ]
}
