Почему Codex тратит токены даже когда просто ждёт: что происходит внутри Harness. ClaudeCode.. ClaudeCode. codex.. ClaudeCode. codex. harness.. ClaudeCode. codex. harness. качество кода.

Сразу скажу: к самой модели 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:

#13733 — Background process polling wastes tokens: each write_stdin poll triggers full API turn with complete history

Автор разбирал поведение фоновых процессов вроде 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 — более свежий:

#35259 — Codex Desktop repeatedly re-enters the model during wait/status polling, consuming substantial credits

Здесь автор не ограничился субъективным ощущением, а разобрал локальную 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, а управления контекстом:

#19001 — Add RTK Directly Into Codex CLI to Reduce Token Usage 60–90% by Filtering Shell Command Output

Автор описывает проблему так:

“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

Источник