Модуль 3 · Датчики и ограничения · урок 12 из 24
Петли обратной связи
Агент ошибается не реже человека, но быстрее узнаёт об ошибке — если среда ему об этом сообщает. Скорость и точность этого сигнала определяют, сколько итераций уйдёт на задачу и сколько брака доберётся до ревью. В уроке — рамка, по которой удобно раскладывать любые проверки, и практическая сборка петли, которая работает внутри хода агента, а не через полчаса в CI.
Направляющие и датчики
Самая полезная на сегодня классификация — у Биргитты Бёкелер в статье Harness engineering for coding agent users. Агент в ней — это модель плюс обвязка (harness), а внешняя обвязка, которую строит команда, преследует две цели: повысить шанс верного результата с первой попытки и поймать как можно больше проблем до того, как код увидит человек.
Инструменты делятся по двум осям.
Когда действуют. Направляющие (guides, feedforward) работают до действия: соглашения в AGENTS.md, навыки, инструкции по запуску. Датчики (sensors, feedback) — после: тесты, линтеры, ревью. Одни направляющие дают правила, которые никто не проверяет. Одни датчики заставляют агента повторять одни и те же ошибки и чинить их по факту.
Как устроены. Детерминированные (computational) — тесты, типы, линтеры, структурный анализ: быстрые, дешёвые, надёжные. Вероятностные (inferential) — ревью агентом, LLM в роли судьи: медленнее и дороже, зато понимают смысл.
| Детерминированные | Вероятностные | |
|---|---|---|
| Направляющие | шаблоны и генераторы кода, codemod-рецепты | AGENTS.md, навыки, примеры в docs/ |
| Датчики | PHPUnit, PHPStan, структурные тесты, Playwright | /review, отдельный агент-ревьюер, оценщик по чек-листу |
Примеры в таблице — наши, для PHP-проекта агентства; у Бёкелер примеры другие, но сетка та же. Полезное упражнение — разложить по этой таблице всё, что уже есть в вашем репозитории. Обычно выясняется, что левый нижний угол заполнен, а левый верхний пуст: генераторов, которые сразу делают правильный каркас компонента, почти ни у кого нет.
Петли разной скорости
Бёкелер формулирует принцип «keep quality left»: дешёвые проверки — как можно раньше, дорогие — позже, плюс непрерывные датчики вне жизненного цикла изменения. Для агентного проекта получается пять уровней:
- Внутри хода — хук после каждой правки файла: форматер, линтер изменённого файла. Секунды.
- Перед завершением хода — быстрые тесты затронутого модуля. Десятки секунд.
- Перед коммитом или пушем — pre-commit/pre-push хуки Git. В статье упоминается практика Stripe: pre-push хуки запускают релевантные линтеры по эвристике.
- В CI — полный набор: backend-тесты, сборка фронта, e2e на Playwright, затем дорогие проверки вроде мутационного тестирования.
- Непрерывно — мёртвый код, дрейф зависимостей, качество покрытия, SLO. Это тема урока об энтропии.
Каждый уровень ниже стоит дороже в двух валютах: время ожидания и контекст. Ошибка, пойманная хуком через две секунды после правки, стоит агенту одной короткой реплики. Та же ошибка из CI через двадцать минут требует перечитать лог, вспомнить задачу и иногда начать новую сессию.
Внутриходовая петля в Codex
С мая 2026 хуки в Codex вышли из беты; общее устройство — в уроке о хуках. Для петли обратной связи важны два события.
PostToolUse срабатывает после вызова инструмента. Если обработчик вернёт "decision": "block" с полем reason, Codex заменит результат инструмента этим текстом — правка уже применена, но агент сразу видит, что с ней не так. Правки файлов ловятся матчером apply_patch|Edit|Write.
Stop срабатывает, когда агент собирается закончить ход. Ответ "decision": "block" с reason не отклоняет ход, а создаёт новое продолжение с этим текстом в качестве промпта. Поле stop_hook_active во входных данных показывает, что ход уже продлевался, — по нему защищаются от бесконечного цикла.
{
"hooks": {
"PostToolUse": [
{
"matcher": "apply_patch|Edit|Write",
"hooks": [
{ "type": "command", "command": "python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/lint_changed.py\"", "timeout": 60 }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/fast_tests.py\"", "timeout": 300 }
]
}
]
}
}import json
import subprocess
import sys
event = json.load(sys.stdin)
if event.get("stop_hook_active"):
sys.exit(0) # уже продлевали ход: не зацикливаемся, остальное поймает CI
# Хук запускается в рабочей папке сессии, а она может быть вложенной
root = subprocess.run(
["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True
).stdout.strip()
changed = subprocess.run(
["git", "diff", "--name-only", "HEAD"], capture_output=True, text=True, cwd=root
).stdout.split()
if not any(path.endswith(".php") for path in changed):
sys.exit(0)
run = subprocess.run(
["vendor/bin/phpunit", "--testsuite", "fast", "--no-progress"],
capture_output=True, text=True, cwd=root,
)
if run.returncode != 0:
tail = "\n".join(run.stdout.strip().splitlines()[-40:])
print(json.dumps({
"decision": "block",
"reason": "Быстрые тесты упали. Исправьте код, не меняя тесты, и прогоните "
"vendor/bin/phpunit --testsuite fast ещё раз.\n" + tail,
}))Команды хуков выполняются в рабочей папке сессии, поэтому пути строятся от корня git, как советует документация. Хуки из проектного .codex/ работают только в доверенных проектах и только после того, как вы подтвердили их через /hooks; доверие привязано к содержимому определения. Формат полей проверяйте по документации хуков — он ещё меняется.
Две детали в скрипте важнее, чем кажется. Вывод обрезан до последних сорока строк: Джеффри Хантли в описании своего цикла называет переполнение контекста ошибками компиляции одним из типичных провалов. И в сообщении есть «не меняя тесты» — иначе самый короткий путь к зелёному прогону очевиден.
Противодавление и рулевая петля
Хантли называет механические проверки противодавлением (backpressure): тесты, типы и статический анализ должны отвергать плохой вывод так быстро, чтобы «колесо крутилось». Для языков без строгой типизации он советует обязательно подключать статический анализатор — для PHP это PHPStan или Psalm на максимальном уровне, который выдерживает проект.
Над петлями агента есть ещё одна — человеческая. Бёкелер называет её рулевой (steering loop): если проблема повторяется, команда усиливает направляющую или датчик, чтобы она стала маловероятной или невозможной. У OpenAI та же мысль звучит как вопрос, который инженер задаёт при каждом провале: какой возможности не хватает агенту и как сделать её понятной и обязательной. Цепочка повторяется: замечание в ревью → правило в AGENTS.md → проверка в коде, когда текста оказалось мало.
Вероятностные датчики
Детерминированные проверки ловят нарушения формы, но не смысла. Для смысла нужен второй агент. Anthropic в исследовании длинных задач отделяет генератор от оценщика: агент склонен хвалить собственную посредственную работу, а настроить отдельного оценщика на скепсис проще, чем научить генератор самокритике.
В Codex это /review — он запускает отдельного ревьюера, который не меняет рабочее дерево, — или свой субагент с инструкцией «только читать и искать ошибки». Механическое — линт, форматирование — документация советует оставить CI, а ревьюеру дать 2–3 правила с последствиями и безопасным путём.
Открытые вопросы
Бёкелер честно перечисляет то, на что ответа пока нет. Как держать направляющие и датчики согласованными, когда их сотни. Как агент должен поступать, если инструкция и сигнал датчика противоречат друг другу. Означает ли молчащий датчик высокое качество или слабое обнаружение. И как измерять покрытие самой обвязки — по аналогии с покрытием кода тестами. Последнему вопросу посвящён урок о проверке поведения.
Попробуйте сами
10–15 мин на рабочем месте- Нарисуйте таблицу 2×2 из урока для своего репозитория и впишите всё, что есть. Отметьте пустую клетку, которую дешевле всего заполнить.
- Добавьте в проект PostToolUse-хук, который запускает линтер только по изменённому файлу и возвращает
decision: blockс инструкцией. Подтвердите его через/hooksи дайте агенту задачу, где он почти наверняка нарушит правило. - Замерьте, сколько времени проходит от правки до сигнала об ошибке на каждом из пяти уровней в вашем проекте. Самый медленный полезный сигнал — первый кандидат на перенос влево.
Коротко
- Направляющие действуют до шага агента, датчики — после; нужны и те и другие.
- Детерминированные проверки дешёвые и надёжные, вероятностные понимают смысл — у каждых своё место.
- Чем раньше сигнал, тем дешевле исправление: хук через секунды лучше CI через двадцать минут.
- В Codex внутриходовая петля строится на хуках PostToolUse и Stop с
decision: blockи защитой от зацикливания. - Повторяющаяся проблема — повод усилить направляющую или датчик, а не поправить результат вручную.