Сразу скажу: к самой модели Codex у меня больших претензий нет. На сложных задачах по программированию она работает очень хорошо. Но после нескольких длинных запусков я начала подозревать, что часть проблем с расходом лимитов возникает не на уровне модели, а на уровне Harness — той самой обвязки вокруг модели, которая управляет shell-командами, браузером, MCP, фоновыми процессами, агентами и ожиданием их результатов.
Поводом стали два наблюдения из моего обычного рабочего процесса. А потом я полезла в Issues репозитория Codex и обнаружила, что похожее поведение замечала далеко не только я.
Сначала я случайно получила довольно наглядный «счётчик»
После того как закончился мой недельный лимит, я получила $40 бонусного баланса за приглашения. В обычной подписке довольно сложно понять, сколько именно стоила конкретная задача, а здесь расход стал виден почти буквально в долларах.
У меня есть автоматизированный процесс поиска ключевых слов для блога. В одном случае я работала из Claude Code и вызывала внутри него Codex CLI: Codex исправил сломавшийся workflow, после чего прогнал тесты. На всё это ушло примерно $1.
Позже я запустила уже полный процесс поиска ключевых слов непосредственно через Codex. Оставшиеся на балансе примерно $14 закончились полностью.
Сразу важная оговорка: это не A/B-тест и не доказательство того, что Codex в 14 раз дороже Claude Code. Во втором случае объём работы был больше, плюс использовались Chrome/browser tools. Браузер вполне мог внести заметную часть дополнительных расходов.
Но разница была достаточно большой, чтобы я начала смотреть не только на результат работы модели, но и на то, что происходит между её полезными действиями.
Самое странное оказалось не в браузере, а в ожидании
На длинных задачах я заметила довольно простую разницу в поведении.
Условно Claude Code часто работает так:
запустить долгую операцию
↓
ждать
↓
получить результат
↓
разбудить модель
А Codex во многих случаях выглядит скорее так:
запустить долгую операцию
↓
ждать
↓
разбудить модель
↓
проверить статус
↓
ждать
↓
разбудить модель
↓
проверить статус
↓
...
Сам по себе polling — совершенно нормальная техническая практика. Вопрос в другом: нужно ли для каждого polling cycle снова запускать LLM?
Если wait заканчивается, управление возвращается модели, модель снова получает большой контекст, понимает, что задача ещё выполняется, и вызывает следующий wait, то обычное «подожди ещё немного» внезапно становится полноценным model turn.
На короткой задаче это почти незаметно. Но если build, browser workflow, MCP-вызов или subagent работает 10, 30 или 60 минут, таких пробуждений может накопиться много.
Я решила проверить, обсуждал ли кто-нибудь это в репозитории Codex. Оказалось — да, и некоторые отчёты удивительно хорошо совпадают с тем, что я увидела сама.
Issue #13733: polling фонового процесса снова тащит историю в модель
Первый очень показательный Issue:
Автор разбирал поведение фоновых процессов вроде cargo build и cargo test и пришёл к следующему выводу:
“Each poll = 1 full API request with complete conversation history.”
По описанию автора, процесс запускается через exec_command, после чего Codex получает промежуточный результат. Если процесс ещё не завершился, модель позже вызывает write_stdin, чтобы проверить состояние. Если нового вывода нет, цикл повторяется.
Автор приводит пример: 60-секундный cargo build способен породить около 12 таких polling turns. И чем больше к этому моменту накопилась история разговора, тем дороже становится каждый следующий цикл ожидания.
Здесь важна формулировка: это анализ автора Issue, а не официальное заключение OpenAI. Но сам Issue помечен как bug, а также тегами rate-limits и tool-calls, поэтому проблема как минимум не выглядит совсем надуманной.
Issue #32640: примерно один новый model turn каждые 50 секунд
Ещё ближе к моему случаю оказался:
#32640 — Built-in wait tool capped at ~50s causes MASSIVE token burn on long waits
Автор изучил локальные логи Codex CLI и увидел 12 последовательных вызовов wait примерно за 10 минут. Каждый вызов длился около 50 секунд, после чего управление возвращалось модели и происходил следующий sampling.
Особенно интересна дополнительная проверка с Claude Code. Автор использовал тот же MCP server и тот же server-side blocking wait, но подключил его через другой клиент:
“another agent CLI (Anthropic’s Claude Code) makes ONE blocking MCP call and suspends cleanly for the whole job”
То есть, по наблюдениям автора, Claude Code в этом сценарии просто сделал один блокирующий MCP-вызов и ждал. Codex CLI вместо этого возвращался к модели через короткие интервалы и снова запускал wait.
Именно это очень похоже на разницу, которую я вижу в своих длинных задачах.
Issue #35259: автор намерил 19,8% токенов только на wait/status turns
Самый интересный для меня Issue — более свежий:
Здесь автор не ограничился субъективным ощущением, а разобрал локальную telemetry за полный участок недельного usage cycle. После очистки данных от дублированных snapshots и скопированной истории получилось 10 586 реальных model turns.
Результат:
“model turns whose only tool action was wait/status polling accounted for 19.8% of raw local token volume.”
В эту категорию попали turns, где единственным полезным действием были:
-
wait_agent -
list_agents -
write_stdin -
wait
Всего автор насчитал 1 968 таких turns, на которые пришлось около 273,6 млн raw local tokens, или 19,8% от общего объёма в исследованном окне.
Есть ещё более показательный пример. Один дочерний task выполнялся около 10 часов 55 минут. В нём автор насчитал 613 wait/status-only turns, и на них пришлось примерно 45% raw token volume этого конкретного child task.
Но здесь снова важна оговорка самого автора: raw local tokens — это не то же самое, что фактическое списание подписочной квоты. Это локальная telemetry, а не внутренний billing ledger OpenAI. Поэтому я бы не превращала цифру 19,8% в утверждение «Codex всегда тратит 20% денег впустую».
Тем не менее сам механизм выглядит очень интересно.
Есть и другая сторона проблемы: что именно Harness кладёт обратно в context
Ещё один связанный Issue касается уже не polling, а управления контекстом:
Автор описывает проблему так:
“Token usage grows rapidly during normal coding sessions because raw shell command output is passed directly into the LLM context.”
Речь о выводе git diff, git status, cargo test, docker ps, ls и других команд. Если Harness возвращает модели слишком много сырого и повторяющегося stdout, context растёт быстрее, чем реально растёт количество полезной информации.
Это немного другая проблема, но для меня она относится к той же категории. Цена agentic workflow определяется уже не только интеллектом модели. Очень много зависит от того, насколько аккуратно Harness обращается с контекстом и инструментами.
Почему мне кажется странным будить LLM только для того, чтобы узнать: «уже готово?»
Здесь у меня возникает довольно простой архитектурный вопрос.
Представим, что Codex уже запустил cargo build. Модели не нужно рассуждать каждую минуту о том, завершился ли процесс. Это состояние вполне может отслеживать обычный код.
В идеале схема могла бы выглядеть примерно так:
Model
↓
start_process()
↓
Harness ждёт событие
↓
process exited / new meaningful output
↓
Model
Вместо:
Model
↓
wait(50s)
↓
Model
↓
«ещё работает»
↓
wait(50s)
↓
Model
↓
«ещё работает»
↓
...
Именно похожий вариант предлагает и автор #35259: ожидание процесса или другого агента по возможности должно быть event-driven внутри Harness, а модель стоит будить тогда, когда действительно появилось новое состояние, требующее reasoning.
Это не значит, что polling нужно полностью убрать. Иногда heartbeat или контрольный checkpoint действительно нужен. Но между «раз в несколько минут проверить зависший процесс» и «каждые 30–60 секунд запускать model turn» есть довольно большая разница.
Поэтому $1 против $14 для меня — не главный вывод
Мой конкретный случай легко раскритиковать, и справедливо: задачи отличались, browser tools могли стоить дорого, объём работы был разный, а subscription credits нельзя напрямую приравнивать к raw token telemetry из GitHub Issues.
Поэтому мой вывод не состоит в том, что «Codex в 14 раз дороже Claude Code». У меня нет данных, чтобы это утверждать.
Меня заинтересовало другое: эффективность Harness может очень сильно влиять на реальную стоимость агентной работы, хотя в benchmark’ах мы почти никогда на неё не смотрим.
Обычно мы сравниваем GPT и Claude по качеству кода, SWE-bench, reasoning, context window и цене миллиона токенов. Но пользователь на самом деле покупает не модель.
Он использует систему:
Model
+ Harness
+ Tools
+ Context management
+ Execution strategy
Если одна система умеет запустить 20-минутную операцию и спокойно дождаться события, а другая за эти 20 минут несколько десятков раз возвращается в LLM только ради проверки статуса, разница между ними уже не описывается качеством модели.
Codex мне по-прежнему нравится. Но Harness явно есть куда расти
Именно поэтому эта история для меня не про «Codex плохой». Наоборот, модель настолько хороша, что неэффективность вокруг неё становится особенно заметной.
В коротких coding-задачах подобные детали могут вообще не иметь значения. Но когда агент начинает автономно работать десятки минут или часы, использовать браузер, MCP, shell, subagents и длинные build/test pipelines, поведение Harness становится частью производительности продукта.
После этого опыта я бы добавила к обычным benchmark’ам coding agents ещё один вопрос:
сколько токенов агент тратит не на решение задачи, а просто на управление самим процессом решения?
Мне кажется, для следующего поколения coding agents то, как агент ждёт, может оказаться почти таким же важным, как то, как он думает.
Если кто-то уже измерял похожий overhead у Codex, Claude Code или других coding agents — было бы интересно сравнить результаты.
Автор: liuyi0414


