Харнесс-инжиниринг для ИИ-агентов: проектирование контуров управления
Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.
Обновление статьи
Первоначально опубликовано 22 июля 2026 года. Проверено и обновлено 6 сентября 2026 года. В обновлении добавлены более свежие данные по бенчмаркам харнессов и случаи вмешательства провайдеров, а также уточнено, что именно подтверждают приведённые результаты.
Агент может завершить свой ход, хотя работа ещё не закончена. Для кодинг-агента полезные подтверждения — изменённый артефакт и результаты обязательных тестов. Финальное сообщение «готово» не подтверждает ни то, ни другое.
Харнесс — это управляющий код вокруг цикла ризонинга. Он передаёт контекст, валидирует и авторизует tool calls, записывает результаты и решает, достаточно ли подтверждений, чтобы принять работу. Рантайм поддерживает выполнение и состояние под ним.
При ревью харнесса я бы задал два вопроса: что не даёт ему принять незавершённую работу и какие сбои оправдывают добавленные элементы управления? В этой статье мы разберём проверки приёмки, ретраи и handoff, а затем покажем, как сравнить контрольный механизм с фиксированным baseline. Примеры магазина вымышлены; companion lab — детерминированная симуляция, а не измерение работы живого агента.
Коротко: не принимайте coding-задачу только потому, что модель говорит, будто закончила. Требуйте изменённый артефакт и результаты обязательных тестов. Добавляйте ретрай, handoff или эвалуатор только тогда, когда контролируемое сравнение показывает улучшение результата.
Простейшую проверку приёмки легко написать для небольшого исследовательского агента, который создавался в этой серии, — агента LangGraph, получающего рыночные данные и составляющего отчёт аналитика. Хук за пределами модели валидирует отчёт по схеме и проверяет, что в нём действительно есть тикеры акций; некорректный отчёт оставляет ран открытым. Двенадцать строк обычного кода — и модель уже не может сама объявить свой вывод корректным. В репозитории используется более мягкая проверка: голосует эвалуатор в свежем контексте, после чего результат проверяет человек. (В части 4 набросан детерминированный вариант.)
Чего этот пример не показывает, так это самого интересного: что происходит, когда подтверждения неоднозначны, когда ретрай может дважды списать деньги или когда работа переживает сессию, в которой была начата. Для этого нужна задача с более чёткой границей pass/fail, чем у исследовательского отчёта. Исследовательский агент остаётся примером проверки приёмки; для случаев с ретраями и handoff добавим небольшой вымышленный репозиторий магазина. Coding-задача — снизить порог для автоматической скидки 10% со $100 до $75 в src/checkout.py. В репозитории есть две обязательные проверки:
pytest tests/test_checkout.pyпроверяет расчёт скидки.pnpm playwright test tests/checkout_discount.spec.tsдобавляет товар за $80 в локальный тестовый магазин и проверяет, что на странице checkout отображается скидка $8.
Пример — учебная фикстура, а не настоящее приложение или бенчмарк. Каждая попытка начинается с одного и того же коммита и одних и тех же seed-данных тестов. Харнесс может принять изменение только тогда, когда обе команды завершились успешно, а долговечная запись о приёмке связывает оба результата с чистым закоммиченным кандидатом или дайджестом полного протестированного снапшота, включая релевантные неотслеживаемые файлы.
Диаграмма показывает путь изменения скидки от предложения до подтверждений. Харнесс передаёт задачу и файлы, проверяет аргументы и разрешения предложенного edit_file и отправляет принятный вызов. После того как рантайм применяет изменение, харнесс запускает указанные unit- и browser acceptance-тесты. Неуспешная команда возвращается модели как подтверждение для следующего хода; две успешные команды делают изменение кандидатом на приёмку.
Что входит в ответственность харнесса
В описании цикла Codex от OpenAI приведён базовый цикл. Харнесс собирает промпт, просит модель предложить следующее действие, отправляет в рантайм принятый tool call и добавляет результат. Затем снова спрашивает модель. Это повторяется, пока харнесс не примет результат или не вернёт управление пользователю.
В реализации несколько обязанностей могут быть объединены в один процесс. Но границы сбоев всё равно различаются:
| Термин | Задача | Пример для coding-агента |
|---|---|---|
| Модель | Предлагает текст, tool call или финальный ответ | Предлагает изменить src/checkout.py |
| Цикл ризонинга | Выбирает следующий шаг из доступного контекста | Изучить, изменить, протестировать, снова изучить |
| Харнесс | Передаёт контекст, валидирует предложения, авторизует их, отправляет принятые вызовы, записывает результаты и проверяет завершение | Разрешает изменения в src/ и требует оба указанных теста |
| Рантайм | Выполняет принятые вызовы и поддерживает состояние за пределами worker-процесса | Лог сессии, сэндбокс, хранилище чекпоинтов, бэкенд трейсинга |
Строка рантайма охватывает четыре вещи: сессию, сэндбокс, чекпоинт и трейс. Все четыре либо хранят состояние, либо ограничивают выполнение. Модель предлагает действие, а цикл ризонинга выбирает следующий шаг. Харнесс решает, можно ли выполнить предложенный вызов и достаточно ли подтверждений для завершения, поэтому ему посвящена отдельная статья. В части 5 харнесс считается вместе с этими четырьмя компонентами одним из пяти примитивов, которые нужно разместить до выхода в продакшен; здесь он снова выделен отдельно.
При появлении сбоя диагностируйте границу, которая должна на него реагировать. Плохому плану могут понадобиться более точные инструкции или улучшенный ризонинг модели. Если edit_file обращается к пути за пределами src/, харнесс должен отклонить вызов. Если процесс сэндбокса завершается до выполнения изменения, это относится к рантайму: он должен перезапустить worker или сообщить о падении.
Где находятся предыдущие части
Строка харнесса выше выполняет большую часть работы в этой таблице — именно к ней сводятся части 2, 3 и 4. Каждая из них принимает одно решение о конкретном ходе:
| Предыдущая часть | Что она решает для этого хода | Где действует в walkthrough следующего раздела |
|---|---|---|
| Часть 2 — память | Какое предыдущее состояние попадает в промпт | Шаг 1, сборщик контекста |
| Часть 3 — tool use | Какие действия доступны и как выглядит валидный результат | Валидация аргументов на шаге 3 и форма результата на шаге 4 |
| Часть 4 — безопасность | Можно ли выполнить этот конкретный вызов сейчас | Шаг 3, проверка пути и решение об одобрении |
| Часть 6 — эта статья | Завершает ли выполнение полученное подтверждение | Шаги 5–7, проверки приёмки и трейс |
Части 3 и 4 используют один и тот же шаг 3, и именно это подтверждает, что их стоит рассматривать как одну программу. Один и тот же слой кода харнесса, который отклоняет некорректный аргумент, также отклоняет разрешённый, но ещё не одобренный вызов. Если валидация и авторизация выполняются в разных сервисах, сохраняйте валидированные аргументы при переходе между ними, чтобы решение об авторизации относилось к тому вызову, который будет выполнен.
Для отладки разделение всё же важно: изменение не того файла — это правило пути из части 4, а не проблема retrieval из части 2. Ближе к концу этой статьи это превращается в таблицу маршрутизации.
В собственном case study по harness engineering OpenAI описывает загружаемый экземпляр приложения для каждого worktree. Команда также встроила browser automation в окружение агента и открыла ему логи, метрики и трейсы.
Требование вроде «ни один спан в этих четырёх критически важных пользовательских сценариях не должен превышать две секунды» стало тестируемым, потому что агент мог запустить приложение и запросить те же сигналы, которые проверял бы инженер. Case study специфичен для конкретного продукта. Переносимо другое: приложение и его сигналы производительности должны быть доступны внутри окружения агента.
Lopopolo, автор этого case study, ведёт field guide по harness engineering. Он называет два рычага, которые используются в этой статье: зафиксировать модель и coding-agent как чёрный ящик, а контекст и инструменты проектировать вокруг них. Его формулировка также объясняет, почему большая часть харнесса в итоге оказывается обычным кодом.
Планка качества организации, процедуры, история исключений и отношения полномочий находятся за пределами того, что может знать general-purpose модель. Харнесс выводит их наружу в виде инструкций репозитория, правил разрешений и проверок приёмки. Каждый принятый ран может возвращать свои уроки в эти артефакты, вместо того чтобы полагаться на то, что следующая сессия заново их обнаружит.
Проследим изменение скидки от предложения до приёмки
Для описанной выше задачи модель предлагает изменить calculate_discount в src/checkout.py. До того как это изменение станет прогрессом, происходит несколько вещей:
- Сборщик контекста передаёт задачу, инструкции репозитория, релевантные файлы, результаты предыдущих tool calls и текущий план.
- Модель предлагает вызов
edit_fileс путём и текстом замены. - Граница инструмента (код харнесса между предложением и выполнением) валидирует аргументы, проверяет путь на соответствие разрешённой области и запрашивает одобрение, если операция этого требует.
- Рантайм применяет изменение в сэндбоксе и возвращает структурированный результат.
- Харнесс запускает
pytest tests/test_checkout.py, затемpnpm playwright test tests/checkout_discount.spec.tsи считывает оба exit code. Browser-тест проверяет видимую скидку $8 в корзине за $80, заполненной seed-данными. - Харнесс решает, что означают результаты. Неуспешная проверка становится новым контекстом для следующего хода модели, а успешный ран делает задачу кандидатом на завершение.
- Успешный результат становится подтверждением завершения только после того, как харнесс надёжно записывает команду, exit code, протестированный снапшот, grader и версии окружения; трейс может ссылаться на эту запись.
После шага 2 ни один файл ещё не изменён. Харнесс может отклонить ../../secrets.env, потребовать одобрение деструктивной команды или остановить ран, исчерпавший бюджет. Это последний дешёвый момент. После запуска тестов харнесс сам считывает их exit code. Модель не может сама пометить своё изменение как прошедшее проверку.
В записи о приёмке должны быть указаны протестированный снапшот, обе команды и их результаты, а также версии grader и окружения; трейсы могут ссылаться на эту запись. Обязательные тесты следует держать за пределами области записи агента или независимо одобрять изменения до запуска grading. Любое последующее изменение файла делает результат недействительным. Эти проверки реализуют принципы стабильного окружения и grader, устойчивого к обходу, из рекомендаций Anthropic по эвалуации. Финальное сообщение done без этих записей не доказывает, что изменение прошло обязательные проверки.
Решаем, где применять каждое правило
Требование, чтобы tests/checkout_discount.spec.ts завершался успешно, должно находиться в детерминированном коде, а не в промпте. Харнесс отправляет команду Playwright в рантайм, считывает её exit code и не завершает ран, пока тест не проходит. Промпт может напомнить модели запустить тест. Он не может помешать модели объявить успех без подтверждений.
Другие правила относятся к другим слоям:
| Где размещать правило | Подходящий случай | Пример |
|---|---|---|
| Промпт или skill | Порядок поиска, соглашения по коду и формат плана | Перед изменением checkout-кода прочитать AGENTS.md |
| Граница инструмента | Валидация аргументов, разрешённые пути, одобрения и доступ к инструментам | Разрешать запись только в src/ |
| Детерминированный код | Бюджеты, тайм-ауты, ретраи, exit code тестов и требования к релизу | Оставлять ран открытым, пока Playwright-тест не проходит |
| Эвалуатор в свежем контексте | Визуальное ревью или критерии, требующие суждения, похожего на человеческое | Сравнить сгенерированную диаграмму с письменной рубрикой |
Контракты инструментов отделяют предложение от разрешения
Задаче со скидкой нужны только изменения файлов и команды тестов. У state-changing API другой тип сбоя, поэтому в этом разделе переключимся на другой пример. Допустим, агент может вызывать create_test_order для staging-сервиса заказов, пока подготавливает тестовые данные. Этот инструмент не входит в проверки приёмки задачи со скидкой. Он полезен здесь, потому что тайм-аут может скрыть, создал ли сервис заказ.
Границе инструмента нужно больше, чем описание на естественном языке. Нужен явный контракт инструмента. В части 3 такой контракт рассматривался со стороны модели: понятные действия, компактная обратная связь, восстанавливаемые ошибки. Харнессу нужен тот же контракт по другой причине. Он должен без участия модели решить, можно ли выполнять вызов и можно ли повторять неуспешный вызов. Для create_test_order это означает контракт со следующими элементами:
- валидированные аргументы, чтобы некорректный ввод отклонялся до выполнения;
- структурированный результат, например
{ "order_id": "123", "created": true }, чтобы последующим проверкам не приходилось парсить текст в свободной форме; - категория эффекта, фиксирующая, получает ли вызов только информацию или изменяет файл, запись в базе данных либо внешний сервис. Она также фиксирует, безопасно ли повторять вызов. Эта метка сообщает харнессу, может ли автоматический ретрай продублировать работу. Харнесс может повторить
get_order_status, если сервис определяет этот lookup как read-only. Нельзя бездумно повторятьcreate_test_order, потому что первый вызов уже мог создать заказ; - политика тайм-аута и ретраев, чтобы потерянный ответ не запускал неограниченную последовательность вызовов;
- правило разрешений, определяющее необходимое одобрение. Чтение статуса заказа может выполняться автоматически, а создание заказа — требовать подтверждения.
Описание на естественном языке — это текст, который показывается модели. Например: «Создай тестовый заказ для проверки checkout». Это предложение помогает модели решить, когда предложить create_test_order. Но оно не авторизует вызов. В этом примере MCP-клиент харнесса валидирует аргументы, применяет собственные правила и перед отправкой проверяет доверие к серверу, требования к одобрению и безопасность ретраев. Так объединяются правила разрешений и проверки до вызова из части 4, с добавлением одного вопроса: можно ли повторно отправить уже завершившийся сбоем вызов.
MCP-сервер публикует клиенту описания инструментов и необязательные аннотации поведения. Некорректный или вредоносный сервер может описать state-changing инструмент как безопасный. Клиент, автоматически принимающий это утверждение, может без одобрения запустить или повторить create_test_order и создать дубликат. Поэтому спецификация MCP требует считать аннотации инструментов недоверенными, если сам сервер не является доверенным.
Спецификация не предписывает одну универсальную настройку доверия, поэтому для вашего деплоя нужна явная политика доверия; сервер не может сделать собственные аннотации доверенными. Эта политика определяет, какие метаданные могут влиять на решения о разрешениях или ретраях, а какие аннотации остаются только рекомендательными.
Ретрай state-changing вызова требует защиты от повторного воспроизведения
Часть 5 требует долговечного идентификатора операции для побочных эффектов, которые могут дублироваться при ретрае. Харнесс решает, когда этот ключ должен принять на себя такую роль. create_test_order создаёт заказ, но HTTP-ответ теряется. Харнесс видит тайм-аут и не может определить, завершил ли сервер запрос. Повторный вызов может создать второй заказ.
Сохраните принадлежащий приложению ID операции до отправки и свяжите его с одобренными аргументами. Используйте тот же ID при восстановлении той же операции с заказом, даже если модель сгенерировала новый ID tool call; ID модели храните отдельно для корреляции. Если payload изменился или истекло окно дедупликации провайдера, выполните reconciliation, а не отправляйте запрос вслепую повторно. Например, контракт Stripe разрешает удалять ключи спустя как минимум 24 часа.
Lookup статуса можно повторить, если сервис определяет его как read-only. Для вызова создания нужен ключ: клиент добавляет уникальный идентификатор запроса, а сервис при повторном получении этого идентификатора возвращает первый результат вместо создания ещё одного заказа. Без такой защиты харнесс должен проверить существование заказа или запросить решение человека перед новой попыткой. AWS документирует этот паттерн в своих рекомендациях по idempotent API.
Для приёмки нужны независимые подтверждения
Успешный ответ create_test_order подтверждает только то, что инструмент вернул данные. Он не доказывает, что coding-задача прошла тесты. Если последующий browser-тест зависит от созданного staging-заказа, харнесс должен валидировать схему ответа и всё равно запустить этот тест до принятия изменения кода.
Некоторые критерии нельзя свести к exit code. В отдельной задаче по визуальному дизайну эвалуатор в свежем контексте может сравнить отрендеренную страницу или диаграмму с письменной рубрикой — «свежий контекст» означает вторую сессию модели, которая начинает без истории рана и читает созданные артефакты, а не транскрипт. Прежде чем позволить такому результату решать, завершена ли задача, сравните его с человеческими ревью.
Миграция payment-адаптера требует handoff
Снова сменим задачу, но останемся в вымышленном репозитории магазина. Теперь агент должен перевести checkout с payment-адаптера v1 на v2. Работа затрагивает checkout-handler, payment-клиент, конфигурацию и тесты, поэтому может не уложиться в одну сессию модели — один непрерывный фрагмент контекста модели, завершённый рестартом или осознанным fresh start, а не перенесённый дальше.
До достижения лимита контекстного окна первой сессии она успевает изменить несколько файлов, запустить локальный payment-сэндбокс и оставить tests/payment_migration.spec.ts неуспешным. Этот browser acceptance-тест выполняет один платёж через адаптер v2 и проверяет записанный ID провайдера. Сводка диалога может сориентировать следующую сессию модели, но не может перезапустить сэндбокс или доказать, какие файлы сейчас изменены.
Следующая сессия должна восстановить три вещи:
| Что необходимо восстановить | Что это включает | Как это может сломаться |
|---|---|---|
| История диалога | Сообщения, tool calls и возвращённые результаты | Старые детали вытесняют текущую задачу |
| Рабочее окружение | Файлы, payment-сэндбокс и состояние browser-теста | В транскрипте сказано, что сервис работает, хотя он уже упал |
| Прогресс задачи | План, выполненные проверки, ожидающее одобрение, следующий шаг | Следующая сессия повторяет уже завершённую работу |
Компаκция заменяет старые сообщения короткой сводкой, чтобы текущая сессия могла продолжиться. Progress handoff фиксирует то, что нужно следующей сессии: текущую ветку, изменённые файлы, последнюю команду теста и её вывод, а также следующий нерешённый шаг.
Файл handoff — это документная память для следующей сессии модели. Чекпоинт уже может сохранять план, выполненные шаги, результаты и оставшуюся работу. Добавляйте handoff, если в следующем контексте эти сведения отсутствуют или непригодны, и проверяйте файлы и работающие сервисы по живому окружению.
Если старый диалог содержит устаревшие предположения, харнесс может начать новую сессию модели с этим handoff и текущим workspace. Замена упавшего worker и восстановление его процессов — отдельная задача восстановления рантайма.
Небольшому изменению документации эти механизмы могут не понадобиться. Для миграции payment-адаптера handoff нужен, если сохранённое состояние не содержит пригодного прогресса задачи, потому что следующая сессия модели должна восстановить и workspace, и статус задачи.
В экспериментах Anthropic с долгоживущими coding-агентами между сессиями использовались история git и файл прогресса. В более позднем отчёте о дизайне харнесса Anthropic отделяет compaction от handoff в свежем контексте и сообщает, что handoff добавляют оркестрацию, расход токенов и wall time, но не публикует цифры, которые связывали бы эти накладные расходы именно с handoff.
Используйте трейсы, чтобы различать три сбоя
Следующие три строки — иллюстративные наброски трейсов, а не измеренные раны и не вывод companion lab. Каждая строка показывает отдельный сбой и поэтому требует отдельной реакции харнесса.
| Что записывает трейс | Что произошло | Правильная реакция |
|---|---|---|
Read-only вызов get_order_status возвращает 503; state-changing вызов не выполняется | Временный lookup завершился сбоем | Повторить lookup с ограничением и backoff |
create_test_order завершается по тайм-ауту, затем lookup статуса находит заказ 123 с idempotency key checkout-42 | Сервис создал заказ, но ответ потерялся | Вернуть существующий заказ; не создавать новый |
Изменение и unit-тест проходят, но в трейсе нет результата для tests/checkout_discount.spec.ts на протестированном снапшоте | Отсутствует обязательное подтверждение приёмки | Оставить ран открытым и отправить browser acceptance-тест |
Сбой, похожий на временный, не делает безопасным для ретрая любой вызов. В первой строке показан read-only lookup. Во второй — state-changing запрос, поэтому idempotency key и статус на сервере определяют, разрешена ли ещё одна попытка создания. В третьей строке вообще нет сбоя инструмента: харнесс ещё не собрал подтверждение, необходимое для принятия изменения скидки.
Транскрипт чата записывает то, что видела модель. Он не может доказать, зафиксировал ли сервис заказ до исчезновения ответа. Трейс может предоставить такое подтверждение только при наличии релевантного результата сервера или lookup статуса; одного клиентского тайм-аута недостаточно, он оставляет исход неопределённым. Долговечные записи операции и приёмки должны связывать клиентский вызов, решение об одобрении, идентификатор операции, результат сервера или lookup статуса, протестированный снапшот и результат acceptance-теста. Трейсы могут показывать эти связи для отладки, но не должны становиться журналом восстановления. Эти поля сообщают харнессу, по какому из трёх путей он идёт.
| Повторяющийся симптом | Небольшое изменение для проверки | Что измерять |
|---|---|---|
| Read-only lookup временно завершается сбоем | Ограниченный ретрай с backoff | Доля восстановлений, дополнительные вызовы, wall time |
| Возобновлённые сессии повторяют завершённую работу | Структурированный progress handoff | Дублирующие tool actions после возобновления |
| При завершении отсутствуют обязательные тесты | Отклонять завершение, пока не пройдены все обязательные проверки | Задачи, принятые без полного набора проверок |
| Визуальные дефекты переживают детерминированные проверки | Эвалуатор в свежем контексте с рубрикой | Найденные дефекты, ложные отклонения, время ревью |
| Агент изменяет файлы за пределами своей области | Более узкие разрешения инструментов | Заблокированные вызовы и ручные переопределения |
| Вспомненные факты вытесняют текущую задачу | Ограничить число извлекаемых фактов; ранжировать перед добавлением | Токены, потраченные на retrieval, завершённые задачи, стоимость задачи |
Для необязательной помощи — например, планировщиков, сводок и дополнительных эвалуаторов — назовите сбой и измерьте, окупает ли компонент свою стоимость. Требования к авторизации, изоляции, приватности и обязательные проверки приёмки действуют, даже если обычные задачи проходят без них. Проверяйте эти ограничения на adversarial-кейсах и с помощью явных инвариантов; небольшой success-бенчмарк не может оправдать их удаление.
Превращение этих повторяющихся сбоев в версионируемый regression suite — отдельная задача. Я написал об этом отдельно в AI Agent Evaluation in Production.
Измеряйте по одному изменению за раз
Абляция измеряет, вызывает ли компонент харнесса ожидаемый эффект: компонент изменяют или удаляют, а остальные условия эксперимента оставляют фиксированными. Например, помогает ли linting редактора этой модели на данном наборе задач?
Используйте следующий протокол:
- Зафиксируйте версию модели, экземпляры задач, окружение, grader и промпты, не относящиеся к тестируемому компоненту.
- Дайте обоим вариантам одинаковый общий бюджет токенов, времени и денег.
- До запуска сравнения выберите число испытаний или правило остановки.
- Запускайте одни и те же экземпляры задач в обоих вариантах. Поскольку вывод модели варьируется, повторите каждую задачу несколько раз.
- Отчитывайтесь о среднем значении вместе с разбросом или доверительным интервалом.
- Считайте каждое начатое испытание, включая тайм-ауты, остановки по политике, падения харнесса и сбои эвалуатора.
Один показатель success rate может скрыть дорогой компонент. Как минимум отслеживайте принятые как завершённые сломанные задачи, стоимость и wall time на завершённую задачу, ошибки инструментов, дублирующиеся заказы, минуты ревью и ручные переопределения разрешений. Выбирайте метрику, которая отражает реальную стоимость для вашего продукта. Двухпроцентный рост числа завершённых задач — плохой компромисс, если он удваивает очередь на ревью.
Парный эксперимент по миграции payment-адаптера делает progress handoff измеримым. Каждая пара control/treatment начинается с одного и того же коммита репозитория и seed-чекпоинта, с той же моделью, задачей, grader и общим бюджетом. Единственный переключатель — handoff. Основная метрика считает дублирующиеся tool actions после возобновления: действие считается дублирующимся, если его операция и артефакт совпадают с шагом, который предыдущая сессия уже завершила.
В статье SWE-agent GPT-4 Turbo зафиксирована на 300 задачах SWE-bench Lite; сообщается о 18,0% решённых задач с полным интерфейсом против 11,0% у shell-only агента с готовой демонстрацией и 7,3% у того же агента без неё. Заголовочный разрыв в 10,7 процентного пункта измерен относительно baseline 7,3%; в части 3 те же три числа разобраны со стороны дизайна интерфейса. В статье также изменялись отдельные функции интерфейса:
| Изменение интерфейса | Решено |
|---|---|
| Полный интерфейс SWE-agent (reference, без изменений) | 18,0% |
| Редактор без linting | 15,0% |
| Полный файл вместо viewer на 100 строк | 12,7% |
| Полная история наблюдений вместо последних пяти | 15,0% |
Эти числа относятся к данной модели, бенчмарку и лимиту $4 на задачу. Три строки ниже reference — полезные тесты одного изменения: в каждом случае менялась одна функция интерфейса, а модель и схема эвалуации оставались фиксированными.
LangChain опубликовала более широкое сравнение с фиксированной моделью для deepagents-cli. Сообщается о росте на Terminal-Bench 2.0 с 52,8% до 66,5% при фиксированном gpt-5.2-codex, тогда как команда меняла системный промпт, инструменты и middleware. В публикации объединены несколько изменений; в ней нет доверительного интервала, сравнения с фиксированным общим бюджетом и таблицы абляций по отдельным изменениям. Поэтому результат не позволяет определить, какое изменение помогло. Названия моделей в этом разделе — те, которые фиксировались в соответствующих исследованиях на момент их проведения; переносим здесь протокол, а не список моделей.
Более новое сравнение показывает, почему конфигурация API должна входить в зафиксированный baseline. В отчёте ARC-AGI-3 от 29 июля 2026 года OpenAI сообщает, что score GPT-5.6 Sol на public set вырос с 13,3% до 38,3%, когда харнесс сохранял ризонинг и использовал compaction вместо удаления ризонинга и усечения истории. Метрика — Relative Human Action Efficiency, а не доля решённых задач. Это bundled comparison от провайдера; он не изолирует две настройки и не устанавливает effect size для продакшена. При обновлении фиксируйте API, сохранение ризонинга, политику compaction и бюджеты вместе с ID модели. Иначе кажущаяся регрессия модели может оказаться отсутствующей возможностью в адаптере.
Добавьте вмешательство провайдера в failure suite. misalignment_policy_violation должен перейти в путь остановки и ревью даже после streamed output; это не случай для временного ретрая. В части 4 описана его зависимая от API область. Проверьте, что харнесс прекращает отправку вызовов и записывает уже выполненные эффекты.
Отчёт Anthropic о долгоживущем приложении — это качественный case study для конкретного продукта, а не контролируемый бенчмарк. Приложение называется RetroForge и представляет собой редактор 2D-ретроигр; в Sprint 3 эвалуатор харнесса проверял 27 критериев, относящихся к его level editor. Работа началась на более ранних моделях Opus, а после выхода Opus 4.6 команда по одному убирала компоненты харнесса, чтобы выяснить, какие из них новая модель сделала избыточными. В отчёте говорится, что вызовы эвалуатора стали накладными расходами на задачах, которые Opus 4.6 мог надёжно выполнять самостоятельно, но по-прежнему помогали на границе возможностей модели. Этот пример показывает, почему при смене модели стоит заново проверять старую обвязку; он не оценивает общий effect size.
Сохраняйте возможность редактировать харнесс после подтверждения его ценности
Абляции помогают держать харнесс небольшим, но его код всё равно может пережить модель, под которую был настроен. Запрос вроде «маскировать секреты в каждом capture path» описывает поведение, а не файл. В продакшен-харнессе это поведение может быть распределено между этапами выполнения и общим состоянием. Прежде чем безопасно изменить его, нужно найти все места реализации — и coding-agent, которому вы поручаете эту задачу, должен сделать то же самое.
На исследовательской стадии можно использовать препринт Wang et al. 2026 года — Harness Handbook, где этот поиск назван локализацией поведения. Handbook строит карту кодовой базы харнесса, ориентированную на поведение. Статический анализ, не требующий вызовов модели, извлекает граф программы, а затем LLM организует его узлы по этапам выполнения.
Мейнтейнер или coding-agent начинает с обзора системы, открывает релевантный этап выполнения и спускается к привязанным к исходному коду записям для функции или файла. Реестр состояния фиксирует, где общее состояние записывается и считывается между этапами. Такая иерархия сохраняет компактность обзора, одновременно оставляя путь к исходному коду.
Актуальность — отдельное правило. Карта служит навигационной подсказкой; поведение определяет живой исходный код. Каждый локатор должен разрешаться относительно актуального репозитория. Handbook замораживает устаревшие записи вместо того, чтобы делать догадки, а каждый непустой diff синхронизирует затронутые записи заново.
Диаграмма сжимает цикл изменения: запрос только о поведении спускается по уровням handbook, каждый кандидат-локатор проверяется по живому репозиторию до записи плана, а каждый применённый diff повторно синхронизирует карту.
Эвалуация Handbook сравнивает сопоставленные arms на 30 запросах для каждого репозитория. Она не подтверждает равенство общих бюджетов, повторные стохастические испытания или оценки неопределённости; опубликованные сравнения исключают отсутствующие результаты и ошибки планировщика. Поэтому она иллюстрирует только часть описанного протокола. Исследование охватывает два open-source харнесса: Terminus-2 (шесть Python-файлов) и монорепозиторий Codex (2267 Rust-файлов). В каждом случае read-only планировщик на базе DeepSeek-V4-Pro либо напрямую исследовал репозиторий, либо выполнял маршрутизацию через handbook. Запросы, репозиторий, разрешения инструментов и decoding были идентичны в обоих arms. Три джаджа (GPT-5.5, Opus 4.8, DeepSeek-V4-Pro) оценивали каждый план изменения по локализации, контролю области и ризонингу — отметим, что один из джаджей является той же моделью, которая создавала планы. Победа означает, что score качества одного arm в диапазоне 0–100 превосходил score другого как минимум на три пункта; иначе сравнение judge–request считалось ничьёй. Опубликованная доля — это число побед, делённое на число валидных сравнений judge–request:
| Харнесс | Доля побед baseline | Доля побед с handbook | Токены планировщика |
|---|---|---|---|
| Terminus-2 (6 файлов) | 26,7% | 45,6% | −8,6% |
| Монорепозиторий Codex (2267 файлов) | 28,3% | 38,3% | −12,7% |
Планировщик с handbook чаще побеждал и в обоих репозиториях использовал меньше токенов. Но результат нужно рассматривать в этих условиях: три LLM-джаджа оценивали планы изменений, созданные одной моделью-планировщиком, для двух харнессов. В исследовании оценивались планы, а не применённые diff или частота дефектов в продакшене.
Попробуйте метод в companion lab
Проект harness-demo на коммите 517353f3 — это небольшое детерминированное упражнение с 12 общими синтетическими задачами, охватывающими изменения кода вроде fix-parser-edge-case, split-large-module и wire-browser-test. Вымышленный репозиторий магазина в нём не реализован.
Каждая фикстура задачи задаёт сложность и четыре булевых условия: flaky-инструмент, потерянный прогресс, пропущенный участок реализации и неоднозначное завершение. Симулятор выводит пятое условие для сложных задач, которым также нужен файл прогресса: без context_reset compaction сохраняет устаревшие предположения. Детерминированный grader отмечает задачу как пройденную только тогда, когда выбранная конфигурация обрабатывает каждое применимое условие. Модель или внешний сервис не запускаются.
Команды отвечают на разные вопросы:
make checkзапускает Ruff и семь unit-тестов, включая валидатор, отклоняющий любую пару абляций, в которой изменено более одного компонента.make runпечатает накопительную учебную матрицу, а затем пять валидных сравнений leave-one-component-out.make failuresназывает необработанное условие для каждой неуспешной задачи. Полный харнесс должен завершиться сall synthetic tasks pass.
make check
make run
make failures
Причинный раздел make run выглядит так:
component control treatment delta
retry_policy 8/12 12/12 +4
progress_handoff 7/12 12/12 +5
evaluator 8/12 12/12 +4
fail_closed_acceptance 7/12 12/12 +5
context_reset 10/12 12/12 +2
В каждой строке control — это полная конфигурация с удалённым одним компонентом; treatment восстанавливает только этот компонент. Предыдущая накопительная матрица полезна для ориентации, но некоторые соседние строки добавляют сразу несколько компонентов и поэтому не позволяют определить причину.
Перед запуском сравнения lab валидирует каждую объявленную пару. Его regression-тесты также содержат намеренно некорректную пару, в которой одновременно меняются политика ретраев и эвалуатор; валидатор её отклоняет.
При валидации пары lab сравнивает все пять полей компонентов. Следующий исполняемый фрагмент показывает ту же защиту на одной валидной паре с progress handoff:
from dataclasses import dataclass, fields
@dataclass(frozen=True)
class Config:
progress_handoff: bool = False
evaluator: bool = False
retry_policy: bool = False
fail_closed_acceptance: bool = False
context_reset: bool = False
def changed_components(control: Config, treatment: Config) -> tuple[str, ...]:
return tuple(
field.name
for field in fields(control)
if getattr(control, field.name) != getattr(treatment, field.name)
)
control = Config(progress_handoff=False, evaluator=True, retry_policy=True)
treatment = Config(progress_handoff=True, evaluator=True, retry_policy=True)
assert changed_components(control, treatment) == ("progress_handoff",)
Какой слой открыть при сбое рана
Серия двигалась от цикла ризонинга наружу. Начните с первого обнаруженного сбоя, а затем исследуйте компонент, отвечающий за эту задачу. В одном ране может участвовать несколько компонентов:
| Что сделал ран | Где находится исправление | Часть |
|---|---|---|
| Выбрал плохой следующий шаг, хотя нужная информация уже была перед ним | Цикл ризонинга или модель | 1 |
| Повторял работу или потерял решение, принятое час назад | Сборка контекста и handoff | 2 |
| Не смог выразить нужное действие или неправильно прочитал возвращённый результат | Контракт инструмента | 3 |
| Сделал то, что ему вообще нельзя было делать | Правила разрешений | 4 |
| Потерял всё после смерти worker посреди вызова | Сессия, чекпоинт, сэндбокс | 5 |
| Объявил завершённой невыполненную работу | Проверки приёмки и трейсы | 6 |
Четыре строки указывают на код харнесса, а строка 5 — на рантайм. Инструкции могут влиять на поведение, но не могут заменить проверку разрешений, долговечный чекпоинт или acceptance-тест.
Начните с одного цикла и одной проверки приёмки
Я бы начал харнесс coding-агента с одной сильной моделью, инструкций репозитория, нескольких узких инструментов, сэндбокса и одной явной acceptance-проверки. Я записывал бы tool calls, результаты, затраты и этот финальный тест в один трейс, чтобы первые полезные сбои были видны без реконструкции по логам терминала и транскриптам чата. Это предлагаемый baseline, а не свидетельство из задеплоенной системы.
Дальше добавляйте только то, что оправдано трейcом. Фиксируйте, кто поддерживает каждый компонент, сколько токенов или секунд он добавляет и какой regression-тест позволил бы удалить его после обновления модели.
Через шесть месяцев человек, увидевший progress_handoff=True, должен суметь найти трейсы сбоев, оправдавшие его добавление, и regression-кейсы, из-за которых он всё ещё нужен. Трейсы объясняют, почему компонент существует; актуальная карта поведения объясняет, где его изменять.
Если вы попали сюда из поиска, пять предыдущих статей построили систему вокруг цикла ризонинга:
- Цикл выбирает следующий шаг.
- Память поставляет контекст, а настоящее хранилище чекпоинтов Postgres сохраняет его.
- Контракты инструментов определяют действия и формы результатов, которые могут читать последующие проверки.
- Безопасность добавляет deny hook и валидатор stop-hook. В примере оба остаются набросками, но обозначают точки управления.
- Рантайм поддерживает процесс между сессиями и при сбоях.
В серии также добавлены необязательная поверхность MCP-сервера и узел эвалуатора, который проверяет черновой отчёт до того, как его увидит человек. Маршрутизация worker через прокси, хранящий credentials, остаётся предлагаемым расширением. Это обычные фрагменты кода вокруг вызова модели. Роутер — код харнесса по той же причине: он выбирает паттерн ризонинга до запуска цикла ризонинга.
Для следующего необязательного компонента помощи храните вместе трейс сбоя, правило приёмки и сравнение с отключённым компонентом. Не добавляйте компонент, если не можете определить его пользу. Обязательные ограничения безопасности и приёмки не зависят от такого сравнения.
Ссылки
- OpenAI, Unrolling the Codex agent loop.
- OpenAI, Harness engineering: leveraging Codex in an agent-first world.
- Lopopolo, Harness engineering: anthology, field guide, and agent context bundle.
- Anthropic Engineering, Effective harnesses for long-running agents.
- Anthropic Engineering, Harness design for long-running application development.
- LangChain, Improving Deep Agents with harness engineering.
- Yang et al., SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering.
- Wang et al., Harness Handbook: Making Evolving Agent Harnesses Readable, Navigable, and Editable, arXiv:2607.13285, 2026.
- AWS, Making retries safe with idempotent APIs.
- Model Context Protocol, Tools specification.
- Market Analyst Agent Repository
Код Market Analyst Agent находится на GitHub.