Blog

Codex CLI, structured JSON outputs, and cron-safe AI workers

Инженерные заметки Naly: Codex CLI, структурированный JSON и cron-безопасные ИИ-воркеры

Эта заметка объясняет, как Naly превращает Codex CLI из терминального кодового агента в детерминированного продакшн-воркера, сочетая cron-планирование, строгие контракты JSON и внешние логи. Она раскрывает, почему надежность рождается из эксплуатационных инвариантов — блокировок, повторов и валидации — при том, что модель остается взаимозаменяемой,

June 25, 20269 sources

Аннотация

Naly использует Codex CLI как control plane для плановой AI-работы: каждая запись cron запускает включенный в репозиторий скрипт, который инициализирует ограниченную задачу помощника с веб-поиском и строгим контрактом вывода, затем пишет валидированные JSON-артефакты для последующей публикации, воспроизведения и повторов. Это делает поведение модели похожим на эксплуатационный pipeline — одна и та же команда, явная схема, явные артефакты — вместо изменяемого интерактивного процесса. На 2026-06-25 эта грань является конкурентным преимуществом в надежности инфраструктуры контента, критичной для роста.

Где это находится в Naly

У Naly уже есть активные приоритеты GST вокруг процессов discovery, retention и distribution. Этот паттерн напрямую подходит этому слою исполнения:

  • Cron-задачи вызывают один tsx скрипт на сценарий, чтобы каждый запуск оставался в версионированном коде, а не в разрозненных shell-сниппетах.
  • Codex CLI выполняет рассуждение и извлечение данных, а Naly берет на себя контроль оркестрации: расписание, повторы, блокировки и устойчивые артефакты.
  • Структурированный вывод подается в downstream-системы на Next.js, React, Drizzle ORM и Neon без предположений о downstream-схеме.
  • Внешние логи и метаданные запусков записываются в NALY_LOG_ROOT , благодаря чему пост-мортемы воспроизводимы независимо от буферизации вывода процесса.

Тезис такой: для production-контекста качество LLM — лишь половина системы; другую половину дают детерминированные ограничения вокруг этой LLM.

Технический механизм

1) Точка входа воркера с приоритетом контракта

Каждая задача стартует из трех неизменяемых входных данных:

  • Кодифицированный шаблон промпта и намерение задачи.
  • Схема ответа, которую нужно валидировать.
  • Окружение запуска (run_id, source_query, attempt, env), сохраненное до вызова API.

Naly запускает Codex CLI в пакетном режиме из cron, не в интерактивном. Codex описан как локальный coding agent и распространяется как самостоятельный CLI с открытым кодом и активными релизами, и может быть запущен в скриптовом окружении Codex CLI, репозиторий OpenAI Codex.

2) Почему структурированный вывод — это некомпромиссный выбор

Руководства OpenAI по structured-output описывают извлечение схемы через парсер и поведение strict mode, необходимое для машинных пайплайнов. В Naly вывод модели рассматривается как промежуточный артефакт, а не финальная истина, поэтому именно JSON-контракт служит точкой принудительной надежности:

  • обязательные поля (заголовок, список доказательств, confidence, цитаты, причина сбоя)
  • необязательные поля с значениями по умолчанию
  • числовая уверенность и ограниченные перечисления
  • явные ошибки парсера показываются как ошибки запуска, а не тихо автоисправленный текст.

3) Жизненный цикл cron-to-agent с контролем параллелизма

Cron выполняет строки по расписанию согласно стандартным 5-полюсным полям и запускает команду, когда поля совпадают crontab. Для production-надежности Naly добавляет:

  • защиту блокировкой (один активный запуск на задачу)
  • идемпотентный ключ запуска
  • ограниченную политику повторов с jitter
  • внешний захват логов для каждой фазы
  • обновление состояния после выполнения в таблицах базы, управляемых Drizzle/Neon.

flock разработан именно под этот паттерн контроля: захватить блокировку, выполнить критическую секцию, корректно завершиться, если уже заблокировано flock. Поскольку состояние блокировки привязано к файловым дескрипторам, перекрывающиеся окна cron явно блокируются вместо повреждения состояния.

4) Почему MCP важен в этом паттерне

Model Context Protocol формализует контракты host/client/server-инструментов с помощью JSON-RPC, переговоров о возможностях и структурированных вызовов инструментов MCP. В Naly границы в стиле MCP снижают неявную связанность: веб-поиск может быть представлен как управляемый инструментальный интерфейс с явно описанными возможностями вместо поведения shell в свободной форме.

Что говорит литература

Недавние исследования показывают, что надежность не эквивалентна сырой мощности. Работа «Towards a Science of AI Agent Reliability» сообщает о существенных разрывах между точностью задачи и последовательностью в разных запусках и предлагает явные измерения надежности (consistency, robustness, predictability, safety) для эксплуатационной оценки Towards a Science of AI Agent Reliability. Это подтверждает design-first подход Naly: если запуск успешен, но не может быть воспроизведен с понятными артефактами, он не является production-grade.

Для структурированных выводов Paper ToolPRM утверждает, что поведение структурированного вызова инструментов требует явного надзора и что улучшения особенно заметны при моделировании внутреннего процесса вызова функций, а не только финальных результатов ToolPRM: Fine-Grained Inference Scaling of Structured Outputs for Function Calling. Это соответствует циклу раннера Naly с приоритетом схемы: quality gate находится на границах интерфейса, а не только на плавности контента.

Третья работа на том же рубеже, SLOT, показывает практический альтернативный путь, добавляя над LLM слой формирования вывода, независимый от модели SLOT: Structuring the Output of Large Language Models. Это подтверждает тот же принцип: надежность структуры — это инженерная задача даже при высоком качестве базовой модели.

Проектные компромиссы

  1. Строгая схема vs переносимость модели: строгие схемы уменьшают downstream неоднозначность, но иногда увеличивают churn парсинга, когда провайдеры по-разному интерпретируют ограничения.
  2. Простота cron vs эластичность очереди: cron прост и прозрачен, но пиковые нагрузки требуют backoff с учетом блокировок или очередного слоя, чтобы избежать перекрывающихся запусков и пропущенных окон.
  3. Внутризадачный веб-поиск vs детерминированное воспроизведение: актуальный веб-поиск повышает свежесть, но вносит волатильность источников; поэтому Naly сохраняет текст запроса, список источников и сырые ссылки в артефактах запуска.
  4. Внешние логи vs только DB-логи: логи файловой системы переживают рестарты процессов и дешевы для инжеста; DB-логи упрощают эргономику запросов, но могут давать сбои в незакрытых циклах, если их не грамотно партицировать.

Режимы сбоев

  • Уход схемы: выход провайдера нарушает схему; смягчение — строгая fail-fast валидация, фиксация версии схемы и dead-letter-запуски.
  • Перекрывающиеся исполнения cron: двойные записи или дублирование внешних действий; смягчение — advisory lock и guard корректного завершения процесса.
  • Неустойчивость поиска: ответы апстримного инструмента меняются между попытками; смягчение — ограничения на повторы, экспоненциальная задержка и сохраненные апстримные ссылки.
  • Сюрпризы во времени: различия между DST и часовым поясом хоста могут менять семантику расписания; смягчение — политика UTC-расписания и явные проверки окружения.
  • Молчаливые частичные записи: парсинг проходит успешно, но downstream-запись падает; смягчение — транзакционная последовательность сохранения и идемпотентные upsert'ы.
  • Утечка безопасности/контекста: любой путь агента с доступом к инструментам может выходить за рамки, поэтому нужны границы минимальных привилегий в стиле MCP и явные предпосылки согласия/аутентификации, особенно когда пути выполнения инструментов затрагивают сетевые ресурсы заметки по безопасности MCP.

Примечания к реализации

  • Держите одну команду воркера для каждой бизнес-задачи в scripts/ и пусть cron вызывает только эту точку входа.
  • Сохраняйте hash файла схемы в метаданных запуска, чтобы ожидания парсера можно было аудировать после обновления модели.
  • Настройте set -euo pipefailповедение `set -e` в wrapper-скриптах и всегда включайте run ID в имена логов.
  • Пишите в логи три контрольных точки: started, codex_result_parsed, artifact_persisted.
  • Используйте NALY_LOG_ROOT чтобы runtime traces никогда не загрязняли состояние репозитория и сохранялись между контекстами рестарта.
  • Сохраняйте attempt, exit_code, retry_reason, и validation_errors чтобы GST и дашборды аудита могли разделять нестабильную инфраструктуру и настоящие регрессии модели.

Ссылки

Sources