Журнал SkillMetric
Разработка ПО с Codex и агентами
Устройство разработки с ИИ-агентами: AGENTS.md, структура docs, git worktree, Chrome DevTools, телеметрия, архитектурные проверки и полный цикл PR.
Что нужно агенту для работающего результата
ИИ-агенту поручают исправить ошибку. Он находит подозрительный участок кода, меняет несколько строк и сообщает об успехе. Через минуту пользователь повторяет прежние действия и снова получает сбой. Чтобы такие задачи доходили до работающего результата, агенту нужны доступное описание системы, воспроизводимая среда, инструменты проверки и правила, которые действуют на каждом шаге.
*Harness Engineering* — проектирование этой инженерной среды, или обвязки агента. Она связывает модель с репозиторием, документацией, командами, браузером, журналами, тестами и процедурой принятия изменений. От её устройства зависит, сможет ли агент самостоятельно обнаружить ошибку, исправить её и подтвердить результат.
Для разработчика это предметная работа: обеспечить запуск приложения одной известной командой, сделать требования доступными, определить допустимые зависимости между модулями, подключить наблюдение за выполнением и превратить повторяющиеся замечания в автоматические проверки. Промпт запускает процесс, а подготовленная среда позволяет пройти его до конца.
Миллион строк кода и другая организация работы
В феврале 2026 года команда OpenAI описала разработку внутреннего продукта, в которой весь код поручили Codex — ИИ-агенту, работающему с файлами и инструментами разработчика. Первый коммит в пустой Git-репозиторий был сделан в конце августа 2025 года. Репозиторий хранит файлы проекта и историю их изменений; в данном случае у агента не было заранее написанной человеком кодовой базы.
Первоначальную структуру сгенерировал Codex CLI с моделью GPT‑5 и небольшим набором готовых шаблонов. В неё вошли конфигурация CI, правила форматирования, настройка менеджера пакетов и каркас приложения. Даже первый `AGENTS.md` — файл с инструкциями для агентов — создал Codex.
За пять месяцев проект вырос примерно до миллиона строк, включая приложение, инфраструктуру, документацию и внутренние инструменты. Было объединено около 1500 pull request — предложений внести изменения в общую версию проекта. Сначала в команде было три инженера, затем семь. В публикации приведена средняя интенсивность около 3,5 PR на инженера в день; при расширении команды она увеличивалась. Общие затраты времени авторы оценили примерно в одну десятую от предполагаемой ручной разработки. Продукт использовали сотни сотрудников компании, в том числе ежедневно, а также внешние альфа-тестеры.
Условие «люди не пишут код вручную» было сознательным ограничением эксперимента. Агент создавал код продукта и тесты, конфигурацию непрерывной проверки изменений и средства выпуска, внутренние инструменты разработчика, документацию и историю проектных решений, системы оценки, комментарии к проверке и ответы на них, скрипты обслуживания репозитория и определения панелей мониторинга рабочего продукта.
Инженеры определяли приоритеты, превращали обратную связь пользователей в критерии готовности и проверяли результат. Если требовалось изменить саму среду разработки, реализацию тоже поручали агенту. Работа человека сместилась к устройству процесса: какие сведения доступны, какие действия возможны и как обнаруживается неверный результат.
В начале продвижение было медленным: агенту не хватало инструментов, понятных абстракций и внутренней структуры. Сложные цели раскладывали на более мелкие части — проектирование, реализацию, проверку, тестирование. Работа строилась по принципу поиска в глубину: сначала подготовить нужную возможность, затем использовать её для следующей, более сложной задачи. Если агент застревал, команда искала отсутствующую возможность и способ сделать её понятной и обязательной для исполнителя.
У задачи должен быть проверяемый результат
Агент работает в цикле: получает цель, выбирает действие, вызывает инструмент, читает ответ и решает, что делать дальше. Обвязка задаёт доступные инструменты, передаёт сведения о проекте и связывает действия с проверкой результата. Сбой на любом из этих участков может выглядеть одинаково: агент долго работает, но функция остаётся неисправной.
Поэтому в постановке задачи нужно описать ожидаемое поведение, ограничения и способ проверки. Для ошибки в форме «исправь сохранение» слишком мало: агенту неизвестно, какие значения теряются, при каком ответе сервера и что должно происходить после повторной отправки.
Постановка определяет, что считать завершением, но ещё нужно обеспечить её исполнение. Агенту понадобятся команда запуска, тестовые данные, способ получить ошибку сервера, управление браузером и доступ к результатам проверок. Если одного из этих средств нет, следует подготовить его или явно обозначить предел проверки.
Полезно различать три вида затруднений. Неверно понятая цель требует уточнения требований. Отсутствующий инструмент требует изменения среды. Неправильное решение при доступных сведениях требует содержательной обратной связи и повторной проверки. Повторение одной и той же просьбы не устраняет автоматически ни одну из этих причин.
Короткие инструкции и документация по запросу
Модель учитывает ограниченный объём сведений — контекст. В нём одновременно находятся задача, инструкции, фрагменты кода и ответы инструментов. Большое руководство занимает часть этого пространства ещё до того, как агент прочитает нужный модуль. Общие правила смешиваются с частными, устаревшее указание становится трудно отличить от действующего, а важные ограничения теряются среди остальных.
Попытка собрать всё в одном большом `AGENTS.md` в эксперименте не сработала. Причин было четыре: инструкции вытесняли нужный контекст; множество одинаково важных указаний мешало выбирать приоритеты; устаревшие правила накапливались; проверять покрытие документации, актуальность, ответственных и перекрёстные ссылки в монолите было трудно.
Большой файл заменили коротким входным документом объёмом около ста строк. Его назначение — объяснить порядок работы и направить к нужным сведениям. Подробные требования хранятся в отдельных документах, которые агент открывает по мере необходимости. Так реализуется постепенное раскрытие информации: сначала карта проекта, затем сведения для конкретной задачи.
Ниже — пример содержимого такого входного документа. Указанные команды иллюстрируют его устройство; в проекте им должны соответствовать существующие сценарии запуска.
Такой файл помогает начать работу, но технические решения по-прежнему нужно искать в соответствующих разделах. Указание «прочитай документацию» следует заменить точным путём или понятной точкой входа. Для крупного репозитория особенно полезны индексы по предметным областям: заказам, оплате, пользователям, настройкам.
Решения, влияющие на разработку, должны быть доступны из рабочей среды агента. Обсуждение в чате становится частью контекста проекта после фиксации в доступном документе.
Если правило согласовали в Slack, Google Docs или устном обсуждении, агент сможет его учесть только после получения доступа к нужным сведениям. В этом проекте их переносили в версионируемые материалы репозитория. Фиксировали и причины решения: например, почему используется определённая схема авторизации или запрещено прямое обращение интерфейса к хранилищу данных.
Это касается инженерных соглашений, принципов продукта и принятых в команде предпочтений — вплоть до использования эмодзи. Организованные сведения помогают получать согласованный результат. Разрозненные сообщения, скопированные в длинный промпт, гораздо труднее связать с конкретным решением в коде.
Репозиторий как рабочая база знаний
Знания удобнее организовать по назначению: устройство системы, поведение продукта, текущая работа, технические ограничения и справочные сведения. В эксперименте использовалась структура с отдельными каталогами проектных решений, спецификаций, планов выполнения и автоматически получаемых материалов.
В `docs/references/` находились, в частности, `design-system-reference-llms.txt`, `nixpacks-llms.txt` и `uv-llms.txt`. Это отдельные точки доступа к сведениям о дизайн-системе и используемых инструментах. Их можно открыть для соответствующей задачи, не включая все справочники в начальный контекст.
Важна связь документов с действующей программой. Описание схемы данных полезнее, когда оно получено из реальной схемы и обновляется вместе с ней. Архитектурное правило полезнее, когда рядом указана проверка его соблюдения. Спецификация функции должна соответствовать поведению, которое проверяют тесты.
У проектных документов хранят статус проверки и ответственных; ссылки связывают решения с другими материалами. `QUALITY_SCORE.md` позволяет оценивать каждую предметную область и архитектурный слой отдельно и отслеживать пробелы во времени. Это помогает различать хорошо проверенную часть продукта и участок, где недостаёт подтверждений качества.
Линтеры и задания CI проверяют наличие обязательных файлов, внутренние ссылки и структуру базы знаний. Содержательное устаревание требует сверки документации с кодом, тестами и фактическим поведением. В эксперименте периодический агент *doc-gardening* искал такие расхождения и открывал PR с исправлениями.
Агент начинает с короткой инструкции, выбирает предметную область и открывает необходимые документы. Ему не приходится каждый раз получать полную историю проекта в одном сообщении.
План выполнения сохраняет состояние длинной задачи
Для небольшой правки достаточно краткого плана. Сложное изменение затрагивает несколько частей системы, требует промежуточных решений и может занимать больше одного сеанса. Здесь план становится отдельным версионируемым файлом: его изменения сохраняются вместе с кодом.
В нём нужно различать намерение и фактически выполненную работу. Запись «проверить форму» описывает следующий шаг. Запись «сценарий с ошибкой сервера воспроизведён, значения очищаются после ответа; проверка находится в таком-то тесте» сохраняет полезное состояние задачи. Следующий запуск может опереться на неё и проверить связанный материал.
Активный план, завершённый план и реестр технического долга выполняют разные задачи. Первый управляет текущей работой, второй сохраняет историю, третий удерживает проблемы, которые сознательно оставили на потом. Все три вида записей находятся рядом с кодом и позволяют продолжать работу без восстановления контекста по внешней переписке.
Отдельная среда для каждого изменения
Несколько агентов могут параллельно менять разные участки проекта. Общий рабочий каталог и один запущенный экземпляр приложения мешают понять, чьё изменение сейчас проверяется. Один процесс обновил файл, другой перезапустил сервер, третий прочитал журнал предыдущего запуска — результаты начинают расходиться.
Git worktree позволяет создать отдельную рабочую копию того же репозитория со своей веткой. Например, новая задача по исправлению формы может получить собственный каталог:
В эксперименте отдельный экземпляр приложения и локальные средства наблюдения создавались для каждого worktree. Агент видел поведение своей версии, а временные ресурсы удалялись после завершения задачи. За счёт этого результат проверки можно было связать с определённым изменением.
Для агентной среды полезны три явные операции: подготовить рабочее состояние, запустить приложение и завершить работу с освобождением временных ресурсов. Их вывод должен содержать необходимые сведения — адрес приложения, расположение журналов и причину отказа запуска. Тогда агент получает исходные данные для следующего действия без ручного переноса сообщений между инструментами.
Проверка интерфейса через браузер
Агенту-разработчику нужен способ увидеть приложение в работе. В эксперименте для этого использовался Chrome DevTools Protocol — программный интерфейс управления браузером. Через доступные инструменты агент мог переходить по страницам, выполнять действия, получать скриншоты и снимки DOM — структуры элементов страницы. Для работы с этими возможностями команда подготовила отдельные навыки.
Агент запускает свою версию приложения, выполняет сценарий, получает наблюдения и возвращается к изменению кода. После исправления сценарий повторяется.
Для формы из примера проверка выглядит так: открыть страницу, ввести известные значения, получить ошибку сервера, проверить содержимое полей и сообщение об ошибке. После изменения кода нужно выполнить ту же последовательность, затем отдельно проверить успешную отправку. Это даёт проверку и исправленного дефекта, и соседнего рабочего сценария.
Скриншот фиксирует вид экрана, DOM помогает проверить элементы и значения, а действия браузера подтверждают переходы между состояниями. Для сложной ошибки полезна видеозапись последовательности действий до исправления и после него. Эти материалы дополняют результат тестов и помогают проверяющему воспроизвести поведение.
Журналы, метрики и трассировки как инструменты агента
Часть проблем не видна в интерфейсе: слишком долгий запуск, повторный запрос, неверный порядок операций, ошибка фонового процесса. Для их проверки нужны технические сведения о выполнении программы.
**Журнал** сообщает о событиях: запрос получен, операция началась, проверка не прошла. **Метрика** хранит числовую характеристику: длительность, количество ошибок, число активных задач. **Трассировка** связывает этапы прохождения одного запроса; отдельный участок такой трассы называется span. Эти средства помогают найти задержку или сбой внутри конкретного сценария.
Наблюдения относятся к тому экземпляру приложения, который проверяет агент. Потоки данных и команды обращения к ним должны быть доступны из его рабочей среды.
В показанном стеке Vector принимает и маршрутизирует телеметрию, VictoriaLogs хранит журналы, VictoriaMetrics — метрики, VictoriaTraces — трассировки. Агент получает доступ к запросам через инструменты своей среды. Вся цепочка относится к конкретному worktree и существует на время выполнения задачи.
В исходном кейсе агент обращался к журналам через LogQL, а к метрикам через PromQL — языки запросов к соответствующим системам. Существенна сама возможность получить конкретный ответ инструмента: какое событие произошло, сколько заняла операция, на каком этапе возникла задержка.
Структурированная запись удобнее свободного сообщения, когда её нужно найти и связать с задачей. Например, формат события запуска сервиса может включать такие поля:
Требование «сервис запускается менее чем за 800 мс» становится исполнимым, когда определены начало и конец измерения, команда запуска и место получения результата. Аналогично можно проверить, что ни один span в четырёх критических пользовательских сценариях не превышает двух секунд. Оба вида требований приведены в описании эксперимента; для своего приложения нужно определить соответствующие сценарии и условия измерения.
Проверка должна завершаться фактом: измеренной длительностью, результатом запроса или ссылкой на трассу конкретного запуска. Иначе в отчёте останется оценочное «достаточно быстро», которое трудно использовать при принятии изменения.
Архитектура, которую проверяют инструменты
При высокой скорости генерации кода архитектурные нарушения быстро размножаются. Если агент находит несколько способов обратиться к данным, он может воспроизвести любой из них, включая случайный обход общего слоя. Поэтому устройство проекта должно быть достаточно ясным для выбора решения, а обязательные ограничения — доступными автоматической проверке.
В описанной архитектуре каждая предметная область, например настройки приложения, разделена на фиксированные слои: `Types → Config → Repo → Service → Runtime → UI`. Они отвечают за типы данных, конфигурацию, доступ к данным, бизнес-логику, выполнение и пользовательский интерфейс. Направления зависимостей ограничены. Общие возможности — аутентификация, коннекторы, телеметрия и флаги функций — подключаются через явный интерфейс `Providers`. Связи за пределами разрешённой модели отклоняются проверками.
Схема разделяет роли компонентов и показывает предусмотренные связи. В конкретном репозитории разрешённые зависимости закрепляются структурными проверками.
Такой уровень формализации часто откладывают до роста команды. При большом потоке агентных изменений он нужен раньше: скорость появления кода увеличивает и скорость распространения неудачных зависимостей. Документ с архитектурой задаёт модель, а инструменты обеспечивают её соблюдение при каждом изменении.
Для действующего проекта сначала нужно описать собственные границы. Затем выбрать нарушения, которые можно выявлять автоматически. Линтер проверяет заданные свойства кода; структурный тест может обнаружить запрещённый импорт или зависимость между модулями. В эксперименте эти инструменты тоже генерировал Codex.
Понятное сообщение проверки даёт агенту сведения для исправления. Например, если в проекте запрещено обращаться из UI прямо к слою доступа к данным, сообщение может выглядеть так:
Это сообщение указывает место, нарушенное ограничение и следующий допустимый путь. Агент может открыть правило, найти интерфейс сервиса, исправить связь и повторить проверку. Ошибка инструмента таким образом становится частью рабочего цикла.
В исходном проекте автоматически контролировались также структурированное журналирование, соглашения об именовании схем и типов, ограничения размера файлов и специфичные для платформы требования к надёжности. Настраиваемые линтеры позволяли включать инструкции по исправлению непосредственно в текст ошибки.
Не каждую деталь реализации нужно закреплять одинаково жёстко. Например, требование проверять внешние данные на границе системы определяет необходимое поведение, но допускает разные библиотеки. В кейсе агент выбирал Zod, хотя команда не предписывала именно этот инструмент. Это оставляет свободу решения внутри проверяемых границ.
Результат мог отличаться от привычного человеку стиля. Критериями оставались корректность, сопровождаемость, воспроизводимость и понятность для следующих запусков агента. Устойчивые замечания людей, результаты рефакторинга и ошибки пользователей возвращались в документацию и инструменты. Когда описания правила не хватало, его закрепляли исполняемой проверкой.
Предсказуемые компоненты и небольшие абстракции
Агенту проще менять компонент, если его поведение можно понять по коду, интерфейсу и тестам внутри проекта. Стабильные API и возможность собирать решение из понятных частей уменьшают число предположений. Распространённые, хорошо изученные технологии дополнительно дают модели знакомые образцы работы; в публикации это названо преимуществом «скучных» технологий.
Иногда небольшой собственный компонент оказывается удобнее универсальной зависимости с непрозрачным поведением. В эксперименте вместо общего пакета наподобие `p-limit` использовали собственный `map-with-concurrency`: обработку элементов с ограничением числа одновременных операций. Компонент был тесно интегрирован с OpenTelemetry — инструментами сбора технических данных — и имел заявленное 100% покрытие тестами. Он реализовывал нужный поднабор возможностей и предсказуемо работал в выбранной среде выполнения.
Решение обеспечивало нужное поведение и связь операций с наблюдениями. В другом проекте подходящим выбором может оказаться готовая библиотека. Критерий выбора — понятность контракта, проверяемость поведения и стоимость сопровождения интеграции.
На границах компонентов особенно важно работать с проверенными структурами данных. Входной ответ следует разобрать по ожидаемой схеме либо получить через типизированный SDK — набор средств доступа с описанными типами. Догадки о наличии поля и его значении легко превращаются в повторяемый образец для следующих изменений.
Общие функции тоже должны быть хорошо видны агенту. Если готовая утилита спрятана в неожиданном модуле и не имеет примера использования, новые задачи будут порождать её копии. Явное назначение, документация и единый путь подключения помогают сохранять общие правила в одном месте.
Проверяемость и доступность проекта полезны сразу нескольким инструментам. В исходной публикации наряду с Codex упоминается Aardvark, работавший с той же кодовой базой. Однажды подготовленные команды, контракты и проверки могут служить разным агентам.
Полный цикл от ошибки до принятого изменения
Автономность полезно измерять завершёнными этапами работы. В подготовленном репозитории агент может пройти путь от воспроизведения ошибки до принятия изменения, включая замечания проверяющих и исправление сборки. В описанном проекте отдельные задачи выполнялись более шести часов, в том числе без присутствия инженеров.
Полная последовательность, которую команда смогла поручать Codex по одному промпту, включала одиннадцать действий:
CI — автоматический запуск проверок при изменении проекта. Его результаты должны быть доступны агенту так же, как локальные тесты. В эксперименте агент использовал обычные инструменты разработчика: локальные скрипты, GitHub CLI `gh` и навыки, сохранённые в репозитории. Это позволяло собирать сведения, отвечать в обсуждениях, отправлять обновления и часто объединять собственные PR без ручного копирования контекста между окнами.
Перед завершением Codex проверял свои изменения локально, запрашивал дополнительные конкретные проверки у агентов локально и в облаке, отвечал на замечания человека или агента и повторял цикл до устранения замечаний проверяющих. В публикации такой повторяемый процесс сопоставлен с циклом Ralph Wiggum. Его полезное свойство — возврат к задаче по результатам проверки с явным условием завершения.
Люди могли просматривать PR, но в этом эксперименте такое участие не было обязательным для каждого изменения. Со временем команда переносила всё большую часть проверки на взаимодействие агентов. Это стало возможно благодаря подготовке самого репозитория и инструментов; без сопоставимой среды один промпт не обеспечивает описанную автономность.
Дополнительному проверяющему полезно давать конкретный вопрос: сохранён ли серверный контракт, покрыт ли исходный дефект, соблюдена ли граница модулей. Несколько общих подтверждений «выглядит хорошо» не показывают, какие свойства изменения были проверены.
Итоговый отчёт должен позволять восстановить сделанную работу. Для этого достаточно компактной записи с привязкой к задаче:
Высокая скорость работы меняет и организацию слияния. В исходном проекте использовались короткоживущие PR, минимальное число блокирующих условий и быстрые последующие исправления. Нестабильные тесты часто перезапускали вместо длительной остановки процесса. Логика компромисса состояла в соотношении стоимости: исправление агентом обходилось дёшево, а ожидание расходовало дефицитное внимание людей.
Как не накапливать повторяющиеся ошибки
Агент воспроизводит найденные в репозитории способы решения, включая неудачные. Поэтому проблемный образец влияет на последующие изменения: появляются копии вспомогательных функций, разные способы журналирования, устаревшие пояснения и обходы архитектурных границ.
Команда эксперимента сначала тратила каждую пятницу — около 20% рабочей недели — на ручную уборку таких проблем. Затем поддержку перевели в постоянный процесс: закрепили основные инженерные принципы, стали регулярно искать отклонения, обновлять оценки качества и открывать небольшие PR с исправлениями. Большинство таких изменений, по описанию команды, можно было просмотреть меньше чем за минуту и объединить автоматически.
В репозиторий внесли «золотые принципы»: общие пакеты утилит вместо отдельных самодельных помощников; проверка структур данных на границах системы или использование типизированных SDK вместо догадок об ответе. Фоновые задачи Codex искали отклонения от этих принципов и предлагали точечный рефакторинг. Нежелательные образцы устранялись ежедневно, пока они не успели распространиться по проекту.
Рабочая последовательность конкретна. Повторившуюся проблему нужно связать с причиной, определить правило, выбрать способ его проверки и применить его к существующему коду. Например, при повторном создании утилиты сначала стоит убедиться, что общий компонент доступен и его назначение понятно. Затем можно добавить проверку на запрещённый вариант или поправить инструкцию по использованию.
Так замечание из одного PR становится свойством среды, которое действует и в следующих задачах. Технический долг погашается небольшими регулярными изменениями. При этом правила тоже требуют сопровождения: избыточное или устаревшее ограничение мешает работе. У каждого правила должны быть понятная причина, область применения и способ проверки.
Как внедрять это в существующий проект
Начать удобно с одной воспроизводимой ошибки или ограниченного изменения функции. На небольшом участке видно, каких сведений и инструментов не хватает, а результат можно проверить до расширения автономности.
После нескольких задач полезно оценить время до принятого изменения, число вмешательств человека, повторные замечания и ошибки, обнаруженные после завершения работы. Число сгенерированных строк и продолжительность запуска описывают активность, но не дают этой оценки самостоятельно. В стоимость задачи входят последующая проверка, переделка и сопровождение.
Практический результат работы с агентами накапливается в самом проекте: доступных требованиях, командах запуска, понятных интерфейсах, воспроизводимых сценариях и проверках. Исправленная ошибка должна оставить после себя работающую функцию и механизм, который поможет заметить её возвращение.
Автор и источник
**Максимов Егор Васильевич** исследователь промптов и разработчик
CI: Continuous Integration — непрерывная интеграция.
Ryan Lopopolo. [Harness engineering: leveraging Codex in an agent-first world](https://openai.com/index/harness-engineering/). OpenAI, 11 февраля 2026 года.