Engineering the Agentic Stack · Часть 6

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

Автоматический перевод

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

Агент может завершить свой ход, хотя работа ещё не закончена. Эта статья для инженеров, которые создают или проверяют агентов для программирования и должны решать, когда принять результат, повторить операцию с неопределённым исходом или продолжить работу в другой сессии. В ней также показано, как измерить, улучшает ли дополнительный компонент харнесса эти исходы настолько, чтобы оправдать свою стоимость.

Харнесс — это код приложения вокруг цикла ризонинга модели. Он предоставляет контекст, проверяет предложенные действия, записывает, что произошло, и решает, достаточно ли свидетельств для завершения. Рантайм выполняет принятые действия и поддерживает доступность сессии, сэндбокса и чекпоинта. Предыдущие статьи объясняют цикл ризонинга, память, инструменты и разрешения. Здесь вопрос в том, что делает харнесс после того, как модель предложила действие или сказала «готово».

Примеры с магазином и платежами ниже — вымышленные учебные случаи. Сопутствующая лаборатория — детерминированная симуляция; она не измеряет работающего агента для программирования.

Что считается завершением

Предположим, агента для программирования просят снизить порог автоматической скидки 10% с $100 до $75 в вымышленном репозитории магазина. Он изменяет src/checkout.py. Корзина на $80 теперь должна получать скидку $8. У задачи две обязательные проверки:

  • pytest tests/test_checkout.py проверяет расчёт.
  • pnpm playwright test tests/checkout_discount.spec.ts проверяет видимый результат checkout в локальном магазине с заранее подготовленными данными.

Правдоподобный финальный ответ не входит в эти проверки. Харнесс должен принимать только кандидата, чей полный протестированный снапшот идентифицирован и чьи две обязательные проверки прошли на этом же снапшоте. Идентифицировать его может чистый коммит; иначе используйте digest или манифест, включающий релевантные неотслеживаемые файлы. Держите тесты вне доступной агенту области записи либо независимо одобряйте их изменения до грейдинга.

Здесь у модели, харнесса и рантайма разные задачи. Реализация может объединять несколько из них в одном процессе, но границы их отказов остаются разными:

КомпонентЗадачаПример для агента для программирования
МодельПредлагает текст, tool call или финальный ответПредлагает изменить src/checkout.py
Цикл ризонингаВыбирает следующий шаг из доступного контекстаПроверить, изменить, протестировать, снова проверить
ХарнессПредоставляет контекст, валидирует и авторизует предложения, отправляет принятые вызовы, записывает результаты, проверяет завершениеРазрешает изменения в src/ и требует оба названных теста
РантаймВыполняет принятые вызовы и сохраняет состояние вне рабочего процессаЛог сессии, сэндбокс, хранилище чекпоинтов, бэкенд трейсинга

В разборе цикла Codex от OpenAI описан этот цикл: харнесс собирает промпт, запрашивает у модели следующее действие, отправляет принятый tool call в рантайм, добавляет результат и снова делает запрос, пока не примет результат или не вернёт управление пользователю. Для изменения скидки один проход выглядит так:

  1. Сборщик контекста передаёт задачу, инструкции репозитория, релевантные файлы, предыдущие результаты инструментов и текущий план.
  2. Модель предлагает вызов edit_file с путём и текстом замены.
  3. Граница инструмента валидирует аргументы, проверяет путь по разрешённой области и запрашивает одобрение, если операция этого требует.
  4. Рантайм применяет изменение в сэндбоксе и возвращает структурированный результат.
  5. Харнесс запускает pytest tests/test_checkout.py, затем pnpm playwright test tests/checkout_discount.spec.ts и считывает оба кода выхода.
  6. Неуспешная проверка становится новым контекстом для следующего хода модели; две успешные проверки делают изменение кандидатом на завершение.
  7. Харнесс сохраняет надолго команды, коды выхода, протестированный снапшот, грейдер и версии окружения; трейс может ссылаться на эту запись.

Один ход изменения скидки в трёх дорожках. Харнесс собирает контекст, модель предлагает вызов edit_file для src/checkout.py, и харнесс проверяет путь: src/checkout.py разрешён, а ../../secrets.env отклонён, до любого изменения файлов. Рантайм применяет правку в сэндбоксе. Харнесс запускает pytest и Playwright и читает коды выхода. Если оба теста не проходят на одном снапшоте, сбой возвращается на шаг 1 как контекст следующего хода; если проходят, харнесс записывает снапшот, команды, коды выхода, версии грейдера и окружения, а трейс ссылается на эту запись.Один ход изменения скидки в трёх дорожках. Харнесс собирает контекст, модель предлагает вызов edit_file для src/checkout.py, и харнесс проверяет путь: src/checkout.py разрешён, а ../../secrets.env отклонён, до любого изменения файлов. Рантайм применяет правку в сэндбоксе. Харнесс запускает pytest и Playwright и читает коды выхода. Если оба теста не проходят на одном снапшоте, сбой возвращается на шаг 1 как контекст следующего хода; если проходят, харнесс записывает снапшот, команды, коды выхода, версии грейдера и окружения, а трейс ссылается на эту запись.

После шага 2 ни один файл ещё не изменён. Харнесс всё ещё может отклонить ../../secrets.env, потребовать одобрения деструктивной команды или остановить запуск, исчерпавший бюджет. После шага 5 харнесс сам считывает коды выхода, поэтому модель не может сама пометить своё изменение как успешное.

Запись о завершении для этой задачи должна содержать снапшот кандидата, обе команды и их коды выхода, а также версии тестового окружения и грейдера. Она должна быть сохранённой надолго и находиться вне контроля кандидата. Трейс может связать предложение, одобренный вызов, результат рантайма и запись о приёмке для диагностики. Сам по себе трейс не является решением о приёмке.

Трейс тоже не обязательно содержит запрос. Он может записать исходный документ или событие middleware, не показывая, что в итоге содержал собранный запрос. Если нужно установить, что получила модель, фиксируйте это в последней точке перед отправкой вызова, после всех преобразований.

Исследование случая OpenAI по харнесс-инжинирингу описывает запускаемое приложение для каждого worktree, а также доступ к браузеру, логам, метрикам и трейсам. Такая настройка позволила агенту проверять цель по латентности для пользовательского сценария. Она показывает предпосылку любого правила приёмки: нужное приложение и сигналы должны быть доступны в тестовом окружении. Lopopolo, автор этого исследования случая, ведёт практическое руководство по харнесс-инжинирингу с теми же двумя способами: зафиксировать модель и проектировать контекст и инструменты вокруг неё. Требования к качеству организации, процедуры и правила одобрения лежат за пределами того, что знает универсальная модель; харнесс передаёт их через инструкции репозитория, правила разрешений и проверки приёмки.

Рассмотрим три внешне похожих исхода:

Свидетельства в конце запускаРешение харнессаПочему
Unit-тест прошёл; результата браузерного теста нетПродолжить или остановить как незавершённыйУ одной обязательной проверки нет результата
Оба теста прошли, затем агент изменил файлСнова запустить обе проверкиЗаписанные успешные прогоны относятся к старому снапшоту
Оба теста прошли на одном идентифицированном снапшотеДопускается к приёмкеОбязательные свидетельства соответствуют кандидату

«Допускается» оставляет место для одобрения человеком или другого обязательного правила; это не позволяет модели сертифицировать собственную работу. Эвалуатор в свежем контексте может проверить визуальный результат по рубрике, когда код выхода не может выразить критерий. Сравните такого эвалуатора с ревью людей, прежде чем позволить ему решать о завершении. Он не может заменить обязательную детерминированную проверку.

Для исследовательского агента из этой серии предлагаемое детерминированное правило завершения проверяло бы схему отчёта и наличие запрошенных тикеров. Эти проверки не могут установить, что анализ фактически верен; для этого по-прежнему нужна проверка источников. Руководство Anthropic по эвалуации рекомендует для задач программирования стабильные окружения и тщательные тесты, а для модельных грейдеров — калибровку людьми.

Где применяется каждое правило

Требование, чтобы tests/checkout_discount.spec.ts проходил, относится к детерминированному коду, а не к промпту. Промпт может напомнить модели запустить тест. Он не может помешать модели объявить успех без свидетельств. Другие правила подходят для других мест:

Где размещать правилоПодходящий случайПример
Промпт или skillПорядок поиска, соглашения по коду и формат планаПрочитать AGENTS.md перед изменением кода checkout
Граница инструментаВалидация аргументов, разрешённые пути, одобрения и доступ к инструментамРазрешать запись только в src/
Детерминированный кодБюджеты, таймауты, повторы, коды выхода тестов и требования к релизуОставлять запуск открытым, пока Playwright-тест не проходит
Эвалуатор в свежем контекстеВизуальная проверка или критерии, требующие сужденияСравнить сгенерированную диаграмму с письменной рубрикой

Каждая из предыдущих частей серии решает одну вещь внутри этого прохода:

Предыдущая частьЧто она решает для этого ходаГде она действует в проходе
Часть 2 — памятьКакое предыдущее состояние попадает в промптШаг 1, сборщик контекста
Часть 3 — tool useКакие действия существуют и как выглядит валидный результатВалидация на шаге 3 и результат на шаге 4
Часть 4 — безопасностьМожно ли выполнить этот конкретный вызов сейчасШаг 3, проверка пути и одобрение
Часть 6 — эта статьяЗавершает ли полученное свидетельство запускШаги 5–7, проверки приёмки

Таблица распределяет обязанности, а не порядок выполнения. Части 3 и 4 делят шаг 3: тот же код харнесса, который отклоняет некорректный аргумент, отклоняет и разрешённый, но ещё не одобренный вызов. Если валидация и авторизация выполняются в разных сервисах, передавайте валидированные аргументы через эту границу, чтобы решение об авторизации относилось к вызову, который действительно будет выполнен.

Считайте таймаут неизвестным исходом

Изменение скидки локально, и его тесты можно перезапустить. Миграция платежей в том же вымышленном магазине создаёт другую проблему. Предположим, агент вызывает create_test_order у staging-сервиса, и вызов завершается по таймауту. Сервис мог создать заказ, хотя харнесс так и не получил ответ. Слепой повтор может создать второй заказ.

Названия действия или сгенерированного моделью ID tool call недостаточно, чтобы его восстановить. До отправки сохраните принадлежащий приложению ID операции вместе с одобренными аргументами. Сервис должен дедуплицировать повторы с этим ID или предоставлять проверку статуса, позволяющую сверить исход. Используйте тот же ID для того же заказа после рестарта; изменённый запрос на заказ требует нового решения. Руководство AWS по идемпотентным API описывает клиентский идентификатор и атомарность на стороне сервера, которые делают это безопасным. Проверьте, как долго провайдер хранит ключи: контракт Stripe, например, разрешает удалять их не раньше чем через 24 часа, поэтому более поздний повтор требует сверки, а не повторной отправки.

На рисунке один вызов create_test_order с таймаутом разобран дважды: при слепом повторе и при повторе с сохранённым ID операции.

Две последовательности между харнессом и staging-сервисом заказов. При слепом повторе харнесс вызывает create_test_order, сервис создаёт заказ 123, ответ теряется, и харнесс видит таймаут; харнесс вызывает снова, и теперь у сервиса есть заказ 123 и второй заказ. При повторе с тем же ID операции харнесс сначала сохраняет ID операции и одобренные аргументы, вызывает create_test_order с ID, и ответ теряется. Он повторяет вызов с тем же ID, сервис не создаёт новый заказ и возвращает заказ 123, а харнесс записывает сверенный результат.Две последовательности между харнессом и staging-сервисом заказов. При слепом повторе харнесс вызывает create_test_order, сервис создаёт заказ 123, ответ теряется, и харнесс видит таймаут; харнесс вызывает снова, и теперь у сервиса есть заказ 123 и второй заказ. При повторе с тем же ID операции харнесс сначала сохраняет ID операции и одобренные аргументы, вызывает create_test_order с ID, и ответ теряется. Он повторяет вызов с тем же ID, сервис не создаёт новый заказ и возвращает заказ 123, а харнесс записывает сверенный результат.

Харнесс может принимать эти решения без обращения к модели, только если у каждого инструмента есть явный контракт. Для create_test_order этот контракт включает:

  • валидированные аргументы, чтобы некорректный ввод отклонялся до выполнения;
  • структурированный результат, например { "order_id": "123", "created": true }, чтобы последующим проверкам не приходилось парсить свободный текст;
  • категорию эффекта: только ли читает вызов или изменяет файл, запись или внешний сервис, и безопасно ли его повторять;
  • таймаут и политику повторов, чтобы потерянный ответ не запускал неограниченную последовательность вызовов;
  • правило разрешений: чтение статуса заказа может выполняться автоматически, а создание заказа может требовать подтверждения.
Результат, который видит харнессСледующее действие
Вызов только для чтения get_order_status возвращает 503Повторить с ограничением, если эту проверку безопасно повторять
create_test_order завершается по таймауту; проверка статуса находит заказ по его ID операцииИспользовать существующий заказ и записать сверенный результат
create_test_order завершается по таймауту; сервис не может дедуплицировать или проверить статусОставить исход неопределённым и запросить решение до нового создания

Последнее состояние должно пережить сбой рабочего процесса. Назвать его «неуспешным» было бы неверно: заказ может существовать. Постоянная запись операции должна содержать одобренный payload, ID операции, статус отправки, результат сервера или проверку статуса и любое одобрение. Трейс помогает найти эту запись; транскрипт показывает только то, что видела модель.

Описание инструмента помогает модели выбрать действие. Оно не выдаёт разрешение и не доказывает, что повтор безопасен. Если сервис заказов доступен через MCP, клиент всё равно должен применять собственную валидацию и политику доверия, а сервер — обеспечивать контроль доступа. Спецификация инструментов MCP требует, чтобы клиенты считали аннотации поведения инструментов недоверенными, если они не получены от доверенного сервера. Сервер, описывающий create_test_order как только для чтения, не может сделать его таким. Спецификация не задаёт единую настройку доверия, поэтому напишите для своего деплоя явную политику, указывающую, какие метаданные сервера могут влиять на решения о разрешениях или повторах.

У MCP есть и стандартный способ не терять контроль над долгим вызовом. С официальным расширением Tasks сервер может ответить на tools/call хендлом задачи вместо итогового результата. Клиент затем опрашивает задачу через tasks/get или останавливает работу через tasks/cancel. Спецификация Tasks говорит, что клиентам следует сохранять ID задач, чтобы опрос можно было возобновить после сбоя, поэтому храните ID задачи в той же записи операции. Статус completed не означает, что инструмент отработал успешно: результат инструмента с isError: true тоже завершает задачу, а в failed её переводит только ошибка протокола. Клиент и сервер согласуют поддержку для каждого запроса, поэтому перед использованием убедитесь, что обе стороны её реализуют.

Передайте прогресс следующей сессии

Теперь вымышленный агент должен перенести checkout с платёжного адаптера v1 на v2. Он изменил обработчик и клиент, запустил локальный платёжный сэндбокс и оставил tests/payment_migration.spec.ts в неуспешном состоянии. Задача длиннее одной сессии модели. Сводка старого разговора может утверждать, что сэндбокс работает, даже после того как процесс завершился.

Следующей сессии нужны три разных вида состояния:

СостояниеЧто следующая сессия должна проверить
РазговорЗапрос, решения и релевантные результаты инструментов
Рабочее окружение и рантаймТекущие файлы, ветку, процессы и тестовое окружение
Прогресс задачиВыполненные проверки, ожидающее одобрение или неопределённую запись, следующий шаг

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

Чекпоинт может сохранить состояние выполнения, но не доказывает, что внешняя платёжная операция зафиксирована или что процесс всё ещё жив. Передача прогресса помогает, когда в следующем контексте модели нет пригодного статуса задачи. Запишите ветку, изменённые файлы, последнюю команду и её результат, ожидающие ID операций и следующий нерешённый шаг. При возобновлении сверьте эти утверждения с текущим рабочим окружением и сервисом, прежде чем действовать. В экспериментах Anthropic с долгоживущими агентами для программирования история git и файл прогресса помогали последующим сессиям восстановить работу. Более поздний отчёт Anthropic о дизайне харнесса отмечает, что передача прогресса добавляет оркестрацию, токены и время выполнения.

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

Диагностируйте отказ, прежде чем добавлять компонент

Три случая выше дают разные трейсы: отсутствующий тест приёмки, неопределённая внешняя запись и повторная работа после возобновления. Их исправления находятся в разных местах. Отсутствие результата браузерного теста в записи о приёмке требует обязательной проверки завершения. Неопределённый заказ требует дедупликации или сверки на стороне сервера. Повторное изменение после новой сессии требует пригодного состояния прогресса.

Для следующего запуска записывайте задачу и версию харнесса, предложенное действие, одобрение, результат инструмента, протестированный снапшот, результат приёмки, латентность и стоимость. Учитывайте количество входных токенов отдельно от оплаченной стоимости входа, включая чтения из кэша: два харнесса, которые ведут себя одинаково, могут стоить по-разному в зависимости от того, как каждый собирает свой запрос и был ли кэш провайдера прогретым. Не допускайте в трейсы секреты и лишние персональные данные. Связывайте внешние записи операций по ID, поскольку таймаут на стороне клиента не может показать, что сделал сервер. Когда отказ повторяется, напишите регрессионный кейс, который его воспроизводит. Этот процесс превращения трейсов в тесты развивает статья Эвалуация ИИ-агентов в продакшене.

Каждый повторяющийся симптом подсказывает небольшое изменение и метрику, которая покажет, помогло ли оно:

Повторяющийся симптомНебольшое изменение для проверкиЧто измерять
Проверки статуса только для чтения временно завершаются ошибкойОграниченный повтор с backoffДоля восстановлений, дополнительные вызовы, время выполнения
Возобновлённые сессии повторяют выполненную работуСтруктурированная передача прогрессаДублирующие действия инструментов после возобновления
При завершении отсутствуют обязательные тестыОтклонять завершение, пока не пройдут все обязательные проверкиЗадачи, принятые без всех обязательных проверок
Визуальные дефекты переживают детерминированные проверкиЭвалуатор в свежем контексте с рубрикойНайденные дефекты, ложные отклонения, время ревью
Агент изменяет файлы за пределами своей областиБолее узкое разрешение инструментаЗаблокированные вызовы и ручные переопределения
Извлечённая память вытесняет текущую задачуОграничить число извлекаемых фактов; ранжировать перед добавлениемТокены на извлечение, выполненные задачи, стоимость
Заявленное правило или статус не доходит до моделиФиксировать собранный запрос перед отправкой вызоваЗапросы без правила; число раундов до его повторного появления

Прежде чем добавлять политику повторов, передачу прогресса или эвалуатор, назовите отказ, который он должен изменить, и метрику, которая покажет улучшение. Учитывайте и стоимость: больше принятых задач бесполезно, если дополнительные вызовы удваивают время ревью или создают дублирующие заказы. Авторизация, изоляция, правила приватности и обязательные проверки приёмки — это требования; небольшой бенчмарк успеха не повод их убирать. Тестируйте эти ограничения явными запрещёнными кейсами.

Измеряйте по одному опциональному изменению за раз

Абляция сравнивает харнесс с одним компонентом и без него, оставляя остальной эксперимент фиксированным. Используйте следующий протокол:

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

Для миграции платежей кандидатом служит опциональная передача прогресса. Начинайте каждую пару control–treatment с одного и того же коммита репозитория и подготовленного чекпоинта, где передача прогресса — единственный переключатель.

Основной метрикой могут быть дублирующие действия инструментов после возобновления: операция над артефактом, который предыдущая сессия уже завершила. Также сообщайте принятые задачи, пропущенные дефекты, время выполнения, стоимость модели и эвалуатора и минуты ревью. Выберите число испытаний и правило остановки заранее, затем сообщайте вариацию, а не только среднее. Если передача прогресса сокращает дубликаты, но стоит больше, чем экономит, это полезный отрицательный результат.

Таблица показывает, что версия модели и эндпоинт, экземпляры задач, коммит репозитория и заданный чекпоинт, окружение и лимиты ресурсов, грейдер и промпты и бюджет одинаковы в обеих группах, проверки безопасности и приёмки включены в обеих, а различается только передача прогресса: выключена в контроле, включена в эксперименте. Сетка сопоставляет один контрольный запуск и один запуск с передачей прогресса от одного старта для каждой задачи и повтора; один запуск с передачей прогресса завершился по таймауту и всё равно считается. Карточка перечисляет, что сообщать по каждой группе: сначала дублирующие вызовы инструментов после возобновления, затем принятые задачи, пропущенные дефекты, время выполнения, стоимость и минуты ревью, со средним и его разбросом.Таблица показывает, что версия модели и эндпоинт, экземпляры задач, коммит репозитория и заданный чекпоинт, окружение и лимиты ресурсов, грейдер и промпты и бюджет одинаковы в обеих группах, проверки безопасности и приёмки включены в обеих, а различается только передача прогресса: выключена в контроле, включена в эксперименте. Сетка сопоставляет один контрольный запуск и один запуск с передачей прогресса от одного старта для каждой задачи и повтора; один запуск с передачей прогресса завершился по таймауту и всё равно считается. Карточка перечисляет, что сообщать по каждой группе: сначала дублирующие вызовы инструментов после возобновления, затем принятые задачи, пропущенные дефекты, время выполнения, стоимость и минуты ревью, со средним и его разбросом.

Статья SWE-agent даёт ограниченный пример абляции интерфейса. С GPT-4 Turbo на 300 задачах SWE-bench Lite и лимитом $4 на задачу в ней менялась одна функция интерфейса за раз:

Изменение интерфейсаРешено
Полный интерфейс SWE-agent (эталон)18,0%
Редактор без линтинга15,0%
Полный файл вместо просмотра по 100 строк12,7%
Полная история наблюдений вместо последних пяти15,0%

Это результаты для той модели и того бенчмарка, а не ожидаемый выигрыш для вымышленного магазина. Переносится метод: протестируйте названный выбор интерфейса в фиксированной настройке и сообщайте условия вместе с числом.

Объединённые сравнения встречаются часто, и они не могут назвать изменение, которое помогло. LangChain сообщает о росте deepagents-cli на Terminal-Bench 2.0 с 52,8% до 66,5% при фиксированной модели после одновременного изменения системного промпта, инструментов и middleware. Отчёт OpenAI об ARC-AGI-3 показывает обратный риск: оценка почти утроилась, когда харнесс сохранял ризонинг и использовал компактификацию, — две настройки API изменились одновременно. Записывайте API, сохранение ризонинга, политику компактификации и бюджеты вместе с ID модели; иначе кажущаяся регрессия модели может оказаться недостающей возможностью адаптера.

Тестирование по одному изменению ловит и те, что кажутся слишком мелкими, чтобы иметь значение. Постмортем Anthropic по Claude Code за апрель 2026 сообщает, что API не затронут, но качество снизили три изменения на уровне продукта: меньшее значение усилия ризонинга по умолчанию, баг, из-за которого после простоя сессии на каждом ходу стирался прежний ризонинг, и строка системного промпта, ограничивавшая текст между tool calls. Когда Anthropic провела абляцию этой одной строки на более широком наборе эвалуаций, одна из них показала падение на 3% и для Opus 4.6, и для Opus 4.7. Строка промпта или значение по умолчанию — это изменение харнесса; тестируйте его как таковое.

Заново проверяйте компоненты при смене модели. Отчёт Anthropic о долгоживущем приложении — качественное исследование случая: после выхода Opus 4.6 команда поочерёдно убирала компоненты харнесса и обнаружила, что вызовы эвалуатора стали накладными расходами на задачах, которые модель теперь выполняла сама, но всё ещё помогали на границе её возможностей.

Включите в набор отказов остановку по безопасности на стороне провайдера: она должна прекращать отправку и переводить запуск на ревью, а не на повтор. Подробности описаны в части 4.

Попробуйте сравнение в сопутствующей лаборатории

Проект harness-demo на коммите 517353f3 делает правило абляции запускаемым. Каждая из двенадцати универсальных синтетических задач, например fix-parser-edge-case, split-large-module и wire-browser-test, объявляет сложность и четыре условия: нестабильный инструмент, потерянный прогресс, пропущенный пробел в реализации и неоднозначное завершение. Для сложных задач, которым также нужен файл прогресса, симулятор выводит пятое: без context_reset компактификация сохраняет устаревшие предположения. Чистая функция помечает задачу как пройденную, когда выбранная конфигурация обрабатывает каждое применимое условие. Здесь нет модели, внешнего инструмента или реальной задачи программирования. Исходы и число токенов — симулированные учебные данные, а не свидетельство того, что харнесс из пяти компонентов улучшает работающего агента.

Клонируйте закреплённую ревизию и запустите проверки и симуляцию, установив uv:

git clone https://github.com/slavadubrov/harness-demo.git
cd harness-demo
git checkout 517353f3a47541d66099983170fe90812b6ef23b
make check
make run
make failures

make check запускает Ruff и семь unit-тестов, включая тест, который отклоняет сравнение с изменением двух компонентов. make run выводит накопительную учебную матрицу и пять пар leave-one-component-out. make failures называет каждый симулированный отказ; полная конфигурация завершается строкой all synthetic tasks pass.

Компонент, возвращённый в полную конфигурациюБез негоС ним
Политика повторов8/1212/12
Передача прогресса7/1212/12
Свежий эвалуатор8/1212/12
Fail-closed приёмка7/1212/12
Сброс контекста10/1212/12

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

model.py лаборатории защищает от того, чтобы называть объединённое изменение абляцией: он сравнивает каждое поле компонента в паре, а его регрессионный тест передаёт некорректную пару, в которой изменены и повторы, и эвалуатор. Эта самодостаточная версия защиты запускается на одной валидной паре с передачей прогресса:

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",)

Чтобы перенести метод в реальный проект, замените синтетические флаги задач версионированным набором задач, а симулированное число токенов — измерениями из трейсов. Держите правила приёмки вне контроля кандидата. Одна лишь фиксированная модель не делает сравнение контролируемым, если изменились также промпты, тестовые данные, разрешения или настройки ризонинга и компактификации в API.

Найдите код, отвечающий за поведение

Харнесс, прошедший свои абляции, всё равно может пережить модель, под которую его настраивали, а чтобы потом его изменить, нужен код, реализующий конкретное поведение. Запрос вроде «маскировать секреты в каждом пути захвата» называет поведение, а не файл. В харнессе в продакшене такое поведение может охватывать несколько этапов выполнения и общее состояние.

Препринт Wang et al. 2026 года предлагает для такого поиска Harness Handbook; статья называет этот поиск локализацией поведения. Статический анализ извлекает граф программы, LLM группирует его элементы по этапам выполнения, а мейнтейнер или агент для программирования спускается от обзора системы к привязанным к исходному коду записям для функции или файла. Каждая запись должна разрешаться в текущем репозитории и повторно синхронизируется после каждого изменения.

Запрос «маскировать секреты во всех путях захвата» попадает в справочник через обзор системы, который ведёт к четырём стадиям выполнения; запрос затрагивает стадии 2 и 4. Каждая из этих стадий перечисляет записи для функции или файла. Каждая запись сверяется с текущим репозиторием: три находятся, одна не найдена. В план правок попадают только найденные записи, а справочник синхронизируется заново после каждого непустого диффа.Запрос «маскировать секреты во всех путях захвата» попадает в справочник через обзор системы, который ведёт к четырём стадиям выполнения; запрос затрагивает стадии 2 и 4. Каждая из этих стадий перечисляет записи для функции или файла. Каждая запись сверяется с текущим репозиторием: три находятся, одна не найдена. В план правок попадают только найденные записи, а справочник синхронизируется заново после каждого непустого диффа.

На двух open-source харнессах планировщик, работавший через справочник, создавал планы изменений, которые джаджи предпочитали чаще, и тратил меньше токенов. Исследование оценивало планы, а не выполненные изменения, и не сообщало о неопределённости, поэтому считайте его подходом на стадии исследования.

По отказу найдите ответственного

Серия двигалась от цикла ризонинга наружу. Когда запуск завершается неудачей, найдите первую обязанность, которая могла предотвратить или обнаружить отказ:

Что произошлоЧто исследовать
Плохой следующий шаг при наличии нужной информацииМодель и цикл ризонинга
Потеряно предыдущее решение или повторена выполненная работаСборку контекста и память
Не удалось выразить действие или прочитать его результатКонтракт инструмента
Выполнено запрещённое действиеПроверку разрешений и сервис, который её применяет
Рабочий процесс завершился аварийно, и его окружение исчезлоРантайм и чекпоинт
Успех объявлен без обязательных свидетельствПравило приёмки харнесса и протестированный снапшот

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

Ссылки

Код Market Analyst Agent из этой серии находится на GitHub.