AI-агент сказал «готово» - а на самом деле нет. Почему coding agents врут и как это проверять

Что такое TruthGuard?

TruthGuard — это open-source набор из 6 shell-хуков для Claude Code и Gemini CLI, предотвращающих AI action hallucinations за счёт верификации результатов инструментов снаружи модели. Хуки проверяют SHA256-хэши файлов, exit codes команд и результаты тестов, блокируя коммит, если AI утверждает о действии, которого не произошло.

TL;DR

  • -76.4% разработчиков сталкиваются с высоким уровнем галлюцинаций AI; только 29% доверяют выводу AI (Stack Overflow 2025)
  • -3 типа ошибок: фейковые результаты тестов, выдуманный вывод команд, фантомные правки файлов
  • -Причина: модель предсказывает следующий токен, а не реальный результат инструмента — видит паттерн и дополняет
  • -Промпты не помогают — верификация должна быть снаружи: хуки, проверяющие SHA256 и exit codes
  • -TruthGuard: 6 shell-хуков для Claude Code и Gemini CLI, блокирующих коммит при упавших тестах или пустых правках

Claude Code пишет: «Тесты прошли, коммичу». Смотришь историю - тесты не запускались. Или: «Обновил файл utils.ts» - а файл побайтово тот же, что и до «правки». Или тихо делает git push --force, потому что обычный push не проходит.

Это не баг конкретной версии и не редкий edge case. Это системная проблема AI coding agents - воспроизводится стабильно, задокументирована в десятках GitHub issues.

Масштаб

По данным Qodo State of AI Code Quality 2025, 76.4% разработчиков попадают в квадрант «высокие галлюцинации, низкая уверенность». Каждый четвертый оценивает, что каждое пятое предложение от AI содержит фактические ошибки - несуществующие функции, фейковые API, выдуманные зависимости.

Stack Overflow Developer Survey 2025 (~49 000 респондентов): только 29% доверяют AI-выводу. На 11 пунктов меньше, чем годом ранее. При этом 84% продолжают использовать AI-инструменты.

Вдумайтесь в этот разрыв. Люди работают с инструментом, которому не верят, но проверять каждое действие руками не успевают. Привыкают к фоновому недоверию и надеются, что пронесет.

Три типа вранья

Конкретные кейсы из GitHub issues Claude Code.

Фейковые результаты тестов. Issue #11913: Claude нашел старый файл test-results-clean.json от предыдущего запуска и предъявил его как свежие результаты. Когда пользователь указал на это - сначала отрицал, потом «признался», что тесты не запускались. Что тоже было неправдой. Пользователь написал «STOP STOP STOP. YOU ARE LYING TO ME».

Выдуманный вывод команд. Issue #7381: Claude сгенерировал вывод bash-команд, которые не выполнялись. Утверждал, что создал файл /tmp/claude_test.txt - файла не существовало. Показал вывод date с январем 2025, хотя на дворе был сентябрь.

Фантомные правки. Issue #1501: агент заявляет, что отредактировал файл, но содержимое не изменилось. Сам при этом «уверен», что правка применена, и продолжает работу исходя из этого.

Ответ Anthropic на issue #1501: «likely a limitation of model understanding». Закрыто как NOT_PLANNED.

Почему так происходит

Исследование на ArXiv (сентябрь 2025) разбирает таксономию галлюцинаций LLM-агентов - от фактических ошибок и отклонений от инструкций до более специфичной проблемы: галлюцинации действий, когда модель заявляет, что выполнила инструмент, хотя этого не было.

Последний тип - то, с чем сталкиваются пользователи Claude Code. Механизм такой: когда в контексте есть визуальный паттерн «вызов инструмента + результат», модель предсказывает продолжение паттерна вместо того, чтобы дождаться реального выполнения.

Проще говоря, модель не «решает соврать». Она работает как автокомплит - видит паттерн «запустил тесты» и дописывает «тесты прошли», потому что это статистически вероятное продолжение. Ей все равно, что было на самом деле.

Почему промпты не помогают

Первая реакция - добавить в system prompt: «Всегда проверяй результат. Не утверждай то, чего не делал». Claude Code уже содержит такие инструкции в системном промпте. Не работает.

Промпт - это текст в контексте. Модель взвешивает его наряду с остальными токенами. Когда «уверенность» в том, что действие выполнено, перевешивает инструкцию проверить - инструкция проигрывает. Это свойство архитектуры, а не баг промпта.

Текст нельзя сделать настолько строгим, чтобы модель его гарантированно соблюдала. Нужна проверка снаружи.

Проверять, а не просить

Идея: вместо уговоров агента быть честным - проверять каждое действие программно.

Claude Code и Gemini CLI поддерживают хуки - скрипты, которые запускаются до и после каждого вызова инструмента. Скрипт получает JSON с описанием действия, проверяет результат и решает: пропустить, предупредить или заблокировать.

Агент решает отредактировать файл
    |
[PreToolUse] -> записывает SHA256 хеш файла
    |
Агент редактирует файл
    |
[PostToolUse] -> сравнивает хеш -> БЛОК если не изменился

С чексуммой не поспоришь. Это не промпт, который можно проигнорировать - это программный гейт.

Я реализовал эту схему в TruthGuard - наборе shell-хуков для Claude Code и Gemini CLI. Шесть скриптов, каждый ловит свой тип проблемы.

Что проверяется

Перед каждой правкой файла записывается SHA256 хеш. После - сравнивается. Если файл не изменился после «редактирования» - блок. Фантомная правка определяется мгновенно.

Перед каждым git commit скрипт определяет тестовый фреймворк проекта (Flutter, Node.js, Python, Rust, Go) и прогоняет тесты. Красные - коммит заблокирован. Агент не может сказать «тесты прошли», если они падают.

Опасные команды тоже перехватываются. --no-verify, --force push, rm -rf / - блок. reset --hard и clean -f - предупреждение. Агенты любят «упрощать» себе жизнь опасными командами, особенно когда обычный путь не срабатывает.

Exit code проверяется отдельно: команда упала с ошибкой, а агент продолжает как ни в чем не бывало - хук это ловит.

И последнее. После каждого успешного коммита агент получает: «Ты только что закоммитил код. Останови работу и проверь, что фикс реально решает проблему». Это не блокировка, а nudge - но он заставляет агента сделать паузу вместо мгновенного «Готово!».

Результаты

Погонял два дня на продакшн Flutter-проекте:

  • 5 коммитов заблокировано - каждый раз тесты были красные
  • 3 опасные команды перехвачены: 2 раза git push --force, 1 раз git commit --no-verify
  • Ложных срабатываний: ноль

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

Чего TruthGuard не ловит

Честная часть.

Семантическое вранье. Агент сделал реальное изменение, тесты прошли, но фикс не решает проблему. Чексумма не поможет - файл действительно изменился. Exit code в норме. Формально все правильно, по факту проблема на месте. Напоминание о проверке после коммита частично закрывает этот кейс, но не гарантирует.

Галлюцинации в коде. Агент написал код, который компилируется и проходит тесты, но использует несуществующий API или неправильную логику. Хуки этого не увидят. Тут нужен code review.

Сложные цепочки. Агент выполняет 15 действий подряд, и ошибка - в логической связи между шагами. Каждый шаг верифицирован, но общий результат неверный.

Новые паттерны. Хуки ловят известные способы «срезать углы». Если модель найдет новый - хук для него нужно писать отдельно.

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

Установка

npx truthguard install && npx truthguard init

Или через Homebrew:

brew tap spyrae/truthguard && brew install truthguard

Чистый bash + jq. Никакого бэкенда, никакой телеметрии - все локально. Хуки одинаковые для Claude Code и Gemini CLI - агент-агностик, JSON на входе, JSON на выходе.

Исходники: github.com/spyrae/truthguard

Что дальше

OWASP в декабре 2025 выпустил Top 10 Risks for Agentic Applications - ложные действия агентов и злоупотребление инструментами входят в список признанных угроз. Проблема вышла на уровень индустрии.

Текущая версия TruthGuard - базовая верификация: чексуммы, exit codes, тесты. Следующий шаг - семантическая проверка: второй LLM, который смотрит diff и оценивает, решает ли изменение заявленную проблему. Но это уже pro-версия.

А пока - шесть хуков, которые ловят самые частые паттерны. Два дня тестирования, ноль ложных срабатываний, пять реальных блокировок. Примерно 350 строк bash в сумме. Работает.

FAQ

Могут ли хуки TruthGuard заметно замедлить работу Claude Code, учитывая, что они запускаются до и после каждого вызова инструмента?

На практике накладные расходы незаметны. Самый тяжёлый хук — запуск тестов перед коммитом, который занимает столько, сколько обычно занимает тестовый набор (и должен запускаться в любом случае). Сравнение SHA256-хэшей выполняется менее чем за 10 мс на файл. Единственный сценарий, где хуки добавляют заметную задержку — медленный тестовый набор (дольше 2 минут); в таком случае блокировка --no-verify становится ещё ценнее, поскольку не даёт агентам обходить медленные тесты, а не просто пропускать их.

Если AI-агент галлюцинирует правку файла, но TruthGuard блокирует коммит — агент понимает причину блокировки?

Да — хук возвращает ненулевой exit code с JSON-пейлоадом, описывающим сбой (например, {"type": "phantom_edit", "file": "utils.ts", "reason": "SHA256 unchanged"}). Claude Code читает этот вывод и обычно признаёт сбой в следующем сообщении. Дальнейшее поведение менее предсказуемо: в большинстве случаев агент корректно повторяет правку, но иногда путается и требует явного человеческого промпта вроде «файл не был реально изменён, попробуй ещё раз».

Работает ли TruthGuard с AI-агентами помимо Claude Code и Gemini CLI — например, с Cursor или Windsurf?

Из коробки — нет. TruthGuard использует API хуков, специфичные для Claude Code и Gemini CLI. У Cursor и Windsurf другие механизмы расширений. Базовая логика (сравнение SHA256, проверка exit codes) — универсальный bash, который можно адаптировать, но потребуется вручную интегрировать её в системы плагинов или расширений этих агентов. В GitHub-репозитории есть схема хуков — разумная отправная точка для портирования на другие агенты.