Модуль 1 · Как устроен агент · урок 2 из 24
Агентный цикл Codex изнутри
Чтобы строить среду для агента, надо понимать, что происходит между вашим промптом и готовым диффом. В Codex это не магия, а довольно простой цикл, описанный самими инженерами OpenAI и открытый в исходниках. В уроке разберём его по шагам и выведем из устройства цикла конкретные требования к вашему harness.
Словарь: тред, ход, элемент
В статье Селии Чен «Unlocking the Codex harness» (OpenAI, 4 февраля 2026) три примитива App Server — протокола, на котором работают все поверхности Codex:
- Item (элемент) — атомарная типизированная единица: сообщение пользователя, ответ агента, выполнение команды, правка файла, запрос одобрения, вызов MCP-инструмента. У каждого есть жизненный цикл «начат → дельты → завершён».
- Turn (ход) — единица работы агента, которую запускает ввод пользователя.
- Thread (тред) — долговечный контейнер сессии. Его можно возобновить, форкнуть, архивировать.
Тот же harness работает в CLI, IDE-расширении, ChatGPT desktop app и облаке. Практическое следствие: всё, что вы вложили во внешний harness (AGENTS.md, навыки, хуки, скрипты проверки), работает на любой поверхности, включая codex exec в CI.
Цикл по шагам
Майкл Болин в статье «Unrolling the Codex agent loop» (23 января 2026) описывает цикл так:
- Ввод пользователя превращается в промпт — как именно, разберём в следующем уроке.
- Инференс: модель либо даёт финальный ответ, либо просит вызвать инструмент.
- Harness выполняет вызов и дописывает вывод в промпт.
- Модель опрашивается снова — с полной историей.
- Ход заканчивается, только когда модель выдала сообщение ассистента.
За один ход могут пройти сотни вызовов инструментов. Контекстное окно учитывает и вход, и выход, поэтому управление контекстом — прямая обязанность harness, а не модели.
ввод → [промпт] → инференс ─┬─ сообщение ассистента → конец хода
└─ вызов инструмента → исполнение → вывод в промпт ─┐
↑ │
└──────────────────────────────────────────────────────────────┘Инференс: stateless по замыслу
Codex CLI ходит в Responses API по HTTP. Эндпоинт зависит от входа: через ChatGPT, по API-ключу, локально через --oss или через облачного провайдера.
Ключевое решение, по Болину: Codex не использует previous_response_id, то есть не просит сервер помнить разговор. Каждый запрос несёт всю историю целиком. Так запросы остаются полностью stateless и совместимы с Zero Data Retention. Цена — объём JSON за разговор растёт квадратично. Компенсирует это кэш промптов: при попаданиях стоимость сэмплирования растёт линейно. Кэш работает только при точном совпадении префикса — отсюда целая дисциплина, о ней в уроке о сборке промпта.
Инструменты и граница песочницы
Модели передаётся список инструментов. По Болину, в нём shell-инструмент, встроенный инструмент плана update_plan, инструменты Responses API вроде веб-поиска и инструменты MCP-серверов с именами вида mcp__<server>__<tool>.
Самая недооценённая деталь: песочница Codex распространяется только на его shell-инструмент. MCP-инструменты Codex не изолирует, за свои ограничения они отвечают сами. Если вы подключили MCP-сервер с доступом к базе или GitLab API, права этого сервера и есть ваша граница безопасности, а не sandbox_mode.
Наш вывод. При проектировании harness считайте каждый MCP-сервер отдельной поверхностью атаки: токены с минимальными правами, read-only там, где хватает чтения, отдельные ключи для агента. Подробнее о модели безопасности — в курсе для техдиров, урок «Модель безопасности».
Вывод инструментов — это ваш контекст
Всё, что вернул инструмент, ложится в историю и отправляется в каждый следующий запрос хода. Responses API режет слишком длинный вывод команды, сохраняя начало и конец, а в Codex есть ключ tool_output_token_limit — бюджет токенов на хранение одного вывода в истории.
Значит, качество датчиков определяется не только тем, что они проверяют, но и тем, как они это сообщают:
- PHPUnit или Playwright с подробным репортером выдаёт тысячи строк, из которых важны три. Ошибка может оказаться ровно в вырезанной середине.
- Линтер, который пишет «нарушение правила 47», заставляет агента искать правило. Линтер, который пишет «вынесите запрос в репозиторий модели, см. docs/architecture.md», сразу даёт исправление. Этот приём подробно разобран в уроке об инвариантах.
Практичный шаблон — отдельные «тихие» команды для агента в Makefile или composer-скриптах: короткий вывод, но честный код выхода. Обрезать вывод через простой | tail нельзя: код выхода станет кодом tail, и падение тестов превратится в успех.
# Команды для агента: короткий вывод, код выхода сохраняется
test-agent:
@vendor/bin/phpunit --no-progress --colors=never > .agent-test.log 2>&1; s=$$?; tail -n 60 .agent-test.log; exit $$s
e2e-agent:
@npx playwright test --reporter=line > .agent-e2e.log 2>&1; s=$$?; tail -n 80 .agent-e2e.log; exit $$sГде человек и harness вмешиваются в цикл
Цикл не закрытый. Точки вмешательства:
- Одобрения. Протокол App Server двунаправленный: сервер сам отправляет клиенту запрос на одобрение и ставит ход на паузу до ответа.
- Steer и queue. В CLI Enter во время работы добавляет указание в текущий ход, Tab ставит сообщение в очередь на следующий.
- Хуки. События
PreToolUse,PostToolUse,Stop,PreCompactи другие позволяют детерминированно проверить или заблокировать действие. Это программная часть harness, которая не зависит от того, «послушалась» ли модель. Настройка — в уроке о хуках.
Что из этого следует для внешнего harness
Наши выводы из устройства цикла:
- Ход заканчивает модель. Нет внешнего критерия — агент остановится, когда сочтёт работу сделанной. Поэтому «готово» надо определять явно и проверяемо: команда, которая должна пройти, поведение, которое должно наблюдаться.
- Каждый шаг стоит контекста. Шумный инструмент обходится дороже, чем кажется: он ухудшает все следующие шаги хода.
- Граница безопасности — shell. Всё, что подключено мимо shell, нужно ограничивать отдельно.
- Инвестиции переносимы. Один harness на все поверхности: то, что настроено для CLI, работает в облаке и в CI.
Попробуйте сами
10–15 мин на рабочем месте- Посмотрите на цикл глазами программы. В своём репозитории выполните и посчитайте, каких элементов больше всего:
codex exec --json "Найди, где в проекте настраивается подключение к базе, и кратко опиши" \
| jq -r 'select(.type=="item.completed") | .item.type' | sort | uniq -c- Найдите в выводе (
codex exec --json ... > run.jsonl) самую длинную команду по объёму вывода. Подумайте, нужна ли агенту «тихая» версия этой команды, и добавьте её в Makefile илиcomposer.json. - Перечислите подключённые MCP-серверы (
codex mcp listили/mcpв TUI) и для каждого запишите, какие права у его токена. Это и есть ваша граница безопасности за пределами песочницы.
Коротко
- Тред → ходы → элементы; ход заканчивается сообщением ассистента.
- Codex stateless: каждый запрос несёт всю историю, выручает кэш префикса.
- Песочница покрывает только shell; MCP-серверы ограничивайте сами.
- Вывод инструментов — главный потребитель контекста; датчики должны говорить коротко и по делу.
- «Готово» определяет модель, если вы не определили его проверяемо.