Главная / Статьи

Пять уровней агента-харнеса с примерами кода, который не будет работать без них.

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

Издатель: REACTAPP.TOP14 мин чтения3301 слов

Источники: `The Complete Guide to Agent Harnesses (With Code)`

Представьте команду, которая добавляет одну строку в конфигурацию агента: никакие изменения не вносятся без подтверждения человека. Формулировка ясна, строка расположена близко к верху, и агент загружает её в начале каждой сессии.

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

Само указание было в порядке. Оно просто оказалось не в том месте.

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

Разница между объявленным правилом и реализуемым правилом — это сама инструментальная среда. Она охватывает пять областей:

  1. Действия, которые может выполнить агент
  2. Места, до которых он может добраться
  3. Состояние, сохраняющееся после перезагрузки
  4. Человек, проверяющий его результат
  5. Стоимость того, что ему отображается

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

Что на самом деле даёт вам такой инструмент

Начнём с чего-то настолько очевидного, что его трудно даже сформулировать. Основной цикл кодингового агента состоит из шести строк:

while agent.turns < max_turns:
    call = agent.next_call(observations)
    if call is None:
        break
    result = execute(call)
    observations.append(result)

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

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

По стоимости преимущество у harness-решений. Предварительная публикация за июнь 2026 года сравнила три типа harness-решений (Goose, OpenCode, OpenHands-SDK) на двух моделях с использованием стратифицированного набора из 50 задач Terminal-Bench Pro — более сложной версии набора инструментов Terminal-Bench. Количество токенов, потребляемых на решение одной задачи, различалось между harness-решениями в среднем в 40 раз. Переход на более совершенную модель изменял это значение всего на 1,0–1,3 раза.

По функциональным возможностям преимущество не на их стороне. Уровень успешного выполнения задач различался между harness-решениями на 0–8 пунктов, и с учетом доверительных интервалов значения почти всех случаев, кроме тех с наибольшей разницей, находились в пределах нуля. Сами авторы называют результаты точности описательными при n=50.

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

Перенос правил в код

Формулировка правила в промпте системы равносильна ставке на то, что модель вспомнит его, интерпретирует правильно и будет ему следовать даже при наличии множества контекстных данных. Обычно модель побеждает. Именно этот уровень успеха делает ситуацию рискованной, поскольку люди теряют желание проверять её действия.

Правила на уровне кода срабатывают при каждом вызове, даже если модель уже забыла о них.

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

Слой 1: граница выполнения

Что разрешено агенту делать?

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

SYSTEM_PROMPT = "IMPORTANT: never delete a file without asking the user first."

def execute(call):
    if call.name == "delete_file":
        FILES.pop(call.args["path"], None)
        return f"deleted {call.args['path']}"

При запуске агент удаляет cache.tmp — бесполезный мусор, а затем удаляет client-deliverable.md, что соответствует его названию. Вопросов не задавалось, хотя в инструкции с самого начала было указано это правило.

Функция, размещённая перед инструментом, решает эту проблему:

def boundary(rules):
    def wrap(execute):
        def guarded(call):
            for name, deny_if, reason in rules:
                if deny_if(call):
                    return f"DENIED by {name}: {reason}"
            return execute(call)
        return guarded
    return wrap

NEEDS_APPROVAL = [(
    "delete-needs-approval",
    lambda c: c.name == "delete_file" and not c.args.get("approved_by_human"),
    "deletion requires an explicit human approval flag on the call",
)]

При использовании того же агента, скрипта и запроса ничего не удаляется.

Уровень 2: песочница

До чего может добраться агент?

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

Распространённой альтернативой является список запрещённых путей:

DENY = ["secrets/"]

def execute(call):
    path = call.args["path"]
    if any(path.startswith(d) for d in DENY):   # checks the spelling
        return "DENIED by deny-list"
    real = os.path.normpath(path)               # the ../ collapses HERE, after the check
    return DISK.get(real, "not found")

Запустите его, и агент попытается дважды:

read('secrets/api_key')          -> DENIED by deny-list
read('work/../secrets/api_key')  -> sk-live-DO-NOT-LEAK

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

Для устранения проблемы сначала необходимо нормализовать путь, а затем сравнить его:

ALLOW_ROOTS = ["work"]

def resolve(path):
    real = os.path.normpath(path)               # resolve FIRST
    if not any(real == r or real.startswith(r + os.sep) for r in ALLOW_ROOTS):
        return None
    return real

Оба варианта написания теперь сводятся к secrets/api_key, который находится не под разрешённым корневым уровнем, поэтому оба варианта отклоняются. Здесь важно разрешать, а не запрещать, поскольку список запретов блокирует только те варианты написания, которые представил его создатель.

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

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

Слой 3: сохранение в памяти

Что остаётся после сброса контекста?

Одна привычка приносит больше пользы, чем большинство других: прерывание разговора без долгих раздумий. Содержимое диалога накапливается, качество снижается, и агент начинает противоречить сам себе. Многие продолжают вести такие сессии. Лучше начинать заново раньше, если важная информация сохраняется в месте, которое не будет стерто при перезапуске — будь то зафиксированный код, конфигурация, сама сборка или краткие заметки.

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

Одна функция обработки задач покрывает все три варианта:

CONVERSATION, DISK, HARNESS_CONFIG = [], {}, {}

def remember(kind, key, value):
    """'chat' dies with the session, 'disk' survives it,
    'config' shapes every session that follows."""
    {"chat": lambda: CONVERSATION.append(value),
     "disk": lambda: DISK.__setitem__(key, value),
     "config": lambda: HARNESS_CONFIG.__setitem__(key, value)}[kind]()

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

Уровень 4: циклы проверки

Кто проверяет результаты, и что мешает проверяющему просто всё исправить?

Цикл проверки означает повторный анализ работы агента, выполняемый тем, кому разрешено фиксировать проблемы, но не устранять их.

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

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

Второй приём — пробный запуск. Для любых необратимых действий инструмент принимает флаг, по умолчанию описывает, что он собирался сделать, и действует только тогда, когда кто-то намеренно отключает этот флаг. Безвредное поведение является стандартным, поэтому оплошность ничего не стоит. В демонстрации инструмент исправления сообщает, что собирается переписать файл total.py, ничего не записывает, и ошибка «off-by-one» остаётся в конце.

Один execute включает в себя обе правила:

def execute(call):
    if call.name == "review":
        snapshot = dict(CODE)               # a copy, so the reviewer cannot write
        src = snapshot[call.args["path"]]
        return f"VERDICT: {'off-by-one' if '+ 1' in src else 'looks good'}"
    if call.name == "apply_fix":
        if call.args.get("dry_run", True):  # on by default, turned off on purpose
            return f"DRY RUN: would rewrite {call.args['path']}, nothing written"
        CODE[call.args["path"]] = call.args["new"]
        return f"wrote {call.args['path']}"

Рецензент замечает ошибку типа «один слишком много» и не имеет возможности её исправить.

Слой 5: конвейеры контекста

Что попадает в модель и за что взимается плата?

Конвейер контекста определяет, как информация попадает в окно модели и сколько из неё принимается.

Именно здесь происходит оплата. За каждый токен, который попадает в модель, взимается плата в момент его ввода и снова при каждом последующем вводе, поэтому именно здесь возникает сорокакратная разница в стоимости, а не на Слое 1.

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

Решение заключается в делегировании задач и их обработке путем фильтрации. Подагент работает в отдельном окне, просматривает десятки тысяч токенов и передает обратно один-два тысячи. Основной поток пропускает необработанные результаты поиска и получает только ответ.

В коде всё сводится к тому, какой счётчик увеличивается:

def subagent_search(query):
    """Its own window. The main thread never pays for this reading."""
    global subagent_tokens
    subagent_tokens += sum(len(v.split()) for v in CORPUS.values())
    return next(f"{n}: {b.split('ANSWER:')[1].strip()}"
                for n, b in CORPUS.items() if "ANSWER:" in b)
def execute(call):
    global main_tokens
    distilled = subagent_search(call.args["q"])
    main_tokens += len(distilled.split())    # the only line that bills you
    return distilled

Демонстрация преднамеренно масштабирована до небольших размеров и проводится честно. Вопрос и ответ одинаковы в обоих случаях, однако основной поток обрабатывает 9 602 токена в одном случае и 2 токена в другом.

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

Чего пока никто не может сказать

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

  1. Ничто здесь не прошло рецензирование коллегами. По состоянию на август 2026 года каждое исследование, посвящённое этому устройству, представляет собой предварительную публикацию на arXiv без рецензирования. Самый тщательно спланированный эксперимент описывается как начальная работа, находящаяся на рассмотрении на семинаре, а наиболее известная статья-основа называет себя позиционным документом без собственных экспериментов. Считайте всё это, включая данный текст, нерецензированным и повторяйте сравнение в своих задачах перед тем, как планировать бюджеты на его основе.
  • Результаты обучения редко переносятся на незнакомые задачи. В статье, опубликованной в июле 2026 года, говорится, что автоматическое развитие инструментальных средств не позволяет надежно превзойти традиционный подход к масштабированию во время тестирования и плохо обобщается, поскольку процесс поиска и окончательное тестирование основаны на одном и том же критерии оценки. В ходе оценки непрерывного обучения на Terminal-Bench 2.0 показатель GEPA достиг 70,8% для задач, на которых происходило настройка, но затем снизился до 54,5% для более широкого набора задач, что ниже исходного уровня в 56,8%. На самом деле настройка нанесла вред. Протестируйте любую инструментальную среду на задачах, не использовавшихся при ее создании, поскольку улучшения, наблюдаемые только в наборе для настройки, исчезают в реальных условиях использования.
  • Успех тонкой настройки оценивается с помощью сигнала обучения, используемого для неё самой. Например, модель Gemma 3 27B, тонко настроенная для написания клинических записей, показала результат, на 60% превышающий показатели Claude Sonnet 4, в то время как ранее отставала на 35%. В описании открыто говорится, что инструмент оценки одновременно служил и механизмом вознаграждения в процессе обучения. Это допустимый метод, но оценка формируется на основе того же самого элемента, который использовался для обучения. Когда поставщик заявляет о преимуществах тонкой настройки, спросите, каким был инструмент оценки во время обучения; если это тот же самый инструмент, то цифры указывают на то, что модель просто выучила формат оценки.
  • Ремни не могут быть без проблем перенесены с одной модели на другую. Исследование, проведенное в июне 2026 года, показало, что добавление ремня после обучения восстанавливает лишь незначительную часть того, что даёт обучение с его использованием. Модели, обученные с минимальным использованием ремня, в 75,1% случаев после изменения определений инструментов выдавали ошибку «Неверный формат инструмента», а вариант Qwen2.5-7B показал на 10,8 баллов худшие результаты по сравнению со своей базовой моделью. Ремень, подходящий для всех моделей, на самом деле подходит каждой из них несколько хуже. Выбирайте ремень до завершения обучения, и если вы не обучаете собственную модель, ожидайте, что замена ремня на середине проекта обойдётся дороже, чем кажется на первый взгляд.
  • Терминология остаётся неопределённой. В мае 2026 года Hugging Face опубликовал глоссарий, в котором разделил понятия «harness» и «scaffold», и признал, что Claude Code, Codex и другие инструменты всё равно используют термин «harness» для обозначения всей стек-архитектуры. Пять слоёв — это лишь один из способов её разделения. По более узкому определению примерно половина приведённых выше примеров относится к чему-то другому. Выберите словарь терминов, придерживайтесь его в своей команде и внимательно изучайте сравнения различных исследований. По этой причине пять слоёв лучше всего подходят в качестве чек-листа для настройки вашей собственной системы, поскольку чек-лист остаётся актуальным даже при изменении словаря терминов, тогда как таксономия может противоречить ему. Прежде чем верить в различия между результатами работы двух агентов, узнайте, какие слои были использованы каждым из них.
  • Похоже, эффекты от использования Harness уменьшаются по мере улучшения моделей. Harness-Bench показывает, что более мощные модели обеспечивают более высокие средние показатели при меньшей вариативности между различными версиями Harness. Часть используемых инструментов может компенсировать недостатки, которые постепенно исчезают. Сосредоточьте вложения в Harness на контроле затрат, что приносит постоянную пользу, а не на точности, которую может бесплатно обеспечить новая версия модели.
  • Сопутствующие скрипты

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

    git clone https://github.com/paoloap-py/agent-harness-guide
    cd agent-harness-guide
    
    python3 run_all.py        # all five layers, guard off then on, side by side
    python3 test_harness.py   # asserts every difference above, 10 checks
    

    В документе README пять уровней представлены в виде чек-листа для аудита с одним повторяющимся вопросом: обеспечивает ли код соблюдение правил или же это просто фраза в запросе? Любая галочка, которую невозможно поставить, указав путь к файлу, означает правило, на соблюдение которого можно только надеяться.

    Как это работает на практике

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

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

    Часто задаваемые вопросы

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

    Является ли харнес важнее модели? С точки зрения затрат — да, поскольку количество используемых токенов на решённую задачу может варьироваться в 40 раз. Что касается успешности решения задач — нет. Различия в проценте успешного выполнения задач с использованием разных харнесов составляют от 0 до 8 пунктов, причём бывают периоды, когда этот показатель становится отрицательным; авторы Terminal-Bench приходят к выводу, что для производительности обычно важнее выбор модели.

    Где ремни безопасности стоят дороже всего? В контексте. Каждый токен, показываемый модели на каждом шаге, оплачивается на каждом шаге. Поэтому уровень 5 является самым дорогим, и это объясняет, почему передача поиска подагенту меняет сумму расчета, не меняя при этом ответа.

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

    Создавать собственные ремни безопасности или использовать готовые? Лучше использовать готовые и оставаться с ними. Переход на другие ремни безопасности стирает накопленные знания из файлов конфигурации, и каждое правило, добавленное после реальной ошибки, приходится формировать заново.

    Связанные материалы

  • Инженерия контекста для исследовательского агента Quant: уровни доказательств и хранилища данных — Как исследовательский агент WorldQuant BRAIN разделяет правила, наблюдения и убеждения, а также распределяет контекст между журналом операций, хранилищем событий, графом и файлами.
  • Внутренние механизмы контекста и памяти Claude Code, перепроектированные в виде агента LangGraph — Как Claude Code формирует свои API-запросы, запускает подагентов и сжимает длительные сессии, а также упрощённая версия LangGraph с использованием механизмов сохранения состояния, файлов памяти и навыков.
  • Agent Harnesses: стандарт папок для навыков и ссылок — Как инструмент harness расширяет возможности агента с помощью файла HARNESS.md, каталогов навыков и ссылок, постепенного отображения информации и вложенных структур для крупных агентов.
  • Pi — минимальный инструмент для программирования агентов: расширения, навыки, сессии и механизмы безопасности — Почему небольшой инструмент для программирования в терминале с четырьмя инструментами и кратким промптом системы может превзойти агентов с большим количеством функций: установка, модели, деревья сессий, расширения, навыки и механизмы изоляции.
  • Навыки агентов — это код: определите их объём, раскройте детали, напишите скрипты и протестируйте их — Почему SKILL.md представляет собой логику производства: записывайте только процедурные знания, настраивайте момент активации, постепенно загружайте данные, переносите точную работу в скрипты, создавайте инструменты оценки и удаляйте неэффективные решения.
  • Разработка многоагентных ИИ с использованием AutoGen: концепции, компоненты и план обучения — Простое объяснение фреймворка AutoGen от Microsoft: агенты, групповые чаты, участие человека, инструменты, десятиэтапный план обучения, случаи применения и направления дальнейшего развития.
  • Почему кодинг-агенты по-прежнему сталкиваются с проблемами при работе с кодом частных компаний — Real-SWE тестирует восемь кодинг-агентов на лицензированных частных кодовых базах. Лучший агент решает 38,8 процента задач, а пять категорий неудач показывают, где агенты допускают ошибки.