Ловим ошибки агента автоматически: слои проверок, бюджеты, петли и выдуманные методы
Агент ошибается не так, как человек: быстро, уверенно и иногда с отчётом «всё готово». Разберём, какие ошибки бывают и как встроить в harness проверки, которые ловят их без вас.
После урока вы сможете
- узнавать семь типовых ошибок агента в логе его работы;
- собрать слои защиты: проверки кодом, бюджеты, трейсы и человек на воротах;
- написать детектор петли и проверку выдуманных методов;
- подключить линтер и тесты к агенту через хуки.
Конвейер с ОТК
Завод выпускает детали очень быстро. Никто не смотрит на каждую руками. Вместо этого на ленте стоят датчики, и брак снимают до упаковки. Агент — такая же быстрая линия. Ему нужен свой ОТК (отдел технического контроля).
Лента: зелёные — годные, красные — брак
анимацияОт простого к сложному
Каждый шаг проверяет код, у каждой задачи есть бюджет
Человек ошибается медленно и обычно сомневается. Агент делает десятки правок в минуту и звучит одинаково уверенно, когда прав и когда нет (урок 1). Читать всё руками невозможно. Поэтому правило простое:
- после каждого действия агента его результат проверяет код: линтер, типы, тесты;
- у каждой задачи есть лимиты: шаги, токены, время;
- каждый шаг записывается, чтобы потом разобраться.
Семь типовых ошибок и чем их ловить
Money::fromFloatSafe().Ловят: статанализ (phpstan, tsc, mypy), тесты.Слои защиты
Слои ставят от дешёвых к дорогим. Линтер стоит секунды и ловит много. Человек стоит дорого, поэтому он смотрит только то, что прошло все автоматические слои, и только в важных точках: слияние в main, миграция, выкладка.
Как встроить контроль в harness
- PostToolUse-хук после каждой правки файла запускает линтер для этого файла. Ошибки уходят модели сразу (код выхода 2 и текст в stderr), и она исправляет их на следующем шаге, а не через час в CI.
- Stop-хук не даёт агенту закончить, пока тесты красные. Ложный отчёт «всё готово» становится невозможным.
- PreToolUse-хук запрещает правку защищённых файлов: тестов, конфигов линтера, CI. Иначе агент «починит» проверку, а не код.
- Оркестратор (урок 4) держит бюджет: максимум шагов, токенов и времени. Превысил — остановка с отчётом, а не тихий счёт на сотни долларов.
- Трейс: каждый шаг пишется в JSON-лог: номер, инструмент, аргументы, токены, время, результат. По нему находят петли, дорогие шаги и причины аварий.
Метрики процесса, которые стоит считать: стоимость решённой задачи, доля задач с вмешательством человека, сколько брака дошло до ревью и до прода, сколько раз сработал каждый слой. Если слой не сработал ни разу за месяц — проверьте, работает ли он вообще.
Хуки контроля в коде — линтер после правки, тесты перед финишем
{
"hooks": {
"PostToolUse": [
{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "node .claude/hooks/lint-after-edit.js" }] }
],
"Stop": [
{ "hooks": [{ "type": "command", "command": "python3 .claude/hooks/tests-before-stop.py" }] }
]
}
}
// .claude/hooks/lint-after-edit.js — PostToolUse: ESLint для правленого файла
import { execFileSync } from "node:child_process";
let raw = "";
process.stdin.on("data", (c) => (raw += c));
process.stdin.on("end", () => {
const file = JSON.parse(raw).tool_input?.file_path ?? "";
if (!/\.(ts|js)$/.test(file)) process.exit(0);
try {
execFileSync("npx", ["eslint", "--max-warnings=0", file], { encoding: "utf8" });
process.exit(0);
} catch (e) {
// код 2: ошибки уйдут модели, она исправит их на следующем шаге
process.stderr.write(`ESLint нашёл проблемы в ${file}:\n${e.stdout}`);
process.exit(2);
}
});
#!/usr/bin/env php
<?php
// .claude/hooks/lint-after-edit.php — PostToolUse: phpstan для правленого файла
$event = json_decode(stream_get_contents(STDIN), true, flags: JSON_THROW_ON_ERROR);
$file = $event['tool_input']['file_path'] ?? '';
if (!str_ends_with($file, '.php')) {
exit(0);
}
exec('vendor/bin/phpstan analyse --no-progress --error-format=raw ' . escapeshellarg($file) . ' 2>&1', $out, $code);
if ($code !== 0) {
fwrite(STDERR, "phpstan нашёл ошибки в $file:\n" . implode("\n", $out));
exit(2);
}
exit(0);
#!/usr/bin/env python3
# .claude/hooks/tests-before-stop.py — Stop: не отпускать агента с красными тестами
import json
import subprocess
import sys
event = json.load(sys.stdin)
# Если агент уже продолжает работу из-за этого хука, не держим его вечно
if event.get("stop_hook_active"):
sys.exit(0)
proc = subprocess.run(["pytest", "-q", "-x"], capture_output=True, text=True)
if proc.returncode != 0:
tail = (proc.stdout + proc.stderr)[-3000:]
print("Тесты красные, задачу нельзя считать готовой:\n" + tail, file=sys.stderr)
sys.exit(2)
sys.exit(0)
// trace.ts — простой трейс шагов агента в JSON Lines
import { appendFileSync } from "node:fs";
interface TraceStep {
task: string;
step: number;
tool: string;
input: unknown;
inputTokens: number;
outputTokens: number;
ms: number;
ok: boolean;
}
export function trace(s: TraceStep): void {
// одна строка — один шаг: легко грепать, считать и грузить в Grafana/Loki
appendFileSync("var/agent-trace.jsonl", JSON.stringify({ at: new Date().toISOString(), ...s }) + "\n");
}
// В цикле агента после каждого шага:
// trace({ task: "fix-price", step, tool: block.name, input: block.input,
// inputTokens: res.usage.input_tokens, outputTokens: res.usage.output_tokens, ms, ok: !isError });
Ловим ошибки руками и кодом
Найдите ошибки в логе агента
интерактив 1 из 4Задача агента: «скидка 10% для заказов от 100 BYN не применяется, почини». Для каждого шага выберите, что это. Потом нажмите «Проверить».
Соберите слои защиты
интерактив 2 из 4Агент решает 100 задач. В части задач он ошибается: 15% выдуманный API, 20% логика, 10% правки вне задачи, 6% ослабленный тест, 5% петля. Включайте слои и смотрите, сколько брака дойдёт до прода и сколько это стоит.
| Тип ошибки | Сделано | Поймано | До прода | Кто поймал больше всех |
|---|
Монитор бюджета: стоп-кран
интерактив 3 из 4График — сколько входных токенов агент потратил к каждому шагу. С 11-го шага агент застрял: гоняет одну и ту же команду. Поставьте лимит и включите детектор петли.
Нажмите «Запустить агента».
Ловим выдуманный метод по индексу проекта
интерактив 4 из 4Агент прислал правку. Проверим каждый вызов по индексу символов проекта — списку настоящих классов и методов. Такой индекс строят статанализатор или IDE. Поправьте код и проверьте снова.
Индекс символов проекта — что на самом деле есть в коде
Песочницы с автопроверкой
Напишите два детектора, которые можно поставить в свой harness. Ctrl+Enter — запуск.
Песочница 1. Детектор петли
JSНа вход — шаги агента [{ tool, input }]. Петля — это (а) один и тот же вызов три раза подряд или (б) пара разных вызовов, повторённая три раза подряд: A, B, A, B, A, B. Верните { loop: true, at, reason }, где at — индекс шага, на котором петлю можно заметить впервые. Иначе { loop: false }. Порядок ключей в input не важен.
Песочница 2. Выдуманные методы
JSНапишите findHallucinatedCalls(code, index) для PHP-кода. Статические вызовы Class::method( проверяйте по index[Class]; неизвестный класс — тоже находка. Для вызовов ->method( тип объекта неизвестен, поэтому метод должен быть хоть у одного класса индекса. Верните отсортированный список без повторов: 'Money::fromFloatSafe', '->applyDiscountMagic'. Money::class и new Money( — не вызовы метода.
Проверьте себя
Агент поменял ожидаемое значение в тесте с 90 на 100, и тест позеленел. Как ловить такое автоматически?
Просьба — мягкое правило. Защищённые файлы — жёсткое: агент получит отказ и будет вынужден чинить код, а не проверку.
Что быстрее и дешевле всего ловит вызов несуществующего метода?
Phpstan, tsc или mypy находят такие ошибки за секунды и всегда одинаково. Через PostToolUse-хук агент узнаёт об ошибке на следующем же шаге.
Агент сорок раз подряд запускает одну и ту же команду. Что должно сработать?
Каждый повтор отправляет модели всю историю заново, поэтому петля дорожает быстрее, чем линейно (урок 1). Стоп-кран должен быть кодом.
Почему нельзя верить итоговому сообщению агента «всё готово, тесты проходят»?
Модель пишет правдоподобный отчёт, даже если тесты не запускались. Stop-хук, который сам запускает тесты, превращает отчёт в факт.
Задание на своём проекте — 40 минут
- Подключите PostToolUse-хук с линтером или статанализатором вашего языка. Попросите агента написать код с заведомо несуществующим методом и посмотрите, как он исправится сам.
- Поставьте Stop-хук с тестами. Убедитесь, что агент не может закончить с красным тестом.
- Добавьте PreToolUse-хук, который запрещает правку
tests/и конфигов линтера. Сломайте тест и попросите агента «сделать тесты зелёными». Что он сделал? - Запишите трейс одной задачи в JSON Lines. Посчитайте: сколько шагов, сколько токенов, какой шаг самый дорогой.
Отметьте, что усвоили
- Знаю семь типовых ошибок агента и могу узнать их в логе.
- Понимаю слои защиты и почему их ставят от дешёвых к дорогим.
- Знаю, как подключить линтер через PostToolUse и тесты через Stop.
- Понимаю, зачем защищать тесты и конфиги проверок от правок агента.
- Умею задать бюджет задачи и понимаю, почему петля дорожает быстро.
- Знаю, что писать в трейс шага и какие метрики процесса считать.
- Решил обе песочницы: детектор петли и поиск выдуманных методов.
Что почитать и где попробовать
Материалы на русском отмечены зелёной меткой. Все ссылки проверены 18 октября 2026 года.
Почитать
- Фантазии LLM воплощаются в реальности — фальшивые опенсорсные библиотекиslopsquatting: модель выдумывает пакет, а злоумышленник его публикует. рус.
- Эффективное управление затратамиоткуда берётся расход токенов в Claude Code и как его снизить. рус., офиц.
- Как не дать проекту деградировать при работе с Claude Code: правила, хуки и автоматизацияправила в инфраструктуру, а не в промпт: хуки и автопроверки. рус.
- ИИ под контролем: Guardrails как щит от рисков в агентных системахчто такое guardrails (ограничители), на каких этапах и какими бывают. рус., перевод
- LLM Observability & AI Agent Tracing: большой гайдчто отслеживать у агента и обзор open-source трассировки (Langfuse, Phoenix и др.). рус.
Песочницы по теме
- PHPStan Playgroundвставить код с выдуманным методом и увидеть, как статический анализ ловит «галлюцинацию» до запуска.
- regex101написать регулярку-фильтр на секреты (ключи, токены) для guardrail или hook.
Запустить код из урока
- TypeScript PlaygroundJS и TS из примеров урока: вставить и запустить кнопкой Run.
- 3v4l.orgPHP-примеры урока на любой версии PHP.
- JupyterLitePython-примеры урока прямо в браузере, без установки.