Amiga
Оглавление курса · урок 3 из 14

Модуль 1 · Codex без человека в цикле · урок 3 из 14

Ответ по схеме: JSON для программ

Текст в stdout хорош для человека, но плох для скрипта: формулировки плавают, разбирать их регулярками — источник ошибок. У codex exec есть два машинных режима вывода. Поток событий показывает, что агент делал по шагам. Ответ по схеме выдаёт итог в заданной вами форме. Разберём оба и соберём из них проверку, которая может остановить пайплайн.

Поток событий: --json

С флагом --json stdout превращается в поток JSON Lines: одна строка — одно событие.

bash
codex exec --json "кратко опиши структуру репозитория" > run.jsonl

Типы событий: thread.started, turn.started, turn.completed, turn.failed, item.* и error. Внутри item.* лежат элементы работы агента: сообщения, рассуждения, выполненные команды, изменения файлов, вызовы MCP-инструментов, веб-поиск, обновления плана. Примерно так выглядит поток:

json
{"type":"thread.started","thread_id":"0199a213-81c0-7800-8aa1-bbab2a035a53"}
{"type":"turn.started"}
{"type":"item.started","item":{"id":"item_1","type":"command_execution","command":"bash -lc ls","status":"in_progress"}}
{"type":"item.completed","item":{"id":"item_3","type":"agent_message","text":"В репозитории каталоги plugins, themes и tests."}}
{"type":"turn.completed","usage":{"input_tokens":24763,"cached_input_tokens":24448,"output_tokens":122,"reasoning_output_tokens":0}}

С таким потоком удобно работать через jq:

bash
# какие команды запускал агент и с каким кодом выхода
jq -r 'select(.type=="item.completed" and .item.type=="command_execution")
       | "\(.item.exit_code)\t\(.item.command)"' run.jsonl

# итоговое сообщение агента (последнее agent_message)
jq -rs 'map(select(.type=="item.completed" and .item.type=="agent_message"))
        | last | .item.text' run.jsonl

# расход токенов за ход — пригодится для учёта затрат
jq -c 'select(.type=="turn.completed") | .usage' run.jsonl

Зачем это в агентстве. Поток событий — журнал аудита: видно, какие команды агент выполнял в пайплайне клиента и что менял. Сохраняйте его как артефакт job. Если агент «сделал что-то не то», без журнала вы будете гадать.

Финальный ответ по схеме: --output-schema

Если нужен не журнал, а данные для следующего шага, опишите форму ответа в JSON Schema и передайте её флагом --output-schema. Флаг -o (--output-last-message) запишет финальное сообщение в файл и при этом продублирует его в stdout.

Пример: оценка риска merge request перед ревью. Схема:

json · .codex/schemas/mr-risk.json
{
  "type": "object",
  "properties": {
    "risk": { "type": "string", "enum": ["low", "medium", "high"] },
    "summary": { "type": "string" },
    "touched_areas": {
      "type": "array",
      "items": { "type": "string", "enum": ["payments", "auth", "migrations", "integrations", "frontend", "other"] }
    },
    "needs_manual_qa": { "type": "boolean" }
  },
  "required": ["risk", "summary", "touched_areas", "needs_manual_qa"],
  "additionalProperties": false
}

Запуск:

bash
git diff origin/main...HEAD \
  | codex exec \
      --ephemeral \
      --output-schema .codex/schemas/mr-risk.json \
      -o mr-risk.json \
      "Оцени риск этого изменения для продакшена. Платежи, авторизация и миграции БД — всегда не ниже medium."

В mr-risk.json окажется объект ровно этой формы. Дальше с ним работает обычный скрипт:

bash
risk=$(jq -r .risk mr-risk.json)
if [ "$risk" = "high" ]; then
  echo "Высокий риск: нужен второй ревьюер"
  exit 1
fi

Как проектировать схему

Несколько правил, которые экономят время.

Строгая форма. Перечисляйте все поля в required и ставьте "additionalProperties": false — так сделано в примерах документации. Модель не будет добавлять «полезные» поля, которые сломают ваш парсер.

Корень — объект. Даже если по смыслу нужен массив, заверните его в объект: {"issues": [...]}. Потом jq '.issues' достанет массив в нужном формате. Этот приём понадобится в уроке про GitLab CI, где отчёт должен быть массивом в формате Code Quality.

Перечисления вместо свободного текста. Всё, по чему скрипт будет принимать решение, делайте enum: уровень риска, статус, категорию. Свободный текст оставьте для полей, которые читает человек.

Схема в репозитории. Храните схемы рядом с кодом, например в .codex/schemas/, и меняйте через MR. Схема — это контракт между Codex и вашим скриптом, и изменение контракта должно быть видно в ревью.

Промпт объясняет смысл полей. Схема задаёт форму, но не критерии. Что считать «high», когда ставить needs_manual_qa — это пишется в промпте. Лучше всего в файле промпта, который тоже лежит в репозитории.

Сбой — не пустой результат

Самая опасная ошибка в автоматизации — принять упавший запуск за чистый результат. В одном из примеров Cookbook OpenAI добавила к своему старому пайплайну именно такое предупреждение: неудачный запуск Codex или невалидный вывод нельзя считать отсутствием замечаний.

На практике это значит:

bash
set -euo pipefail

codex exec --output-schema schema.json -o result.json "..." \
  || { echo "Codex завершился с ошибкой"; exit 1; }

# файл есть и в нём JSON нужной формы
jq -e 'has("risk") and has("summary")' result.json > /dev/null \
  || { echo "Ответ не соответствует ожиданиям"; exit 1; }

Codex проверяет ответ по схеме сам, но вторая проверка на вашей стороне стоит одной строки и ловит обрыв сети, лимиты и пустые файлы. Если нужна полная валидация, используйте валидатор JSON Schema из своего стека.

Оба режима сразу

Флаги можно совмещать: --json пишет поток событий в stdout, а -o сохраняет финальный ответ по схеме в файл.

bash
codex exec --json \
  --output-schema .codex/schemas/mr-risk.json \
  -o mr-risk.json \
  "Оцени риск изменений в текущей ветке относительно origin/main" \
  > codex-events.jsonl

Журнал идёт в артефакты для аудита, mr-risk.json — в следующий шаг пайплайна. Такую связку мы используем дальше во всех CI-сценариях курса.

Попробуйте сами

10–15 мин на рабочем месте
  1. Запустите codex exec --json "назови три самых больших файла в репозитории" > run.jsonl и вытащите через jq список выполненных команд.
  2. Напишите схему из трёх полей для своей задачи — например, классификации бага по описанию: компонент (enum), серьёзность (enum), краткое описание. Прогоните её на двух-трёх реальных описаниях задач из трекера.
  3. Добавьте в скрипт проверку jq -e и убедитесь, что он падает, если удалить файл ответа.

Коротко

  • --json даёт поток событий JSONL: команды, правки, сообщения, расход токенов. Сохраняйте его для аудита.
  • --output-schema вместе с -o дают финальный ответ заданной формы.
  • Схема строгая: все поля в required, additionalProperties: false, корень — объект.
  • Решения скрипт принимает по полям enum, а не по свободному тексту.
  • Упавший запуск или невалидный ответ — это ошибка, а не «замечаний нет».
Отметка хранится только в вашем браузере
Хотите связаться с владельцем
компании напрямую?
Дмитрий Тарасов
Дмитрий Тарасов
СЕО

НАПИСАТЬ

Написать в MAX