Перейти к тексту

Первый harness ​

В этой главе начнём с программы, которую можно прочитать целиком: examples/harness-labs/01-manual-runner/runner.py. Она принимает задачу, проверяет наличие нужных файлов, копирует подготовленный результат и запускает тесты. Код можно написать вручную, сверяясь с этим образцом.

Ответ модели здесь заменён fixture, папкой с заранее подготовленными файлами. На этом этапе мы проверяем работу runner: порядок действий, обработку ошибок и передачу результата. Генерацию кода моделью добавим как отдельный backend, когда будем готовы проверять и её.

Прочитайте основной цикл ​

В main после разбора аргументов выполняются три шага:

python
require_context(workspace, task)
apply_scripted_fixture(workspace, task_path, task)

if run_checks(workspace, task):
    print("done: all checks passed")
    return 0

require_context ищет спецификацию и остальные файлы из задачи. apply_scripted_fixture копирует файлы в рабочую папку, включая замену существующих. run_checks выполняет команды с cwd=workspace и проверяет их коды завершения.

Здесь уже есть решения, которые стоит заметить. Пути fixture отсчитываются от файла задачи. Команды получают аргументы списком и не передаются через shell. После падения одной проверки runner всё равно запускает остальные. Если список checks пуст, функция вернёт успех - это пробел, к которому вернёмся в проверке harness.

Напишите v01 и сравните с образцом ​

Для собственной версии понадобятся argparse, json, pathlib, shutil и subprocess из стандартной библиотеки. Сначала реализуйте чтение --task и --workspace. Затем проверку наличия контекста, копирование fixture и запуск команд. Не добавляйте автоматические повторы, пока не умеете отличать успешную проверку от неуспешной.

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

В текущем v01 результат представлен так:

Код завершенияСообщениеЧто это означает в реализации
0doneВсе перечисленные команды завершились с кодом 0.
1failedХотя бы одна команда завершилась с ненулевым кодом.
2blockedИсключение, включая отсутствие контекста или fixture.

Последняя строка слишком широкая: ошибка самого runner тоже попадёт в blocked. При развитии harness такие сбои нужно различать. Также done пока не означает, что все требования спецификации проверены или ревью закончено.

Соберите v02 через v01 ​

Полный порядок команд приведён в практике. Для первого шага нужна чистая рабочая папка со спецификациями harness:

bash
LAB_ROOT=$(mktemp -d /tmp/handbook-labs.XXXXXX)
cp -a examples/harness-labs/harness-seed "$LAB_ROOT/harness-v02"

python3 examples/harness-labs/01-manual-runner/runner.py \
  --task examples/harness-labs/tasks/02-build-observable-runner.json \
  --workspace "$LAB_ROOT/harness-v02"

Запускайте из корня репозитория курса. Если используете собственный v01, подставьте путь к нему вместо пути к образцу.

В рабочей папке появится runner.py версии v02. Задача проверяет его компиляцию через py_compile. Она ещё не проверяет ведение журнала: JSONL появится, когда вы запустите полученный v02 на следующей задаче. Сам v01 выводит сообщения только в терминал.

Что добавляют следующие версии ​

ВерсияИзменениеКак увидеть его в работе
v01Чтение задачи, применение fixture, запуск проверок.Сообщения в терминале и код завершения.
v02JSONL-журнал событий и вывод проверок в нём.Выполнить первую задачу ALGI и открыть runs/ рядом с runner.
v03Проверка путей записи и команд по policy.Передать запрещённую команду и увидеть отказ в журнале.

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

Где подключать настоящий backend ​

При замене fixture на агентский CLI сохраните остальную последовательность. Адаптер должен запустить инструмент в нужной рабочей папке, передать задачу и контекст, дождаться завершения, сохранить доступные результаты. После него runner запускает свои проверки над полученными файлами.

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

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

Хендбук по Agentic Engineering