Klepa AI
Установить
Часть 4 · Доверяй, но проверяй · Урок 7

Ловим ошибки агента автоматически: слои проверок, бюджеты, петли и выдуманные методы

Агент ошибается не так, как человек: быстро, уверенно и иногда с отчётом «всё готово». Разберём, какие ошибки бывают и как встроить в harness проверки, которые ловят их без вас.

~50 минут 4 интерактива 2 песочницы с проверками Примеры: JS · TS · PHP · Python

После урока вы сможете

  • узнавать семь типовых ошибок агента в логе его работы;
  • собрать слои защиты: проверки кодом, бюджеты, трейсы и человек на воротах;
  • написать детектор петли и проверку выдуманных методов;
  • подключить линтер и тесты к агенту через хуки.
1 Аналогия

Конвейер с ОТК

Представьте

Завод выпускает детали очень быстро. Никто не смотрит на каждую руками. Вместо этого на ленте стоят датчики, и брак снимают до упаковки. Агент — такая же быстрая линия. Ему нужен свой ОТК (отдел технического контроля).

Лента: зелёные — годные, красные — брак

анимация
0деталей
0брака снято
0брака дошло до клиента
Датчик размера= линтер и типы: ловят выдуманные методы
Проверка на стенде= тесты: ловят неверную логику
Пломба на станке= защищённые файлы: агент не ослабит тест
Счётчик с автоматом= бюджет токенов и шагов: стоп-кран для петли
Журнал смены= трейс: что агент делал на каждом шаге
Мастер смены= человек на ключевых воротах
2 Объяснение

От простого к сложному

Уровень 1 · Суть

Каждый шаг проверяет код, у каждой задачи есть бюджет

Человек ошибается медленно и обычно сомневается. Агент делает десятки правок в минуту и звучит одинаково уверенно, когда прав и когда нет (урок 1). Читать всё руками невозможно. Поэтому правило простое:

  • после каждого действия агента его результат проверяет код: линтер, типы, тесты;
  • у каждой задачи есть лимиты: шаги, токены, время;
  • каждый шаг записывается, чтобы потом разобраться.
ГлавноеНе верьте отчёту агента «всё готово». Верьте результатам проверок, которые агент не может подделать.
Уровень 2 · Детали

Семь типовых ошибок и чем их ловить

Выдуманный APIВызов метода или импорт, которого нет: Money::fromFloatSafe().Ловят: статанализ (phpstan, tsc, mypy), тесты.
Неверная логикаКод работает, но считает не то: скидка не применяется.Ловят: тесты, особенно написанные до правки.
Правки вне задачи«Заодно поправил» конфиг безопасности или соседний модуль.Ловят: проверка diff — какие файлы можно трогать.
Ослабленный тестТест красный, и агент поменял ожидание в тесте, а не код.Ловят: запрет правки tests/ без явного разрешения, ревью.
ПетляОдна и та же команда раз за разом, с тем же результатом.Ловят: детектор повторов, лимит шагов.
ПерерасходЗадача на 50 тысяч токенов съела 2 миллиона.Ловят: бюджет токенов и денег на задачу.
Ложный отчёт«Готово, тесты проходят», а тесты не запускались.Ловят: Stop-хук, который сам запускает тесты.

Слои защиты

Проверки кодомлинтер, типы, тесты
→
Проверки артефактовсхема JSON, границы diff
→
Лимитышаги, токены, время
→
Наблюдаемостьтрейсы, логи шагов
→
Человекна ключевых воротах

Слои ставят от дешёвых к дорогим. Линтер стоит секунды и ловит много. Человек стоит дорого, поэтому он смотрит только то, что прошло все автоматические слои, и только в важных точках: слияние в main, миграция, выкладка.

Уровень 3 · Усложнение

Как встроить контроль в harness

  • PostToolUse-хук после каждой правки файла запускает линтер для этого файла. Ошибки уходят модели сразу (код выхода 2 и текст в stderr), и она исправляет их на следующем шаге, а не через час в CI.
  • Stop-хук не даёт агенту закончить, пока тесты красные. Ложный отчёт «всё готово» становится невозможным.
  • PreToolUse-хук запрещает правку защищённых файлов: тестов, конфигов линтера, CI. Иначе агент «починит» проверку, а не код.
  • Оркестратор (урок 4) держит бюджет: максимум шагов, токенов и времени. Превысил — остановка с отчётом, а не тихий счёт на сотни долларов.
  • Трейс: каждый шаг пишется в JSON-лог: номер, инструмент, аргументы, токены, время, результат. По нему находят петли, дорогие шаги и причины аварий.

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

ЛовушкаНе делайте единственным контролем «вторую модель, которая проверит первую». Это полезный слой (урок 8), но у проверяющей модели те же слабости. Основа — детерминированные проверки, которые дают один и тот же ответ на один и тот же код.
Хуки контроля в коде — линтер после правки, тесты перед финишем
{
  "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 });
3 Интерактив

Ловим ошибки руками и кодом

Найдите ошибки в логе агента

интерактив 1 из 4

Задача агента: «скидка 10% для заказов от 100 BYN не применяется, почини». Для каждого шага выберите, что это. Потом нажмите «Проверить».

Соберите слои защиты

интерактив 2 из 4

Агент решает 100 задач. В части задач он ошибается: 15% выдуманный API, 20% логика, 10% правки вне задачи, 6% ослабленный тест, 5% петля. Включайте слои и смотрите, сколько брака дойдёт до прода и сколько это стоит.

Случайность фиксирована: один и тот же набор задач для честного сравнения.
—ошибок сделано
—поймано слоями
—дошло до прода
—минут человека
—млн токенов
Тип ошибкиСделаноПойманоДо продаКто поймал больше всех

Монитор бюджета: стоп-кран

интерактив 3 из 4

График — сколько входных токенов агент потратил к каждому шагу. С 11-го шага агент застрял: гоняет одну и ту же команду. Поставьте лимит и включите детектор петли.

Нажмите «Запустить агента».

Ловим выдуманный метод по индексу проекта

интерактив 4 из 4

Агент прислал правку. Проверим каждый вызов по индексу символов проекта — списку настоящих классов и методов. Такой индекс строят статанализатор или IDE. Поправьте код и проверьте снова.

Индекс символов проекта — что на самом деле есть в коде
    4 Практика

    Песочницы с автопроверкой

    Напишите два детектора, которые можно поставить в свой 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 минут
    1. Подключите PostToolUse-хук с линтером или статанализатором вашего языка. Попросите агента написать код с заведомо несуществующим методом и посмотрите, как он исправится сам.
    2. Поставьте Stop-хук с тестами. Убедитесь, что агент не может закончить с красным тестом.
    3. Добавьте PreToolUse-хук, который запрещает правку tests/ и конфигов линтера. Сломайте тест и попросите агента «сделать тесты зелёными». Что он сделал?
    4. Запишите трейс одной задачи в JSON Lines. Посчитайте: сколько шагов, сколько токенов, какой шаг самый дорогой.
    5 Чек-лист

    Отметьте, что усвоили

    • Знаю семь типовых ошибок агента и могу узнать их в логе.
    • Понимаю слои защиты и почему их ставят от дешёвых к дорогим.
    • Знаю, как подключить линтер через PostToolUse и тесты через Stop.
    • Понимаю, зачем защищать тесты и конфиги проверок от правок агента.
    • Умею задать бюджет задачи и понимаю, почему петля дорожает быстро.
    • Знаю, что писать в трейс шага и какие метрики процесса считать.
    • Решил обе песочницы: детектор петли и поиск выдуманных методов.
    Урок пройденТеперь брак агента снимается с ленты автоматически. Дальше — как доказать цифрами, что агент и скилы стали работать лучше, а не хуже.