← Все статьи

Почему reasoning-модель отвечает пустотой: скрытые thinking-токены съедают весь лимит вывода

Почему reasoning-модель отвечает пустотой: скрытые thinking-токены съедают весь лимит вывода

Три ночи подряд конвейер дайджестов возвращал пустые ответы, хотя каждый запрос завершался «успешно»: в ответе стоял finish_reason: "length" и пустое поле content. Дело оказалось не в сети и не в лимитах аккаунта, а в том, как reasoning-модели считают бюджет вывода: их невидимые thinking-токены тратятся из того же max_tokens, что и видимый ответ. Лимит, которого хватает обычной чат-модели, рассуждающая модель сжигает целиком на внутренние размышления и не пишет ни слова.

Что именно сломалось

Пайплайн был простой: забрать сырые результаты по вакансиям, попросить модель написать дайджест на 200 слов и сохранить текст. В середине недели автор заменил обычную чат-модель на reasoning-модель — расчёт был на более умные и аккуратные пересказы. Вместо этого каждый ответ приходил с finish_reason: "length" и пустым content. Ни ошибки, ни предупреждения об обрезке: запрос формально прошёл, а ответа нет.

Причина — max_tokens. Семьсот токенов с запасом хватает на 200 слов у чат-модели. Но у reasoning-модели в бюджет завершения входят ещё и токены внутренних рассуждений: тысячи невидимых шагов генерируются до первого видимого символа. Лимит в 700 токенов купил примерно ноль рассуждений и ноль ответа — модель выгорела на размышлениях и оборвалась на полуслове. finish_reason при этом честно сообщил length: лимит действительно был достигнут, просто не тот, о котором думал автор.

Лимит вывода и скрытые токены размышления

Как поймать такую поломку

Самое неприятное здесь — тишина. Ошибки нет, HTTP-код 200, ретраи не срабатывают, потому что с точки зрения транспорта всё в порядке. Единственный сигнал — сочетание двух полей: пустой content и finish_reason: "length". Если ваш мониторинг смотрит только на finish_reason или только на статус запроса, он прочитает эту ситуацию как нормальное завершение.

Полезно помнить, что у разных классов моделей одно и то же имя параметра означает разное. OpenAI-совместимый интерфейс прячет эти различия: max_tokens у чат-модели — это почти весь ответ, а у reasoning-модели — ответ плюс длинная цепочка размышлений. Если вы гоняете один и тот же промпт через несколько семейств моделей одним эндпоинтом, стоит отдельно проверить, что каждое поле ответа значит для каждого семейства.

Два исправления, которые остались в коде

  • Бюджет завершения для reasoning-моделей подняли примерно до 4 000 токенов. Дайджесты стали точнее, а счёт почти не вырос: отдельный thinking-токен дешёвый, но в больших объёмах его легко недосчитать.
  • Пустой content вместе с finish_reason: "length" теперь считается полноценной ошибкой, а не тихим успехом. Такой ответ уходит в ретрай и в лог, а не в базу как готовый результат.

Есть и третий, менее очевидный вывод: если вы переводите пайплайн с чат-модели на reasoning-модель, пересчитайте не только цену, но и лимиты вывода. Иначе получите ровно тот же сценарий — формально успешные запросы и пустые строки в базе.

Что показывают наши собственные цифры

Тихие отказы — не редкость и в генеративных сервисах. По агрегатам боевой базы (файл server/seo/benchmarkData.json, без промптов и идентификаторов) за 30 дней, с 4 сентября по 4 октября 2026 года, через платформу прошло 12 689 генераций: 9 907 изображений, 2 334 видео и 448 музыкальных треков. По 3 518 терминальным генерациям модели gpt-image-2 за 90 дней (6 июля — 4 октября 2026) доля успешных составила 92,2%, у seedream-45 — 91,9% из 1 127 генераций. У видео-модели kling-3.0 успешных 79,7% из 374 генераций за тот же период: видео объективно тяжелее, и обрыв по лимиту вывода — лишь один из видов «тихих» отказов, которые не видно по одному только статусу запроса.

Источник разбора — статья «max_tokens=700 on a reasoning model returned empty replies — hidden thinking tokens was the whole budget» в блоге dev.to. Больше практических разборов — в нашем блоге.