Инженерный harness: почему "готово" недостаточно
Содержание
TL;DR: Агент-исполнитель (producer-agent) сообщил, что блокирующих замечаний нет. Независимый агент-рецензент (reviewer-agent) нашёл нарушения контракта и безопасности, которых не заметила самопроверка. OpenAI описывает понятный агенту репозиторий, исполняемые ограничения и обратную связь; Anthropic разделяет agent harness и evaluation harness. Из этих подходов я взял повторяющиеся механизмы и связал их в авторскую практическую модель из пяти слоёв. Общепринятой сквозной модели для AI-агентов, которые меняют код, я в проверенных материалах не нашёл. Моя модель не решает за меня, что правильно для бизнеса, а показывает, что именно проверено до слияния.
На экране всё укладывалось в три действия: ввести номер телефона, получить код по SMS и подтвердить его. После подтверждения сервер создавал сессию и возвращал cookie, чтобы браузер узнавал пользователя в следующих запросах. За этим коротким маршрутом обработчик принимал публичный запрос, разбирал ответ SMS-провайдера и создавал сессию. Одна небольшая задача пересекла сразу три чувствительные границы: контракт API, обработку внешней ошибки и безопасность cookie.
Такая задача кажется идеальной для AI-агента: небольшой обработчик, понятный результат, зелёная сборка. На проекте изменение проходило через агентский процесс. Сначала агент-исполнитель менял код и приносил дифф с отчётом. Затем агент-рецензент получал ту же задачу и артефакты, но заново проверял контракт и безопасность. Он мог остановить изменение, а решение о слиянии оставалось за мной. В этой статье весь управляемый путь от задачи до такого решения я называю engineering harness. Разделение ролей было практическим: проверяя собственный код, исполнитель легко сохраняет те же предположения, с которыми писал этот код.
Исполнитель закончил первый проход с вердиктом APPROVED_WITH_NOTES, no blockers. Изменение собрано, доступные проверки зелёные, замечания перечислены. Причин для остановки агент не видел.
Рецензент получил ту же задачу, дифф и отчёт проверки, но остановил изменение до слияния. Он нашёл три проблемы:
- cookie, установленная из JavaScript, не сохраняла
HttpOnly; - сырое тело ошибки могло попасть в структурированные логи вместе с персональными данными (PII);
- публичный контракт запроса стал слабее.
Эпизод собран из нескольких рабочих прогонов. Сами ошибки и факт независимого ревью взяты из исходных материалов. Конкретные исправления и дополнительные проверки ниже не были продолжением одного наблюдаемого запуска: я собрал их позже как реалистичный следующий шаг.
Код ниже – специально собранный пример. Он показывает те же ошибки в контракте и безопасности, но не копирует код исходного проекта.
Что должно произойти между задачей и слиянием, чтобы слово готово можно было перепроверить, а не принять на веру?
"Blockers нет". Почему я остановил задачу
Отчёт исполнителя был полезным: в нём остались дифф, выполненные команды, результаты самопроверки и честная пометка о доступных проверках. По такому отчёту можно восстановить ход работы и быстрее найти сбой. Но открывать путь к слиянию своим же отчётом исполнитель не должен.
Рецензент начал проверку заново, а не продолжил самоотчёт исполнителя. Он вернулся к задаче и ещё раз сверил изменение с публичным контрактом, требованиями безопасности и результатами проверок. Исполнитель видел выполненную задачу, а рецензент – причины её остановить.
Я и сам слишком легко принимал хороший отчёт за почти завершённую работу: ход понятен, проверки зелёные, хвосты перечислены. Рецензент показал, чего не хватало.
После этого я стал заранее отвечать на два вопроса: какую проверку рецензент обязан повторить и какой результат должен остановить слияние. Это и есть право остановки. Без него даже хороший отчёт агента заканчивается тем, что я снова вручную перечитываю весь код.
В этом эпизоде рецензент действительно остановил нарушения контракта и безопасности до слияния. Что было дальше, исходные материалы не показывают. Поэтому утверждаю только одно: нельзя решать, сливать ли изменение, по одному самоотчёту исполнителя.
Почему же подробной задачи и зелёных проверок всё равно не хватило?
Что я дал агенту кроме инструкции
Исполнитель получил не сообщение в чате, а файл задачи, который хранился в репозитории вместе с кодом. Ниже – сокращённая схема для статьи. Полный файл примера также содержит ID задачи, отдельную команду self-check и ID каждой команды.
{
"goal": "Return a hardened session cookie after SMS confirmation",
"allowedPaths": ["AuthApplication.cs", "Contracts.cs"],
"commands": [
"dotnet build HarnessExample.csproj -c Release",
"dotnet run --project HarnessExample.csproj -c Release --no-build -- gate task.json artifacts/control.json"
],
"evidencePolicy": {
"required": ["contract.required-nullability", "auth.cookie-header"],
"optional": ["auth.browser-tls"]
}
}
goal задаёт результат: после подтверждения по SMS приложение возвращает защищённую cookie сессии. allowedPaths показывает, где ожидается изменение. commands фиксирует команды проверки. evidencePolicy заранее отделяет обязательные проверки от необязательной, для которой нужно честно указать причину пропуска.
Но allowedPaths – пока только подсказка. Наш пример не читает дифф и не проверяет изменённые пути. Более того, разрешённый файл ещё не разрешает менять в нём всё подряд: попутный рефакторинг в AuthApplication.cs может не иметь отношения к задаче. Если список путей должен реально блокировать слияние, нужна отдельная проверка, которую исполнитель не сможет тихо отключить.
Я ожидал от хорошей постановки слишком многого. Файл задачи действительно убрал часть догадок: исполнитель заранее видел цель, нужный фрагмент кода, команды и форму отчёта. Но ясная задача не делает самопроверку независимой. Агент проверяет код с теми же предположениями, с которыми его писал.
Поэтому файл задачи остался обязательным, но сам по себе не давал права на слияние. Рецензент отдельно проверял последствия, которых не заметил исполнитель.
Почему я не доверил слияние самооценке агента
Каждое из трёх решений выглядело нормальным в своей узкой части системы. Проблема стала видна только после проверки соседних контрактов.
Cookie. После успешного подтверждения исполнитель записывал cookie сессии через document.cookie. Сценарий работал в браузере, и локальная самопроверка проходила. Но JavaScript не может установить атрибут HttpOnly. В примере, который я собрал позже, cookie выдаёт сервер, а проверка заголовка Set-Cookie ищет HttpOnly, Secure и SameSite=Strict.
Логи. Исполнитель сохранял сырое тело ошибки: так удобнее разбирать сбой внешнего сервиса. Но вместе с телом ответа в структурированные логи могла попасть PII. В следующей версии процесса я оставил бы только статус, тип ошибки и идентификатор корреляции. Саму утечку исходные материалы не подтверждают.
Публичный контракт. После замены required string на string? модель по-прежнему компилировалась, но тип больше не обещал обязательный номер телефона. В исполняемом примере проверка через рефлексию ищет запрет null и RequiredMemberAttribute, поэтому такое ослабление не проходит незаметно. Она проверяет только форму типа, а не поведение реального потребителя или runtime-валидацию. Реального сбоя у потребителя в исходных материалах нет.
Схема во всех трёх случаях одна. Исполнитель проверяет, работает ли само изменение. Рецензент смотрит шире: не сломались ли соседние контракты. А я решаю, какую проверку добавить в следующий запуск.
Исходные материалы не показывают, что эти три проблемы исправлялись одной последовательностью. Дальше я воспроизвожу ошибки с cookie и публичным контрактом на специально собранном примере: в нём можно запустить и слабую, и защищённую версию. Логирование в пример не вошло. Я только описал, как изменил бы его в следующей версии процесса.
Как я разложил harness на пять слоёв
OpenAI использует термин harness engineering для работы с понятным агенту репозиторием, исполняемыми ограничениями и обратной связью. Anthropic различает agent harness, который даёт модели средства действовать в среде, и evaluation harness, который запускает задания и оценивает результат. Вместе эти два описания охватывают явную задачу, инструменты и среду, повторяемые проверки, артефакты запуска и оценку результата.
Но в проверенных мной материалах нет готовой сквозной модели для AI-агента, который меняет код. Описанные там механизмы закрывают отдельные части. Они не дают готового пути от задачи в репозитории и конкретного состояния кода до результатов проверок, независимой остановки и решения о слиянии. Поэтому мне было нечего просто установить и настроить под этот эпизод.
Я взял повторяющиеся механизмы, добавил проблемы, которые увидел на проекте, и собрал практическую модель из пяти слоёв. Для каждого слоя я показываю действие агента, оставшийся артефакт и ошибку, которая должна стать видимой. Я не называю эту модель стандартом индустрии или готовым фреймворком. Это моя первая публичная версия, которую можно применить к другому репозиторию, раскритиковать и доработать.
В этой статье я называю engineering harness весь путь работы агента: где он читает задачу, какие команды запускает, как сохраняет результаты и кто может остановить слияние. Harness исполняет уже принятые правила, но не придумывает их за меня.
Разберём специально собранное изменение AUTH-017. Исполнитель читает задачу о защищённой cookie сессии, собирает приложение и запускает две обязательные проверки. Первая смотрит на контракт запроса. Вторая поднимает приложение и читает сырой заголовок Set-Cookie. Затем режим gate сам повторяет проверки и решает, разрешить слияние или остановить его. Текстовый самоотчёт исполнителя он не читает.
Я разложил этот маршрут на пять слоёв. Английские названия ниже – части моей модели, а не термины отраслевого стандарта. EvidenceReport и IndependentControlGate – имена типов из специально собранного примера:
| Слой | Что делает агент | Что остаётся после шага | Что может пойти не так | Что это даёт |
|---|---|---|---|---|
| Legibility (понятность входа) | Находит задачу, границы и команды | task.json и карта репозитория | Не нашёл правило или не понял границу задачи | Понятно, что ожидается сделать и проверить |
| Execution (запуск) | Запускает зафиксированные команды | Команда и результат запуска | Проверка недоступна или среда отличается | Видно, что именно не запустилось |
| Constraints (ограничения) | Проверяет, что обязательные поля и типы не ослабили контракт | required и проверка допустимости null | Контракт стал слабее | Ослабление контракта видно до слияния |
| Evidence (доказательства) | Записывает Passed / Failed / NotRun и ограничения проверки | EvidenceReport | Слово проверено скрывает пропущенный тест | Видно, чего мы не проверили |
| Control (контроль допуска) | Повторно запускает проверки примера и выносит однозначный вердикт | IndependentControlGate | Самооценка расходится с результатом проверок | Слияние остановлено |
Одной сборки недостаточно
Ослабленная и защищённая версии компилируются. Ниже я для наглядности совместил два независимых плохих изменения; в прогонах каждое из них проверялось отдельно.
Я собрал три варианта: защищённый исходный и два варианта с одним заведомо плохим изменением. Все 3 скомпилировались: обычная сборка не заметила разницы.
При ослаблении PhoneNumber проверка contract.required-nullability получила Failed. При отключении HttpOnly так же сработала auth.cookie-header. В обоих случаях контрольная проверка вернула Block. После восстановления кода обе обязательные проверки получили Passed, а контрольная проверка – Allow.
Поэтому одной зелёной сборки мне недостаточно. Исполнитель приносит результаты двух конкретных проверок, рецензент запускает их ещё раз, а любой Failed останавливает слияние.
Отчёт показывает результат и то, чего мы не проверили
Схема отчёта проверки намеренно короткая: идентификатор проверки (check), статус (status), доказательство результата (evidence) и ограничение проверки (limitation).
public enum CheckStatus
{
Passed,
Failed,
NotRun
}
public sealed record CheckEvidence(
string Check,
CheckStatus Status,
string Evidence,
string? Limitation = null);
Passed означает: проверка выполнилась и подтвердила ровно то, для чего написана. Failed означает, что она нашла проблему. NotRun говорит, что проверки не было. В нашем контракте необязательная проверка с NotRun должна указать причину в limitation, а обязательная проверка должна получить Passed.
В исполняемом примере auth.browser-tls остаётся NotRun. Другая проверка, auth.cookie-header, видит атрибуты сырого заголовка Set-Cookie, но не может подтвердить хранение cookie в браузере, доставку через TLS и поведение между сайтами. Поле limitation прямо называет этот пробел.
Сам пример не пропускает неполный отчёт. Если отсутствует любая известная проверка, обязательная не получила Passed или у необязательной NotRun нет limitation, результат будет Block. Режим gate при этом запускает проверки заново.
В реальном репозитории исполнитель может изменить вместе с продуктовым кодом и саму проверку. Тогда он фактически перепишет правило, которое решает судьбу слияния. Наш пример от этого не защищает. Такую проверку нужно вынести туда, где исполнитель не сможет поменять её вместе с кодом. Для каждой задачи я заранее решаю, допустимо ли обойтись без этой защиты.
Контрольная проверка не читает самооценку исполнителя
Главная часть IndependentControlGate смотрит на обязательные проверки и их статусы:
foreach (var id in required)
{
if (groups.TryGetValue(id, out var checks) &&
checks.Length is 1 &&
checks[0].Status is not CheckStatus.Passed)
{
reasons.Add($"Required check did not pass: {id}");
}
}
return reasons.Count is 0
? new GateDecision(GateVerdict.Allow, [])
: new GateDecision(GateVerdict.Block, reasons);
Текстового вердикта исполнителя в алгоритме нет: режим gate сам создаёт отчёт IndependentControl. Полная версия IndependentControlGate до показанной проверки статусов убеждается, что каждая известная проверка присутствует ровно один раз; иначе она добавляет причину Block. Она также блокирует неизвестные проверки, отчёт с неверным значением actor (заявленной роли источника отчёта) и несовпадающий ID задачи. В листинге показана только проверка статусов уже найденных обязательных проверок.
Исполняемый пример доказывает только одно: режим gate повторяет проверки и вычисляет Allow или причины Block, не читая самоотчёт исполнителя. В реальном процессе я хочу поручить отдельный запуск этого режима агенту-рецензенту (reviewer-agent).
Но отдельной учётной записи для рецензента, изоляции его процесса и защиты настроек в примере нет. Для независимого ревью нужны отдельная учётная запись, изолированный процесс и настройки, которые исполнитель не может изменить.
Пять слоёв задают один маршрут, но глубина проверки зависит от риска задачи. Если превращать в постоянное правило каждое замечание, harness быстро разрастётся и сам станет источником шума.
Как замечание ревью попадает в следующий запуск
Одно замечание ещё не повод переписывать инструкции проекта. Причиной мог быть редкий контекст, неудачная формулировка задачи или ошибка рецензента. Поэтому жду повторения: если одна и та же ошибка встретилась в 3 разных задачах, добавляю одно правило или одну проверку, которую агент сможет найти в репозитории и запустить.
Это не универсальный порог. Для дорогой ошибки ждать трёх повторов нельзя. Здесь правило нужно только для того, чтобы случайное замечание не получило постоянную прописку в проекте.
В следующей задаче я хочу видеть такую цепочку:
повторившаяся ошибка → одно правило или проверка, которую агент может найти и запустить → результат в следующем отчёте
Допустим, исполнитель несколько раз менял лишние файлы. Ещё одна страница пожеланий про аккуратность тут не поможет. Вместо неё команда сравнивает изменённые пути с разрешённым набором и пишет результат в отчёт. В следующей задаче этот результат лежит рядом с диффом, и рецензент может его проверить.
Так можно проверить, ловит ли новое правило нужную ошибку. Но похожих задач после этого изменения у меня нет. Саму команду я добавил в статью как реалистичный следующий шаг, а не как уже доказавшую себя защиту проекта.
В одном эпизоде слабое место проявилось особенно ясно. Агент должен был ограничить список изменяемых файлов, но заодно поправил ненужные соседние файлы. Получилось, что он менял правило для собственной работы и тут же его нарушал. Его самоотчёт не мог подтвердить, что защита работает.
В реальном процессе настройки этой проверки нельзя менять вместе с продуктовым кодом. Любая правка правила должна проходить отдельное ревью: рецензент проверяет, не ослабил ли его исполнитель. В нашем примере такой защиты нет. Агент не должен одновременно менять правило слияния и сам подтверждать его корректность.
Ещё одно правило: не менять сразу всё. Если одновременно переписать задачу, документ с требованиями, тест и формат отчёта, следующий запуск не покажет, что именно помогло. Поэтому на один повторившийся сбой я добавляю одну проверку или одно правило. В следующей задаче сравниваю её результат с исходной проблемой и только потом решаю, оставлять ли изменение.
Даже такой порядок не доказывает улучшение на следующих задачах. Он лишь не позволяет выдать догадку за установленную причину.
Так замечание превращается в проверку, которую можно запустить, а не в вечный пункт памятки. Но каждая новая проверка требует времени. Поэтому пять слоёв модели остаются, а глубина проверки меняется вместе с риском задачи.
Сколько harness нужно задаче
Не каждой задаче нужен одинаковый набор проверок. Я смотрю на цену ошибки и на то, насколько легко её исправить. Потом решаю, какой результат должен остановить слияние, что обязан проверить исполнитель и что отдельно повторит рецензент.
| Риск | Что обязан сделать агент | Какой контроль выбирает автор |
|---|---|---|
| Механический рефакторинг | Остаться в заданной границе, запустить форматирование и модульные тесты, приложить дифф | Обычное ревью диффа |
| Публичный контракт | Запустить проверку контракта и приложить результат проверки потребителя | Ревью совместимости блокирует несовместимое изменение |
| Аутентификация, персональные данные (PII), деньги или трудно обратимые изменения состояния | Добавить интеграционную проверку и назвать недоступные проверки | Отдельное ревью безопасности и финальное решение автора |
В первой строке меняется форма кода, а не поведение. Исполнитель запускает форматирование и модульные тесты, затем прикладывает небольшой дифф. Рецензент проверяет, не спряталось ли в "рефакторинге" изменение логики. Поднимать приложение ради переименования локального типа было бы дороже самой задачи.
Во второй строке затронут публичный контракт. Откат возможен, но несовместимость уже может задеть потребителя. Поэтому исполнитель запускает проверку контракта, а рецензент сравнивает его до и после изменения. Зелёной сборки здесь недостаточно.
Третья строка относится к нашему примеру с аутентификацией и PII. Исполнитель запускает интеграционную проверку Set-Cookie и в отчёте со статусом NotRun и полем limitation указывает то, что не удалось проверить в браузере и TLS. Я добавляю отдельное ревью безопасности и оставляю финальное решение за собой: неверная cookie или PII в логах обходятся дорого, а отменить последствия бывает невозможно. Рецензент проверяет требования безопасности, а не просто перечитывает самоотчёт.
Лишний harness тоже стоит денег. Среду нужно собирать и объяснять, проверки могут иногда падать без ошибки в продукте, отчёты добавляют шум, а весь набор инструментов приходится поддерживать вместе с кодом. Если одинаково проверять форматирование и аутентификацию, команда начнёт тратить время на пустые прогоны или игнорировать красные статусы. Поэтому мой вопрос звучит так: какая самая дешёвая проверка всё ещё соответствует риску этого изменения?
Наличие кнопки отката ещё не делает изменение обратимым. Внутреннее переименование легко отменить, пока оно не затронуло поведение. Ослабленный публичный контракт уже создаёт зависимость у потребителя. А секрет после записи в лог нельзя гарантированно вернуть назад. Поэтому глубина проверки растёт вместе с ценой ошибки.
После выбора глубины проверки нужно проверить и сам harness: он тоже может уверенно ошибаться.
Где harness врёт
Проверка не запускалась. Исполнитель пишет "проверено", рецензент видит гладкое резюме, а причина пропуска исчезает. Поэтому я закрепил форму NotRun + limitation. В нашем примере auth.browser-tls остаётся NotRun: сырой заголовок не подтверждает хранение в браузере, доставку через TLS и поведение между сайтами. Отдельное правило решает, можно ли сливать изменение с таким пробелом.
Вердикт можно понять по-разному. В одном отчёте рецензент пишет APPROVED_WITH_NOTES, в другом – Block, а в третьем описывает блокирующее нарушение только в прозе. Следующий агент не должен угадывать, блокирует ли формулировка слияние. Поэтому Passed, Failed, NotRun, Allow и Block имеют один заранее заданный смысл для всех участников.
Рецензент ошибается. В исходных материалах видно, как он исправлял собственные ложные срабатывания. Я сохраняю такие случаи: какое предположение оказалось неверным и какой факт его опроверг. Затем решаю, нужно ли менять проверку. Одна ошибка ничего не говорит о качестве всех рецензентов.
Независимость существует только на словах. Исполнитель и рецензент могут работать в одном дереве, видеть чужие незакоммиченные файлы и всё равно назвать проход независимым. Поэтому рецензент должен записывать путь, ветку, SHA коммита, состояние рабочего дерева и откуда взялись файлы с результатами. Без этих данных обещание отдельной среды ничего не доказывает. Наш пример такую изоляцию не создаёт; её нужно обеспечивать отдельно.
Агент не нашёл правило. Оно лежит в глубоком документе, команда запуска спрятана в старом комментарии, а исполнитель действует по ближайшему README. Тогда я добавляю команду или исполняемую проверку прямо в файл задачи в репозитории. В следующем запуске рецензент видит результат, а не обещание "учёл правило".
Тест проверяет не то. Рефлексия подтвердит required, но не докажет, что потребитель действительно отправляет значение. Сырой Set-Cookie покажет HttpOnly, но ничего не скажет о доставке через TLS. Поэтому для каждой проверки я прямо пишу, что она доказывает, а что нет.
Исполнитель меняет саму проверку. Тогда красный результат легко "починить", просто ослабив правило. Поэтому настройки проверки должны быть защищены от продуктовых изменений, а их правка требует отдельного ревью. Рецензент сравнивает старое и новое правило. Самооценка исполнителя вообще не входит в расчёт режима gate.
Сломалась среда. Недоступный порт, нехватка памяти, превышение времени или другая версия среды выполнения могут сделать правильную проверку красной либо вообще не дать ей запуститься. Исполнитель записывает NotRun и limitation, рецензент отделяет сбой среды от дефекта продукта. А я решаю: повторить запуск в подходящей среде, разрешить пропуск для дешёвого изменения или оставить слияние закрытым.
Тест устарел. Он может стабильно краснеть на правильном изменении и зеленеть на неправильном. Исполнитель показывает конкретное расхождение, рецензент выясняет, какое правило тест на самом деле проверяет, а я решаю, исправить тест или пока запретить слияние. С отсутствующим документом поступаю так же: агент не получает права придумать правило вместо него. Если без документа нельзя проверить задачу, работа останавливается до моего решения.
Все эти меры нужны в полноценном процессе. Но исходные материалы не показывают, что их внедрили в проекте и проверили одним общим запуском. Поэтому я не выдаю их за готовый результат. Иначе раздел о честных доказательствах сам закончится красивым самоотчётом – было бы даже немного неловко.
Что изменилось в моей работе
В исходных материалах рецензент остановил изменение с нарушениями публичного контракта и безопасности. В первых 5 задачах этого агентского процесса он ещё дважды остановил изменения за пределами разрешённых файлов. Но 5 задач слишком мало, к тому же они разные. По ним нельзя судить о скорости, числе дефектов или превосходстве рецензента.
Я не измерял время, которое тратил человек, объём переделок и поведение системы после изменений. У меня нет и похожего набора задач для сравнения. Поэтому я не знаю, ускорился ли весь процесс и сколько дефектов удалось избежать. Знаю только то, что видно в материалах: конкретные нарушения не прошли слияние, а я иначе проектирую следующий запуск.
Раньше после сильного самоотчёта я часто становился вторым исполнителем: дочитывал дифф, дописывал код и вручную закрывал хвосты агента. Следующий запуск я проектирую иначе: заранее определяю обязательные проверки, причины остановки и правила для NotRun. Цель – не подменять вручную и исполнителя, и рецензента, а заранее дать им проверяемые обязанности.
У этой схемы есть жёсткая граница: harness выполняет уже принятое правило и показывает результат проверки. Но он не решит за меня, допустимо ли менять публичный контракт или выпускать код с непроверенным риском. Это моя работа. Зелёная проверка отвечает только на тот вопрос, который я в неё вложил.
Поэтому эта статья для меня не только разбор одного проекта. Я выношу в публичное обсуждение практическую модель harness для AI-агентов, которые меняют код. Она собрана из известных механизмов, но связывает их в один проверяемый путь от задачи до решения о слиянии. Я не считаю эту версию окончательной: модель нужно пробовать на других репозиториях, критиковать и менять там, где она не выдерживает реальную работу.
После каждого уверенного "готово" я теперь спрашиваю не о количестве запущенных инструментов. Вопрос другой: чему я сейчас верю только со слов исполнителя? Именно это место нужно подтвердить независимой проверкой до решения о слиянии.
Что почитать
Эти материалы не подтверждают результат моего проекта. Я использовал их, чтобы проверить границы термина harness, понять общие механизмы разных реализаций, сверить требования к задаче и среде, а также не перепутать зелёный тест с надёжностью на проде. Ни один источник не решает за меня, достаточно ли выбранной проверки.
- Harness engineering: leveraging Codex in an agent-first world показывает, как понятный агенту репозиторий, исполняемые ограничения и обратные связи становятся отдельной инженерной работой. Это как альтернативный пример подхода.
- Demystifying evals for AI agents разделяет agent harness, evaluation harness, задания, траекторию и результат. Я использовал это различие, чтобы не выдавать свою модель и свои имена классов за общий протокол индустрии.
- SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering показывает, что агент зависит от доступных команд и обратной связи среды, но не доказывает, что новый инструмент улучшит любой процесс на проде. Для меня здесь важнее другой вопрос: может ли среда исполнить то, чего я требую от агента?
- Introducing SWE-bench Verified разбирает случаи, когда задача, среда и тесты раз за разом измеряют не то, что нужно. Результат бенчмарка не равен надёжности на проде. Для себя я беру отсюда простое правило: не доверять зелёному тесту, пока не ясно, что именно он проверяет.
- Effective harnesses for long-running agents описывает отчёты о прогрессе, исполняемую среду и проверку состояния между запусками. Материал не задаёт единственно правильную архитектуру, но помогает понять, сможет ли следующий исполнитель продолжить работу без моего устного контекста.
- Quantifying infrastructure noise in agentic coding evals показывает, как CPU, RAM и ограничение времени влияют на результат запуска. Для нашего
NotRunотсюда важен простой вывод: нужно отличать дефект продукта от сбоя среды и прямо писать об этом в отчёте.