Engineering the Agentic Stack · Часть 6

Харнесс-инжиниринг для ИИ-агентов: проектирование контуров управления

Автоматический перевод Эта статья была автоматически переведена с оригинальной английской версии.

Обновление статьи

Первоначально опубликовано 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, проверки приёмки и трейс

Где находится каждая часть серии Engineering the Agentic StackГде находится каждая часть серии Engineering the Agentic Stack

Части 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. До того как это изменение станет прогрессом, происходит несколько вещей:

  1. Сборщик контекста передаёт задачу, инструкции репозитория, релевантные файлы, результаты предыдущих tool calls и текущий план.
  2. Модель предлагает вызов edit_file с путём и текстом замены.
  3. Граница инструмента (код харнесса между предложением и выполнением) валидирует аргументы, проверяет путь на соответствие разрешённой области и запрашивает одобрение, если операция этого требует.
  4. Рантайм применяет изменение в сэндбоксе и возвращает структурированный результат.
  5. Харнесс запускает pytest tests/test_checkout.py, затем pnpm playwright test tests/checkout_discount.spec.ts и считывает оба exit code. Browser-тест проверяет видимую скидку $8 в корзине за $80, заполненной seed-данными.
  6. Харнесс решает, что означают результаты. Неуспешная проверка становится новым контекстом для следующего хода модели, а успешный ран делает задачу кандидатом на завершение.
  7. Успешный результат становится подтверждением завершения только после того, как харнесс надёжно записывает команду, 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 редактора этой модели на данном наборе задач?

Используйте следующий протокол:

  1. Зафиксируйте версию модели, экземпляры задач, окружение, grader и промпты, не относящиеся к тестируемому компоненту.
  2. Дайте обоим вариантам одинаковый общий бюджет токенов, времени и денег.
  3. До запуска сравнения выберите число испытаний или правило остановки.
  4. Запускайте одни и те же экземпляры задач в обоих вариантах. Поскольку вывод модели варьируется, повторите каждую задачу несколько раз.
  5. Отчитывайтесь о среднем значении вместе с разбросом или доверительным интервалом.
  6. Считайте каждое начатое испытание, включая тайм-ауты, остановки по политике, падения харнесса и сбои эвалуатора.

Один показатель success rate может скрыть дорогой компонент. Как минимум отслеживайте принятые как завершённые сломанные задачи, стоимость и wall time на завершённую задачу, ошибки инструментов, дублирующиеся заказы, минуты ревью и ручные переопределения разрешений. Выбирайте метрику, которая отражает реальную стоимость для вашего продукта. Двухпроцентный рост числа завершённых задач — плохой компромисс, если он удваивает очередь на ревью.

Парный эксперимент по миграции payment-адаптера делает progress handoff измеримым. Каждая пара control/treatment начинается с одного и того же коммита репозитория и seed-чекпоинта, с той же моделью, задачей, grader и общим бюджетом. Единственный переключатель — handoff. Основная метрика считает дублирующиеся tool actions после возобновления: действие считается дублирующимся, если его операция и артефакт совпадают с шагом, который предыдущая сессия уже завершила.

Парный абляционный тест progress handoffПарный абляционный тест progress handoff

В статье 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%
Редактор без linting15,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 харнессаМаршрутизация изменения поведения через handbook харнесса

Эвалуация 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
Повторял работу или потерял решение, принятое час назадСборка контекста и handoff2
Не смог выразить нужное действие или неправильно прочитал возвращённый результатКонтракт инструмента3
Сделал то, что ему вообще нельзя было делатьПравила разрешений4
Потерял всё после смерти worker посреди вызоваСессия, чекпоинт, сэндбокс5
Объявил завершённой невыполненную работуПроверки приёмки и трейсы6

Четыре строки указывают на код харнесса, а строка 5 — на рантайм. Инструкции могут влиять на поведение, но не могут заменить проверку разрешений, долговечный чекпоинт или acceptance-тест.


Начните с одного цикла и одной проверки приёмки

Я бы начал харнесс coding-агента с одной сильной моделью, инструкций репозитория, нескольких узких инструментов, сэндбокса и одной явной acceptance-проверки. Я записывал бы tool calls, результаты, затраты и этот финальный тест в один трейс, чтобы первые полезные сбои были видны без реконструкции по логам терминала и транскриптам чата. Это предлагаемый baseline, а не свидетельство из задеплоенной системы.

Дальше добавляйте только то, что оправдано трейcом. Фиксируйте, кто поддерживает каждый компонент, сколько токенов или секунд он добавляет и какой regression-тест позволил бы удалить его после обновления модели.

Через шесть месяцев человек, увидевший progress_handoff=True, должен суметь найти трейсы сбоев, оправдавшие его добавление, и regression-кейсы, из-за которых он всё ещё нужен. Трейсы объясняют, почему компонент существует; актуальная карта поведения объясняет, где его изменять.

Если вы попали сюда из поиска, пять предыдущих статей построили систему вокруг цикла ризонинга:

  1. Цикл выбирает следующий шаг.
  2. Память поставляет контекст, а настоящее хранилище чекпоинтов Postgres сохраняет его.
  3. Контракты инструментов определяют действия и формы результатов, которые могут читать последующие проверки.
  4. Безопасность добавляет deny hook и валидатор stop-hook. В примере оба остаются набросками, но обозначают точки управления.
  5. Рантайм поддерживает процесс между сессиями и при сбоях.

В серии также добавлены необязательная поверхность MCP-сервера и узел эвалуатора, который проверяет черновой отчёт до того, как его увидит человек. Маршрутизация worker через прокси, хранящий credentials, остаётся предлагаемым расширением. Это обычные фрагменты кода вокруг вызова модели. Роутер — код харнесса по той же причине: он выбирает паттерн ризонинга до запуска цикла ризонинга.

Для следующего необязательного компонента помощи храните вместе трейс сбоя, правило приёмки и сравнение с отключённым компонентом. Не добавляйте компонент, если не можете определить его пользу. Обязательные ограничения безопасности и приёмки не зависят от такого сравнения.


Ссылки


Код Market Analyst Agent находится на GitHub.