Если разобрать любого современного coding agent — Claude Code, Cursor, Codex, OpenHands — то снаружи окажется модель, а изнутри что-то гораздо более интересное: программный слой, который собирает вокруг модели инструменты, sandbox, память, ограничения прав, циклы исполнения и обратную связь. Этот слой называют harness. И есть тезис, который я хочу здесь развернуть: в работающем агенте бутылочное горлышко сегодня — не reasoning ability модели, а то, как устроен этот слой.
Это разбор, собранный из трёх источников. Первый — survey “Code as Agent Harness” (UIUC, Meta, Stanford, май 2026, arxiv:2605.18747) — академическая рамка. Второй — практическая статья Мартина Фаулера и Биргитты Бёккелер “Harness Engineering for Coding Agent Users” (апрель 2026, ThoughtWorks) — то же самое, но из позиции инженера, который агентом пользуется каждый день. Третий — пост Райана Лопополо из OpenAI (февраль 2026) про то, как команда из семи инженеров за пять месяцев сделала миллион строк кода на Codex с правилом “ноль строк, написанных руками”. Я постараюсь дать центральную идею просто, без свалки имён систем, и показать, что из этого можно взять в работу.
Agent = Model + Harness
Модель сама по себе — stateless: получила prompt, выдала токены, забыла. Чтобы из такой модели получился агент, кто-то должен:
- держать состояние между шагами (что мы уже сделали, что осталось);
- давать модели инструменты — файлы, shell, тесты, базу данных;
- ограничивать, что она может, а что нет;
- проверять результаты её действий и возвращать обратную связь;
- решать, когда остановиться.
Это всё и есть harness. Уравнение получается простое:
Agent = Model + Harness.
Из него следует то, что меняет приоритеты: модель ты не переобучаешь у себя на ноутбуке, а harness — можешь. Это единственная часть системы, в которую инженер может влить инженерное время и получить отдачу. Поэтому всё, что я буду обсуждать дальше — это про harness, а не про модель.
Авторы survey разделяют всё, что есть в работающей агентной системе, на три категории:
Левая колонка — то, что у модели уже есть, и что ты не меняешь без переобучения. Правые две — то, что ты как инженер целиком контролируешь. Дальше речь только о них.
Почему именно код
Можно было бы окружить модель чем угодно — например, диалоговой средой, JSON-протоколом, описаниями на естественном языке. Survey формулирует, чем код принципиально лучше любого из этих вариантов. Код:
- Исполняем — выходы модели можно реально запустить и получить детерминированный результат: компилируется или нет, тест прошёл или упал, query вернул что-то или бросил исключение. Никакого “модель сама себя проверила”.
- Инспектируем — между шагами видны не только итоговые ответы, но и трейсы, логи, exit codes, stack traces. Можно вернуть в контекст не “что модель подумала”, а “что произошло на самом деле”.
- Stateful — программа сама несёт прогресс задачи. Файл изменён, тест добавлен, ветка создана. Между шагами не нужно пересказывать модели, что было раньше — оно уже зафиксировано в коде.
В этой триаде самое важное — исполняем. Когда контур замкнут через реальное исполнение, ошибки модели становятся видимыми и поправимыми. Когда не замкнут — модель уверенно делает неправильные вещи, потому что никто её не остановил.
Три петли: где живёт harness engineering
Самая полезная диагностическая рамка для работы с агентом — модель трёх вложенных петель, которую предлагают Фаулер и Бёккелер. Когда сессия идёт не туда, первый вопрос: в какой петле проблема?
Inner loop — то, что делает сам агент за миллисекунды и секунды: посмотреть файл, запустить тест, изменить строчку, посмотреть ошибку, исправить. Десятки и сотни таких циклов в одной сессии. Инженер сюда напрямую не лезет — он только наблюдает.
Middle loop — человек внутри сессии: читает дифф, говорит “не то, давай по-другому”, откатывает плохой коммит, утверждает рискованную команду. Здесь решаются конкретные проблемы конкретной задачи.
Outer loop — то, что инженер делает между сессиями: дописывает AGENTS.md, заводит новый pre-commit hook, добавляет custom-инструмент, сужает доступные команды. Здесь решаются повторяющиеся проблемы.
Диагностический принцип: если ты второй раз правишь одно и то же руками в middle loop — это сигнал, что нужно подняться в outer loop. Каждое повторяющееся “не делай так” должно становиться правилом в AGENTS.md, hook’ом, ограничением tool’а, отдельным skill’ом — чтобы следующая сессия уже не имела возможности туда влезть. Это и есть harness engineering как практика.
Поверхности harness: что именно ты настраиваешь
Я долго искал, есть ли где-то конкретный список того, что в harness вообще можно крутить. У Фаулера он есть, и он стабилизировался вокруг примерно десяти “поверхностей”. Перечислю их с примерами, понятными бэкендеру.
| Поверхность | Что делает | Пример для Java/бэкенда |
|---|---|---|
AGENTS.md | Файл с правилами, который агент читает каждую сессию | Какая логирующая библиотека, как обрабатывать ошибки, где границы модулей, список flaky-тестов, которые можно игнорировать |
| Инструменты | Кастомные команды, которые агент вызывает по имени | Обёртка над Maven с нужными флагами, инспектор схемы БД, OpenRewrite recipe |
| MCP-сервера | Стандартный протокол для подключения внешних систем | Git, Jira, тригеры CI/CD |
| Skills | Пакеты сценариев, которые подгружаются по запросу | ”Как добавить новый REST-эндпоинт”, “Как прогнать интеграционный тест локально” |
| Sub-agents | Подагенты с изолированным контекстом | Отдельный агент для исследования незнакомого модуля |
| Hooks | Автоматизация в точках жизненного цикла | Pre-commit: проверка типов; post-write: линтер; pre-destructive: подтверждение |
| Политика доступа | Что агент может сделать без подтверждения, а что нет | Никаких прямых push в main, максимум 10 изменённых файлов до review |
| Память | Накопление контекста между сессиями | Архитектурные решения, конвенции команды, известные краевые случаи |
| Стратегия сжатия | Что делать, когда контекстное окно переполняется | Прогрессивная суммаризация прежних шагов |
| Изоляция | Файловые и процессные границы | Git worktree для параллельной работы, Docker-sandbox для рискованных команд |
Когда говорят “сделай harness engineering” — это значит сесть и продумать, что именно ты пишешь в каждую из этих поверхностей.
Самая дешёвая и при этом самая отдачливая — первая. AGENTS.md в корне репозитория, в котором написано “в этом проекте используется SLF4J, не Lombok, ошибки оборачиваем в ServiceException, главный flaky-тест — вот этот” — это уже даёт несоизмеримо больше, чем любая попытка добиться того же через prompt engineering.
Guides и sensors: feedforward и feedback
Чтобы понимать, как эти поверхности влияют на агента, Бёккелер предлагает разделить всё на две категории по моменту срабатывания.
Guides (feedforward) — то, что направляет агента до того, как он что-то сделает. AGENTS.md — это guide. Skill “как создать новый сервис” — это guide. OpenRewrite-рецепт, который агент запускает вместо ручного рефакторинга — тоже guide. Все они работают на упреждение: предотвращают плохие действия, не давая им произойти.
Sensors (feedback) — то, что срабатывает после действия и сигналит “что-то не так”. Тест, который упал. Линтер, который запретил коммит. Type checker. ArchUnit-правило, которое поймало нарушение архитектуры. Все они работают на исправление: ошибка уже случилась, но цикл замыкается обратно, агент видит сигнал и исправляется.
Ортогональное разделение — по природе исполнения:
- Computational — детерминированные, дешёвые, выполняются на CPU: тесты, линтеры, типы, ArchUnit. Их можно запускать на каждое изменение.
- Inferential — семантические, выполняются LLM: AI-ревью кода, “LLM as judge”. Медленнее, недетерминированы, дороже. Их применяют дозированно — например, после интеграции.
Хороший harness строится по принципу “максимум computational sensors на горячем пути, inferential — для редких дорогих проверок”. Если ты можешь поймать архитектурный дрейф ArchUnit-правилом — лучше так, чем гонять на каждом коммите LLM-ревьюера.
Фаулер выделяет для типичного Java-монолита три категории сенсорных систем, которые стоит выстраивать как “harness категории”:
- Maintainability — цикломатическая сложность, дубликаты, покрытие, дрейф архитектуры. В основном computational.
- Architecture fitness — ArchUnit, контракты на latency, конвенции observability. Skills + структурные тесты.
- Behaviour — функциональные спецификации, AI-сгенерированные тесты, опционально mutation testing. Это самая нерешённая категория сегодня.
Plan-Execute-Verify и три уровня прав
Внутри inner loop есть один паттерн, который повторяется во всех системах, упомянутых в survey, от SWE-agent до Codex CLI. Он называется Plan-Execute-Verify (PEV) и состоит из четырёх шагов:
- Plan as contract. Прежде чем тронуть код, агент формулирует план: какие файлы в зоне работ, какие инварианты должны сохраниться, что считается “готово”, какие команды для проверки. План — это не просто список шагов, это контракт на следующий переход состояния.
- Execute в sandbox. Действие выполняется в изолированной среде — worktree, контейнер, ограниченный shell. Не в проде, не на главной ветке.
- Verify детерминированными сенсорами. Запускаются тесты, линтер, компилятор, статический анализ. Результат — объективный сигнал, а не оценка модели.
- Revise / rollback / escalate. Если verify упал — обратно в план с новой информацией. После N неудачных циклов — эскалация человеку.
Ключевая вещь, на которую стоит обратить внимание: остановка решается верификацией, а не уверенностью модели. Модель может думать, что всё сделала. Тест либо проходит, либо нет. Цикл завершается только когда сенсоры зелёные, или когда количество попыток исчерпано.
Параллельно с PEV в survey формализованы три уровня прав для шага Execute. Это разделение мне кажется одним из самых полезных вещей в статье — оно прямо переводится в политику доступа.
Это полезно, когда ты настраиваешь политику доступа для своего агента. Не “что он может”, а “что он может где”. mvn test в sandbox-контейнере — read/write. mvn deploy в проде — full-access, через human gate.
Когда нужно больше одного агента
Survey посвящает целую главу multi-agent системам, и краткий ответ из неё такой: чаще всего одного агента достаточно. Многоагентность нужна, когда задача реально не помещается в одно контекстное окно или требует независимых каналов верификации.
Если уж добавлять, то канонической стала четырёхролевая топология (фреймворк AgentMesh, arxiv:2507.19902, реализует именно её):
- Planner — декомпозирует запрос, пишет план, никогда не трогает файлы.
- Coder — реализует подзадачи, работает в изолированном worktree.
- Debugger — гоняет тесты, диагностирует падения, предлагает патчи.
- Reviewer — валидирует итоговый diff против плана и инвариантов репозитория.
Планировщик и ревьюер не пишут код — их можно запускать на более дешёвых моделях. Это экономит ~30% токенов и не теряет качество.
Главная сложность multi-agent — общее состояние. Когда несколько агентов работают над одним репозиторием, они должны как-то синхронизироваться. Survey показывает, что большинство современных multi-agent систем держат это состояние в обычных файлах (по сути неявно), и именно отсюда берётся хрупкость — никто не видит, что у другого агента в голове. Несколько практичных схем:
- Shared blackboard — общий файл вроде
qa-status.mdилиscout-finds.md, куда все агенты пишут и из которого читают. - Параллельные ветки + merge — каждый агент в своём git-worktree, оркестратор сливает.
- Иерархическая память — у каждого подагента своя рабочая память, родитель держит сводку.
Если кратко: пока возможно — оставайся на одном агенте; добавляй роли только когда видишь, что у тебя действительно две независимые работы (например, “пишу код” и “проверяю безопасность”), а не просто “хочется красиво”.
AHE: harness, который улучшает сам себя
Логичное продолжение всей этой истории — а что, если outer loop (изменение поверхностей harness между сессиями) тоже автоматизировать? Этим занимается Agentic Harness Engineering (arxiv:2604.25850, апрель 2026).
Идея простая: запускаем агента на бенчмарке, записываем телеметрию (что вызывал, что упало, где зациклился), отдаём её другому агенту — Evolution Agent — который предлагает изменения в harness: переписать описание инструмента, добавить hook, изменить правило в AGENTS.md. Каждое изменение — это гипотеза с предсказанием: “если я добавлю эту проверку, pass@1 на следующем прогоне вырастет”. Проверяем на следующей итерации; если не сработало — откатываем.
Результат на Terminal-Bench 2: 10 итераций AHE подняли pass@1 с 69.7% до 77.0%, обогнав сделанный руками harness Codex CLI (71.9%). Самое важное в ablations — откуда пришёл прирост:
- инструменты — да;
- middleware — да;
- долгосрочная память — да;
- системный промпт — практически не дал ничего.
И это, пожалуй, главный практический вывод во всей этой области: инвестировать стоит в архитектуру инструментов и памяти, а не в очередную итерацию prompt engineering. То, что зафиксировано в коде и файлах конфигурации, переносится между моделями. То, что зафиксировано в красивом промпте — нет.
Кейс OpenAI: миллион строк, ноль рук
Лучшая иллюстрация того, во что harness engineering превращается на проде — внутренний эксперимент OpenAI, описанный Райаном Лопополо в феврале 2026. Команда из трёх (сейчас семи) инженеров за пять месяцев собрала и запустила в эксплуатацию продукт с правилом: ни одной строки, написанной руками человека. Весь код — приложение, тесты, CI, документация, observability, внутренние утилиты — пишет Codex. Результат: около миллиона строк кода, 1500 PR за период, 3.5 PR на инженера в день (и пропускная способность растёт по мере роста команды, а не падает). По их оценке — примерно в десять раз быстрее, чем если бы код писали руками.
Что из их опыта стоит унести.
Бутылочное горлышко — не модель, а среда. Прогресс в начале был медленнее ожидаемого не потому, что Codex слаб, а потому что среда была недоопределена. Когда что-то не получалось, фикс почти никогда не был “попробуй ещё раз”. Вопрос всегда был один: какая способность отсутствует, и как сделать её одновременно явной и принудительной для агента? Это в точности тот самый переход в outer loop, про который пишут Фаулер и Бёккелер.
AGENTS.md — это оглавление, а не энциклопедия. Они начали с “одного большого AGENTS.md”. Получили четыре проблемы: контекст — дефицитный ресурс, и громадный файл вытесняет код и задачу; когда “важно всё” — не важно ничего; монолитный мануал быстро превращается в кладбище устаревших правил; единый блоб невозможно проверить механически. Решение: короткий AGENTS.md (~100 строк) как карта со ссылками, а реальное знание живёт в структурированном docs/ — design docs с verification status, architecture map, quality grades по доменам. Активные планы, завершённые планы и known tech debt версионируются в репо рядом с кодом. Это они называют progressive disclosure: агент стартует с маленькой стабильной точки входа и узнаёт, куда смотреть дальше, по мере необходимости.
Если агент не видит этого в репо — этого не существует. Это переформулировка, которую стоит держать в голове. Знание в Google Docs, в Slack-треде, в голове у тимлида — для агента не существует. Поэтому всё, что важно — должно стекать в repo: code, markdown, schemas, executable plans. Тот архитектурный консенсус, который договорили в Slack? Если он не задокументирован в репо — он непрозрачен агенту так же, как непрозрачен новому человеку, который придёт через три месяца. Это меняет приоритет: документация перестаёт быть “хорошим тоном” и становится частью runtime.
Boundaries, не implementations. Они формулируют это так: enforcing invariants, not micromanaging implementations. Например, требуют от Codex парсить данные на границе — но не указывают, какой именно библиотекой (модель сама любит Zod, но это не зафиксировано). Архитектура жёсткая: каждый бизнес-домен разделён на фиксированные слои с проверяемым направлением зависимостей (Types → Config → Repo → Service → Runtime → UI), cross-cutting concerns заходят через единый интерфейс Providers. Это уровень дисциплины, который обычно откладывают до сотен инженеров. С агентами это — стартовое требование: именно ограничения дают скорость без деградации.
Custom-линтеры пишут remediation прямо в error message. Тонкий, но мощный приём. Они enforce’ят правила (structured logging, naming conventions, file size limits) кастомными линтерами — и поскольку линтеры свои, текст ошибки они пишут так, чтобы он сразу инструктировал агента, как чинить. Ошибка превращается в feedforward-guide: агент видит сигнал и сразу видит, что делать. Это объединяет sensor и guide в одной точке.
Doc-gardening agent. Раньше команда тратила пятницу (20% недели) на чистку “AI slop” — паттернов, которые Codex реплицировал из уже существующего, в том числе неудачного кода. Это не масштабировалось. Решение: “golden principles” зафиксированы в репо, и фоновые Codex-задачи на регулярной основе сканируют отклонения, обновляют quality grades и открывают точечные refactoring-PR. Большинство таких PR ревьюится за минуту и автомержится. Они сравнивают это с garbage collection: технический долг — кредит под высокий процент, дешевле гасить по чуть-чуть каждый день, чем в больших спазмах.
Делаем UI и observability читаемыми для агента. Когда пропускная способность по коду выросла, бутылочным горлышком стала способность человека делать QA. Их решение — сделать UI и метрики напрямую читаемыми Codex: приложение бутится per git worktree (агент гоняет свою изолированную копию), Chrome DevTools Protocol подключён к agent runtime, навыки для работы с DOM-снимками и скриншотами. Observability stack тоже ephemeral per worktree, Codex запрашивает логи через LogQL и метрики через PromQL. После этого запрос вида “startup сервиса должен завершаться за 800мс” или “ни один span в четырёх критических пользовательских флоу не превышает 2 секунды” становится исполнимым для агента. Один прогон Codex регулярно работает над задачей по шесть часов (часто пока люди спят).
Конечно, как они сами оговаривают, это всё держится на конкретной структуре их репо и инвестициях в инструменты, и не стоит ждать, что это “просто перенесётся” на любой проект. Но направление прозрачное: чем больше системы агент может напрямую прочитать, проверить и изменить — тем выше leverage.
Что стоит сделать прямо сейчас
Я попробую собрать самые конкретные вещи из survey и Фаулера — по приоритету “ROI/усилие”.
В первую очередь (outer loop, час работы)
- Завести
AGENTS.mdв корне репозитория — как оглавление, а не как энциклопедию. Сто строк со ссылками наdocs/важнее, чем тысяча строк правил в одном файле. Внутри — библиотеки, конвенции ошибок, границы модулей, список flaky-тестов, запрещённые паттерны. Самая дешёвая поверхность harness с самым большим эффектом. - Добавить pre-commit hook с ArchUnit-правилами (если Java) или эквивалентом — структурные тесты ловят дрейф детерминированно, на каждом изменении, бесплатно. Текст ошибки писать как инструкцию агенту: “запрещено X, используй Y вот так” — превращает sensor в guide.
- Сузить tool-список агента: дать whitelisted Maven/CLI-команды вместо свободного shell. Слишком много инструментов — модель выбирает не те; слишком мало — она беспомощна.
- Прописать политику: максимум N изменённых файлов до review, никаких прямых push в
main, destructive-команды только через подтверждение.
В каждой сессии (middle loop, постоянно)
- Сначала план, потом код. Заставить агента написать план до любых изменений. Прочитать план. Одобрить. И только потом execute.
- Хранить состояние задачи в файлах:
active-tasks.mdс разделамиDO_NOW / BLOCKED / DONE, отдельные файлы под результаты подагентов. Это переживает компакцию контекста и даёт crash recovery. - Прогонять верификацию после каждой записи агента. Тесты и линтер — не post-session батч, а немедленный сигнал.
В архитектуре (long-term)
- Использовать трёхролевую топологию для нетривиальных задач: Planner (не пишет файлы) → Coder (изолированный worktree) → Reviewer (валидирует diff). Planner и Reviewer — на дешёвых моделях.
- Считать обратную связь от исполнения единственным источником истины: компилятор, тесты, статический анализ — надёжнее, чем самооценка модели.
- Завести фоновую задачу типа “doc-gardening”: регулярно прогонять агента по репо в поисках устаревших правил, дублирующихся утилит, отклонений от “golden principles”. OpenAI заменили этим 20% времени команды на ручную чистку.
- Не вкладываться в “хитрые промпты”. Вкладываться в инструменты и архитектуру памяти. AHE-эксперименты и опыт OpenAI эмпирически показали, что выигрыш приходит оттуда.
Если хочется одной фразы, которую стоит унести: agent = model + harness, и инженер контролирует только правую часть. Когда сессия идёт криво — это сигнал не “модель тупит”, а “поверхность harness недонастроена”. Если ты ловишь себя на повторении одной и той же правки в middle loop — это значит, что время подняться в outer loop и зафиксировать правило в AGENTS.md, hook’е или конфигурации инструмента, чтобы оно работало автоматически. В этом и состоит harness engineering как практика — не одноразовая настройка, а постепенное наращивание окружения, в котором coding agent действительно становится надёжным.
Survey “Code as Agent Harness” даёт этому академическую рамку, статья Фаулера и Бёккелер — рабочий язык, AHE — экспериментальное подтверждение, опыт OpenAI — что это работает в проде на масштабе миллиона строк. Дальше — практика.