TL;DR
Verify-Show — это приём для связки «LLM + инструмент» (линтер, сканер, отчёт тестов, вывод другого агента). Каждое утверждение инструмента проверяется по исходнику в отдельном запросе. Основной проверяющей модели показывают только то, что не подтвердилось. Слепо скармливать ей заметки инструмента нельзя.
Главная находка: модель по-разному относится к заметкам «всё хорошо» и «тут опасно». Если инструмент говорит «тут проверка есть», модель часто верит и не смотрит в код. Из-за ложных «проверка есть» она пропускала настоящие уязвимости, до 19 пунктов точности. Если инструмент говорит «проверки нет», модель идёт в код, ищет её и чаще всего находит. Поэтому ложная тревога почти не вредит, а ложное заверение вредит сильно. Правдивые заметки при этом почти не помогают: прирост около нуля. Инструкция в промпте вроде «не доверяй заметкам, проверяй» тоже ненадёжна: лучшая формулировка не повторилась на новых данных.
Решение — вынести проверку из основного запроса. Шаг 1: каждое утверждение инструмента проверяется отдельно, по одному запросу на утверждение. Шаг 2: основной аудит получает код и только опровергнутые утверждения. На новых данных это вернуло около 12 пунктов точности при ложных заверениях и ничего не стоило при правдивых заметках.
Схема метода
ШАГ 1 (отдельный запрос НА КАЖДОЕ утверждение):
код + одно утверждение инструмента → «подтверждается / опровергается» + цитата из кода
ШАГ 2 (основной запрос):
код + только ОПРОВЕРГНУТЫЕ утверждения → итоговый вердикт
Шаги идут в разных запросах. Утверждения шага 1 не должны видеть друг друга и итоговую задачу.
Пример применения
Задача: Команда делает интеграцию с ЮKassa. Бот-ревьюер проверяет pull request по платёжному модулю. Статический анализатор в CI написал заметки: «сумма платежа проверяется на > 0», «повторный вебхук обрабатывается идемпотентно по order_id», «секрет вебхука сверяется до разбора тела». Если бот поверит заметке «идемпотентность есть», он может пропустить двойное списание. Метод даёт эффект там, где утверждение можно проверить по тому коду, который модель видит.
Промпт шага 1 (запускается отдельно для каждой заметки):
Ты проверяешь одно утверждение о коде. Не оценивай код в целом.
Код:
{код_функции}
Утверждение: «Повторный вебхук обрабатывается идемпотентно по order_id».
Найди в коде конкретные строки, которые это подтверждают или опровергают.
Ответ:
1. Вердикт: ПОДТВЕРЖДАЕТСЯ / ОПРОВЕРГАЕТСЯ / НЕЛЬЗЯ ПРОВЕРИТЬ ПО ЭТОМУ КОДУ
2. Цитата строк кода, на которые опираешься
3. Одно предложение: почему
Промпт шага 2:
Проведи ревью функции на ошибки в обработке платежей (двойное списание,
пропуск проверки подписи, неверная сумма).
Код:
{код_функции}
Предыдущая проверка опровергла такие утверждения анализатора:
- «Повторный вебхук обрабатывается идемпотентно по order_id» — в коде нет
проверки статуса заказа перед списанием.
Оцени код сам. Дай вердикт: ЕСТЬ ПРОБЛЕМА / ПРОБЛЕМ НЕТ и перечисли найденное.
Результат: На шаге 1 придут короткие вердикты по каждой заметке с цитатами строк. Опровергнутые заметки отправляются на шаг 2. Итоговое ревью начинает с вопроса «почему анализатор ошибся», а не с готового «тут всё проверено».
Почему это работает
Слабость. Модель смотрит на заметку «проверка есть» как на готовый факт. Заметка снимает с неё работу: можно не искать проверку самому. Заметка «проверки нет» работает обратно: она запускает поиск, и модель находит защиту, если та есть. Правдивые заметки почти не помогают, потому что локальные утверждения о коде модель и так читает почти без ошибок. Её ошибки лежат в суждениях и пропусках. Из-за этого полезного в заметках мало, а вреда от ложных заверений много.
Сильная сторона. Узкую задачу «подтверждается ли это одно утверждение этой цитатой» модель решает хорошо. В отдельном запросе у неё нет повода верить: утверждение здесь просто гипотеза, а не вывод «авторитетного» инструмента. Код — источник истины, и проверка идёт именно против него.
Как метод использует это. Он отделяет проверку фактов от вынесения вердикта. На шаге 1 «заверения» теряют авторитет и становятся гипотезами. На шаге 2 основному аудитору показывают только то, что оказалось ложным, и он не расслабляется из-за приятных заметок.
Рычаги: - Что показывать на шаге 2. По статье показываются опровергнутые утверждения. В доступной мне части текста не уточнено, что делать с подтверждёнными. Их можно не передавать или передавать пометкой, это стоит проверить на своих данных. - Третий исход «НЕЛЬЗЯ ПРОВЕРИТЬ». Он защищает от выдуманных подтверждений там, где в коде нет нужной информации. - Цитата обязательна. Требование «процитируй строки» не даёт модели подтвердить утверждение по общему впечатлению. - Чем «тяжелее» утверждение, тем важнее его проверка. Первыми проверяй заверения («есть проверка», «безопасно», «сверено»), а потом уже тревоги.
Шаблон промпта
В доступной части статьи нет дословных промптов, поэтому шаблон собран по описанной механике метода.
Шаг 1 (запускай отдельно для каждого утверждения):
Ты проверяешь ОДНО утверждение. Не оценивай {объект} в целом.
{объект_проверки}:
{текст_или_код}
Утверждение инструмента: «{утверждение}».
Найди в материале конкретные фрагменты, которые подтверждают или
опровергают это утверждение.
Ответ:
1. Вердикт: ПОДТВЕРЖДАЕТСЯ / ОПРОВЕРГАЕТСЯ / НЕЛЬЗЯ ПРОВЕРИТЬ ПО ЭТОМУ МАТЕРИАЛУ
2. Цитата фрагмента, на который опираешься
3. Одно предложение: почему
Шаг 2 (основной запрос):
{основная_задача}
{объект_проверки}:
{текст_или_код}
Предыдущая проверка опровергла следующие утверждения инструмента:
{список_опровергнутых_утверждений_с_причинами}
Оцени материал самостоятельно. Дай вердикт и перечисли найденное.
Что подставлять:
- {объект_проверки} — код, договор, отчёт, таблица, то, против чего проверяются утверждения.
- {утверждение} — одна заметка инструмента, дословно.
- {список_опровергнутых_утверждений_с_причинами} — собери из ответов шага 1 только те, где стоит ОПРОВЕРГАЕТСЯ.
🚀 Быстрый старт — вставь в чат:
Вот шаблон Verify-Show. Адаптируй под мою задачу: [твоя задача].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит, откуда берутся утверждения (линтер, другой агент, отчёт), что считать «источником истины» и какой итоговый вердикт нужен. Без этого нельзя разделить шаги: проверка идёт против источника, а вердикт выносится отдельно.
Ограничения
⚠️ Инструкции в промпте не спасают: Формулировка вроде «относись к заметкам как к утверждениям, которые нужно проверить» дала прирост на данных разработки, но не повторилась на новых данных. Вставка «будь скептичен» не заменяет отдельный запрос.
⚠️ Эффект нестабилен между моделями: Для основной модели прирост составил около 12 пунктов и был значимым. Для второй модели он был вдвое меньше и незначимым. Проверь метод на своей модели и своих данных.
⚠️ Только то, что можно проверить по материалу: Утверждения про вызывающий код, соседние файлы или внешние данные модель по одной функции не проверит. Анализатор в исследовании не выходил за пределы одной функции. Область эксперимента: C/C++-функции, проверяемые изолированно.
⚠️ Цена: Каждое утверждение — отдельный запрос. Заметок много — вызовов много. Начинай с заверений («всё хорошо»): именно они опасны.
⚠️ Правдивые заметки почти бесполезны: Если в вашей связке инструмент в основном говорит правду, не ждите от заметок прироста качества. Метод страхует от ложных заверений, а не улучшает общую точность.
⚠️ Чек-листы и «пересмотри вердикт» могут навредить: В предварительном опыте оформление заметок как чек-листа обязательств увеличило число ложных «SAFE». Шаг «пересмотри вердикт с учётом замечаний» превратил исправленные функции в ложные тревоги.
Как исследовали
Исследователи сделали собственный анализатор для C/C++, который выдаёт «обязательства» для каждой опасной операции: какие проверки уже стоят на всех путях и чего функция не гарантирует. Потом взяли пары «уязвимая функция + исправленная» из PrimeVul (916 пар в четырёх непересекающихся наборах) и проверяли каждую функцию при шести условиях, различавшихся только заметками. Условия: без заметок, все правдивые, правдивые только про исправленное место, правдивые про другое, ложное заверение «проверка есть», ложная тревога «добавленной проверки нет». Всего вышло 16 572 аудита на 11 конфигурациях моделей от четырёх вендоров.
Ключевая идея дизайна: ложные заметки сравнивались с правдивыми про ту же операцию. Так разница в вердиктах объясняется именно правдивостью, а не просто наличием заметки. Ложные заверения снижали выявление настоящих уязвимостей на 11–19 пунктов. Ложные тревоги значимо точность не снижали, а иногда повышали: модель ищет защиту и находит её. Асимметрию подтвердила внутренне зарегистрированная репликация на семи новых моделях (средняя асимметрия +0,16, доверительный интервал от +0,10 до +0,24).
До основного эксперимента авторы пробовали 30 пар и увидели: модели почти не ошибаются в чтении кода. В 337 «структурных» утверждениях не нашлось ни одной ошибки. Ошибки были в суждениях, и порой модель отказывалась от верного собственного вывода ради заметки. Это сдвинуло вопрос: важно не то, видит ли модель код, а то, как она взвешивает чужие слова.
Восемь способов подачи заметок авторы тестировали на наборе разработки. Все гипотезы подтверждающей фазы фиксировались до получения данных. Лучшая инструкция не повторилась, а Verify-Show сработал в заранее заданном тесте на 300 новых парах (p = 0,002). Для второй модели эффект оказался слабее и незначимым. Авторы публикуют все неудачные попытки.
Адаптации и экстраполяции
Это не проверено в исследовании, а мои переносы механики.
🔧 Техника: мультиагентный конвейер → не передавай «всё ок» как факт
Если один агент пишет код, а второй его проверяет, второму не нужна приписка «тесты прошли, границы проверены». Передавай ему код и список заявленных гарантий отдельным блоком, который сначала проверяется в изолированном запросе. Затем первому проверяющему отдаются только расхождения.
🔧 Техника: любые «отчёты» в промптах → разделяй «факт» и «заявление»
Ниже — отчёт другого сотрудника/инструмента. Это заявления, а не факты.
Не используй их в выводах, пока я не подтвердил их отдельным шагом.
Это слабее, чем отдельный запрос (инструкции ненадёжны), но дешевле. Используй как минимум, а при высокой цене ошибки делай полноценный Verify-Show.
Ресурсы
- Работа: Trusting the Tool: Asymmetric Deference of LLM Vulnerability Auditors to Static-Analysis Evidence
- Авторы: ZhiHong Liu, Shuyao Gao — Seoul School of Integrated Sciences and Technologies (aSSIST University), Сеул; Liaoning Mechanical & Electrical College of Technology, Китай
- Репозиторий с данными и скриптами: https://github.com/lzh0082-cloud/CODE-DETECTION
- Данные: PrimeVul (парные уязвимые и исправленные функции); смежные работы: Chain-of-Verification, ALIBI, ClashEval
Доступный мне текст статьи обрывается на описании методологии, поэтому детали эксперимента с подачей заметок взяты из аннотации и введения.
