- BrainTools - https://www.braintools.ru -
Когда AI-агенту дают только stack trace и текст файла с упавшим тестом, он часто чинит симптом, а не причину. Тест зеленеет, баг переезжает в master, через неделю всплывает в другом месте. На простых багах это незаметно, на нетривиальных — становится правилом.
В этой статье — что меняется, если вместо stack trace дать агенту сжатый трейс выполнения, собранный на стороне IDE. Три бага из реального кода, эвристики сжатия трейса, склейка многопоточных вызовов по timestamp и цифры на датасете BugSwarm Pro для DeepSeek V3.2 и проприетарных LLM.

Статья пригодится, если вы:
Java/Kotlin-разработчик и хотите понять, почему AI-агент в IDE «не справляется» с реальными багами;
архитектор или тимлид и выбираете подход к интеграции AI в дебаг-процесс;
интересуетесь оценкой качества LLM на инженерных задачах и бенчмарками вроде BugSwarm.
Stack trace отвечает на вопрос «где упало». Он не отвечает на вопрос «почему пришло именно к этому состоянию». На простых багах эти два вопроса совпадают. На нетривиальных — нет: реальная причина в другой ветке вызовов, которую stack trace не видит вообще.
LLM в такой ситуации уезжает в локальный контекст ошибки [1] и предлагает «локальный фикс»: поправить условие в той строке, рядом с которой упало. Тест становится зелёным. Баг — нет.
Альтернатива — давать агенту трейс выполнения теста: дерево вызовов с аргументами, возвращаемыми значениями и брошенными исключениями. Дальше — три кейса, где это меняет ответ агента.
Тест падал на ассерте: ожидалось true, получено false. Поле useDevSite приходило в false, хотя в JSON стояло true.
{
"annotations": { ... },
"useDevSite": true
}
Без трейса агент с Claude Opus 4.6 за полчаса сошёлся на симптоматическом фиксе: добавить отдельную десериализацию именно поля useDevSite, чтобы тест позеленел. Claude Code в тех же условиях тоже не справился.
С трейсом причина становится видна за пару минут. Дело было не в useDevSite, а в самописном десериализаторе соседнего поля annotations: он дважды читал из парсера и ломал его состояние. После этого падала десериализация всех последующих полей по порядку, в том числе useDevSite.
Без полной цепочки вызовов агента «утаскивает» в локальный контекст ошибки. Симптом — у поля useDevSite, причина — у соседа сверху. Stack trace эту связь не показывает.
Тест InvokeTest падал с DAGTraversalException из-за инвертированного логического условия в интерпретаторе конструкции ForEach. Падение и причина были в разных цепочках вызовов, реальный баг сидел на глубине 51.
Очевидное решение — обрезать трейс по глубине — здесь не работает: причину обрежет первой. Поэтому мы используем эвристики сжатия:
Приоритезация исключений. Ветви, в которых выбрасываются и ловятся исключения, сохраняем целиком — там чаще всего живёт причина.
Сжатие циклов. Дерево не разрастается от однотипных итераций: оставляем шаблон + 1–2 репрезентативных прохода.
Репрезентативные вызовы. На большой глубине сохраняем уникальные вызовы, отсекаем повторяющиеся.
Компрессия идентификаторов. Случайные строки (UUID, хеши, токены) токенизируются примерно в 2,5 раза хуже обычного текста. Подмена их на короткие псевдонимы заметно экономит контекст и деньги.
Цель — не «уложиться в окно любой ценой», а сохранить ту часть дерева, в которой действительно живёт причина.
Тест контроллера падал после обновления библиотеки. Метод exchange() инициировал вызов BaseLlmController.streamingChat() в другом потоке. На выходе — два независимых дерева вызовов и stack trace, в котором настоящего исключения нет вообще.
Чтобы не терять связь между потоками, мы склеиваем трейсы по временным меткам:
T+0 ms Thread-main: exchange()
T+3 ms Thread-main: → POST /chat
T+5 ms Thread-llm-1: streamingChat()
T+12 ms Thread-llm-1: ✗ IllegalArgumentException("model: gpt-5.4")
T+18 ms Thread-main: ← 500 Internal Server Error
Без склейки агент видит два дерева как параллельные миры. Со склейкой — что 500-я в основном потоке прилетела в ответ на исключение из llm-потока.
Причина оказалась хрестоматийной: после обновления библиотека стала требовать имя модели GPT-5.4 строго в верхнем регистре. Исключение прилетало в чужом потоке и в основной stack trace не попадало.
Прогоняли агента на датасете BugSwarm Pro: 43 нетривиальных бага в реальных Java-приложениях, в которых stack trace не указывает на файл с настоящим фиксом. Это сознательный отбор: лёгкие баги в этот датасет не попадают.
Сравнивали два режима — без трейса (только stack trace + контекст файла) и с трейсом — на трёх моделях:
|
Метрика |
Что измеряем |
|---|---|
|
Зелёный тест |
Тест прошёл после фикса агента |
|
Попадание в файл |
Файл с фиксом совпал с файлом эталонного фикса |
|
Семантическое соответствие |
LLM-as-a-judge сравнивает фикс агента с эталонным |
Что показали данные:
На всех трёх метриках и всех трёх моделях (включая открытую DeepSeek V3.2) добавление трейса улучшает результат.
В попадании фиксом в правильный файл DeepSeek V3.2 с трейсом выходит на уровень проприетарных моделей без трейса.
Если запускать каждый эксперимент трижды и считать «победы», подход с трейсом надёжнее базового в разы — на багах, где результат вообще зависит от подхода, а не воспроизводится на любой конфигурации.
Цифра про DeepSeek важна на практике: в банках, телекоме, госсекторе и КИИ часто запрещено отправлять код во внешние LLM, и open-source модель внутри периметра — единственный реалистичный вариант. Трейс выравнивает её с закрытыми моделями там, где это критично.
Трейсы выполнения нужны не только для багфикса. Из практики:
Ответ на вопрос «почему так». Диаграмма последовательностей по зелёному тесту иногда ловит баги, которых тест не видит. В одном из наших тестов диаграмма показала, что в перекладывании DTO теряется поле reasoningContent. Тест зелёный, баг есть.
Ревью PR через сравнение трейсов. Один и тот же тест до и после изменений — видно, что реально поменялось в поведении [2], а не только в diff’е.
Актуализация документации на основе того, как код работает сейчас, а не как он работал во времена, когда документация писалась.
Stack trace отвечает на вопрос «где упало», трейс выполнения — на вопрос «почему пришло к этому состоянию». На нетривиальных багах разница принципиальная.
Обрезать трейс по глубине нельзя: причина чаще всего находится глубже точки падения. Нужны эвристики — по исключениям, циклам, репрезентативным вызовам и идентификаторам.
В многопоточных сценариях трейсы потоков надо склеивать по timestamp, иначе агент видит два независимых дерева и причинно-следственную связь не строит.
Open-source модели с трейсом догоняют проприетарные без трейса. Для on-prem-сценариев это меняет экономику.
Кейсы и бенчмарки выше — основа нашего доклада на JPoint 2026.
Автор: dirvika
Источник [3]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/29996
URLs in this post:
[1] ошибки: http://www.braintools.ru/article/4192
[2] поведении: http://www.braintools.ru/article/9372
[3] Источник: https://habr.com/ru/companies/veai/articles/1033008/?utm_campaign=1033008&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.