Codex exec: свои инструкции и правила
Неинтерактивный запуск Codex работает по той же подписке ChatGPT и встраивается в любой скрипт. Малоизвестная часть: ему можно передавать собственные инструкции и правила выполнения.
У Codex есть запуск без интерфейса
Интерактивный codex видели все: TUI, диалог, подтверждения команд. Второй способ запуска заметен хуже — он живёт в --help строчкой ниже:
exec Run Codex non-interactively [aliases: e]
mcp-server Start Codex as an MCP server (stdio)
Вторая строчка выдержки здесь не понадобится: mcp-server — это Codex в роли инструмента для других агентов. Нас интересует первая. codex exec "задача" — не урезанная версия, а тот же агент без интерфейса; он выполняет задачу до конца, печатает результат и завершается — как claude -p у Claude Code:
- человек ⇄ диалог в TUI
- подтверждения — кнопками в интерфейсе
- результат остаётся в сессии
- скрипт → одна команда
- подтверждений нет — работают правила (раздел 04)
- результат — stdout или файл
config.toml · подписка ChatGPTОплата не меняется: codex login один раз, дальше токены обновляются сами.
codex execкак обычный процесс
что в интерактивном TUI
OPENAI_API_KEY = None~/.codex/auth.json — OAuth-токены; codex login один раз, дальше auto-refresh
⚙️ ~/.codex/config.toml — общий конфиг для всех режимов
Обычный процесс со stdout запускается откуда угодно: из CI, из cron, из git-hook, из другого агента. Одна проблема: в stdout попадает вся сессия подряд — заголовок, команды агента, финальный ответ. Скрипту из этого потока нужен только ответ, причём предсказуемой формы. Для этого у exec есть два флага:
--output-schema <FILE> Path to a JSON Schema file describing the
model's final response shape
-o, --output-last-message <FILE>
Specifies file where the last message from
the agent should be written
$ codex exec \
--output-schema shape.json \ # какой формы должен быть ответ
-o result.json \ # куда положить финальное сообщение
"Найди все TODO старше года и сгруппируй по владельцам"
$ cat result.json # это читает уже скрипт, не человек
{"todos": [{"owner": "pavel", "file": "src/auth.py", "line": 42}, …]}
-o отделяет ответ от логов, --output-schema гарантирует его структуру. Вместе они делают exec полноценным шагом пайплайна: следующей команде не приходится выуживать данные из свободного текста.
Типичный кейс: второе мнение на ревью
Чаще всего headless Codex используют для независимого ревью: дифф проверяет не та модель, которая его писала. У меня для этого кастомный сабагент Claude Code — один markdown-файл в ~/.claude/agents/. Говорю «сделай ревью кодексом» — и дальше цепочка отрабатывает сама:
--uncommitted · --base · --commit
рубрика + AGENTS.md
Весь сабагент — это описание на естественном языке:
---
name: codex-reviewer
description: Independent code review by OpenAI Codex. Use when the user
asks for a second opinion, says "let codex review this", wants an
outside perspective on uncommitted work, a branch, or a commit.
tools: Bash, Read
---
You are a thin bridge between Claude Code and the OpenAI Codex CLI.
Invoke `codex review` correctly and return its output verbatim.
Do NOT add your own analysis, agree, disagree, or filter findings.
## Picking the review scope
- default / "uncommitted" / "current changes" → --uncommitted
- "branch" / "vs main" / "before merging" → --base main
- "this commit" / SHA mentioned → --commit <SHA>
- custom focus ("check race conditions") → append as prompt arg
## Running it
Bash timeout 600000 — review on big diffs takes minutes.
## Do not
- summarize or filter Codex's findings
- run `codex login` — surface auth errors to the user instead
Скоуп ревью выбирается из формулировки — незакоммиченное, ветка против main, конкретный коммит, — а свой фокус уходит позиционным аргументом. На этом типичное использование обычно и заканчивается: запустил, прочитал stdout.
Пять каналов своих инструкций
Строчка-задача — только нижний слой промпта. Перед ней exec молча подкладывает ещё два: инструкции проекта и рабочую политику — ровно так же, как это делает интерактивная сессия. Каждый слой управляется своим каналом:
1 · AGENTS.md — знания уровня проекта
Headless-запуск ищет инструкции в тех же местах, что интерактивный, и складывает их в одну цепочку:
~/.codex/AGENTS.md ① глобальные инструкции
<repo>/
├─ AGENTS.md ② корень проекта
└─ src/billing/ ← рабочая директория
└─ AGENTS.md ③ самый близкий к cwd — главный:
при конфликте перекрывает ① и ②
Суммарный лимит цепочки — ключ project_doc_max_bytes, по умолчанию 32 КиБ. Практическое следствие: секция в AGENTS.md управляет даже встроенным ревьюером — его рубрика уступает инструкциям проекта:
## Review guidelines
- Any silent `except`/`catch` without logging is a P1 finding.
- We target Python 3.12+; do not flag `match` statements.
Что канал работает в headless, проверяется за минуту — маркер в инструкциях, ответ из инструкций:
$ mkdir demo && cd demo && git init -q
$ echo 'If asked for the magic word, reply exactly: BANANA-42.' > AGENTS.md
$ codex exec "What is the magic word?"
BANANA-42
2 · Промпт: аргумент + stdin — задача и данные раздельно
[PROMPT] Initial instructions for the agent. If not provided as an
argument (or if `-` is used), instructions are read from stdin.
If stdin is piped and a prompt is also provided, stdin is
appended as a <stdin> block
Последнее предложение — самое полезное: аргумент и stdin не конкурируют, а склеиваются. Формулировка задачи остаётся стабильной, данные к ней — потоковые:
$ tail -200 error.log | codex exec \
"Сопоставь стектрейсы из <stdin> с кодом в src/ и назови виновный модуль"
Сопоставь стектрейсы из <stdin> с кодом в src/ … ← аргумент
<stdin>
Traceback (most recent call last): … ← 200 строк лога
</stdin>
3 · developer_instructions — политика отдельно от задачи
Ключ config.toml; как и любой ключ, переопределяется на один запуск флагом -c:
-c, --config <key=value> Override a configuration value that would
otherwise be loaded from ~/.codex/config.toml. Use a dotted
path (foo.bar.baz) to override nested values.
Текст попадает в developer-слот — выше user-промпта (см. схему в начале раздела). При конфликте побеждает верхний слой, и на этом строится приём: задача формулируется один раз, а политику скрипт меняет по окружению:
# staging — политики нет, «исправь» значит исправь:
$ codex exec "Разбери новые предупреждения компилятора и исправь их"
# production — та же задача, но сверху политика read-only:
$ codex exec \
-c developer_instructions="You run unattended in CI. Never modify files; propose every change as a unified diff." \
"Разбери новые предупреждения компилятора и исправь их"
developer «Never modify files; propose a unified diff» ← сильнее
user «…и исправь их» ← слабее
───────────────────────────────────────────────────────
исход файлы не тронуты; «исправь» = готовый diff в ответе
Наложить полученный diff (git apply) — уже осознанное действие человека, а не самодеятельность ночного агента.
4 · project_doc_fallback_filenames — один файл инструкций на все агенты
Ключ задаёт, какие имена искать в директории, если AGENTS.md там нет (по умолчанию — никаких). Типичная ситуация: в репозитории уже есть CLAUDE.md для Claude Code, и дублировать его в AGENTS.md не хочется:
$ codex exec -c 'project_doc_fallback_filenames=["CLAUDE.md"]' \
"Найди мёртвый код и составь список кандидатов на удаление"
есть AGENTS.md? да → берём его
нет → ищем по списку fallback-имён по порядку:
["CLAUDE.md", …] → нашли CLAUDE.md → берём его
Прописанный в config.toml (а не через -c), ключ действует на все запуски, включая интерактивные.
5 · Профили -p <name> — набор настроек одним флагом
-p, --profile <CONFIG_PROFILE> Layer $CODEX_HOME/<name>.config.toml
on top of the base user config
Слои конфига накладываются по порядку — каждый следующий перекрывает предыдущий:
~/.codex/config.toml → review-strict.config.toml → -c key=value
базовый конфиг профиль (флаг -p) разовые оверрайды
Профиль — это модель, глубина reasoning и developer-инструкции разом. Держите fast для черновых прогонов и review-strict для предрелизных:
model_reasoning_effort = "xhigh"
developer_instructions = """
You are reviewing production code. Prioritize data loss,
race conditions, security. Ignore naming and formatting.
"""
$ codex review -p review-strict --base main
Шестым каналом можно считать skills (~/.codex/skills/) — они инжектятся и в headless. А вот system-слот в exec не подменяется: параметр base-instructions существует только у MCP-инструмента.
Правила выполнения: execpolicy в headless
Инструкции — половина дела. Вторая половина — правила: в терминологии Codex «rules» — это не инструкции модели, а политика выполнения shell-команд, Starlark-файлы *.rules.
~/.codex/rules/default.rules»
То есть файл правил уже копится у каждого, кто жмёт «always allow» в интерактиве. Headless-запуски читают те же файлы — в codex exec --help это видно от противного: раз есть флаг отключения, по умолчанию правила загружены.
--ignore-rules Do not load user or project execpolicy `.rules`
files for this run
~/.codex/rules/*.rules user-слой — всегда
<repo>/.codex/rules/*.rules project-слой — только если проект trusted
Каждая команда агента сверяется с правилами. В headless по умолчанию approval = never — спросить некого, поэтому исходы жёстче, чем в TUI:
shell-команда агента
│ совпала с prefix_rule?
├─ decision = allow → выполняется сразу, вне sandbox
├─ decision = prompt → блок: «approval required by policy,
│ but AskForApproval is set to Never»
├─ decision = forbidden → блок
└─ правила нет → обычный режим sandbox
(read-only / workspace-write)
Так правила из «удобства одобрений» превращаются в границы для автономного агента: явные allow — узкий коридор разрешённого, forbidden — жёсткий запрет. Пример для CI-прогона, где Codex имеет право запускать только тесты и линтер:
prefix_rule(pattern=["pytest", "-q"], decision="allow")
prefix_rule(pattern=["npm", "run", "lint"], decision="allow")
prefix_rule(pattern=["git", "push"], decision="forbidden")
prefix_rule(pattern=["rm", "-rf"], decision="forbidden")
$ codex execpolicy check --pretty \
--rules ~/.codex/rules/ci.rules -- pytest -q
decision="prompt" в headless не «спросит потом» — команда просто не выполнится. Для пайплайнов правила должны быть явными allow либо forbidden.
Что доезжает до headless, а что нет
Инструкции и правила доезжают до headless полностью. Память — нет, и это не ошибка конфигурации:
| Канал | Интерактивный TUI | codex exec |
|---|---|---|
| AGENTS.md+ fallback-имена | да | да |
| Rules*.rules / execpolicy | да | дас ~0.122; в 0.114 был баг (issue #14572) |
| Memories: чтение | да | да |
| Memories: записьпополнение памяти из сессии | да | нет |
TUI- и IDE-сессии ──▶ память ──▶ инжект в новые сессии (и в exec тоже)
exec-сессии ──✗ не источник: что агент узнал в headless-прогоне,
в память не попадает никогда
Память — экспериментальная фича (состояние покажет codex features list), и это ограничение — сознательное: сессии exec не входят в список «interactive session sources», допущенных к генерации воспоминаний. Документация об этом молчит; ограничение видно только в исходниках openai/codex. Вывод для практики: контекст, который должен доезжать до headless надёжно, живёт в AGENTS.md — не в памяти.
exec игнорировал rules. Прежде чем спорить в комментариях — codex --version.