Привет, Хабр! Меня зовут Андрей Бирюков. Я — независимый эксперт в области ИТ и ИБ, преподаю в учебных центрах и пишу статьи и книги.

Если вы ловите себя на мысли: «Мне нравится ответ ИИ, но я всё равно должен перепроверить каждый абзац» — значит, вы используете его как генератор красивых слов, а не как инструмент. Галлюцинации возникают не потому, что ИИ глупый. Они возникают потому, что архитектура генерации текста (авторегрессия) подталкивает модель выбирать статистически вероятное следующее слово, а не фактически верное.

И здесь наша задача — перехватить управление у этой авторегрессии. В этой статье мы построим систему из трёх защитных контуров: ограничение свободы, валидация неуверенности и автоматическая валидация.

Шаг 1. Отказ от нарратива. Переводим ИИ в режим «Следователя»

Первая и главная ошибка — просить ИИ «напиши ответ». Глагол «напиши» запускает в модели сценарий литератора. Нам нужно запустить сценарий аналитика. Для этого мы полностью запрещаем ему выдавать итоговый текст в первой итерации.

Вместо: «Напиши отчет о продажах за март».

Делаем: «Твоя задача — заполнить строгую структурную форму фактами. Ты не пишешь ответ пользователю. Ты готовишь черновик для самого себя».

Вот как должен выглядеть ваш первый промпт. Обратите внимание на жесткую структуру, которая не оставляет места для «воды»:

Системная инструкция (System):

Ты — фактологический экстрактор. Твоя задача — извлечь из предоставленного контекста (или из твоих знаний, если контекст пуст) только проверяемую информацию.

Запрещено использовать вводные конструкции («вероятно», «может быть», «считается»).

Запрещено давать советы.

Если факта нет в контексте, ты обязан указать это в соответствующем поле.

Запрос пользователя (User):

На основе имеющихся данных заполни следующую JSON‑структуру. Не добавляй никакого текста кроме этой структуры:

{

«extracted_facts»: [«массив из 3–5 сухих утверждений, взятых слово в слово или максимально близко к источнику»],

«logical_conclusions»: [«массив логических выводов, следующих из фактов (но только явных)»],

«missing_data»: [«четко перечисленные пункты, которых не хватает для полного ответа на мой вопрос»]

}

Давайте разберем, почему такой подход работает. Когда вы указываете формат JSON и конкретные имена полей, вы смещаете распределение вероятностей модели. Она перестаёт думать о том, как красиво построить предложение, и начинает думать о том, как заполнить ячейки. Это снижает галлюцинации примерно на 40% уже на старте, потому что формат диктует строгость.

Шаг 2. Внедряем метрику «Уверенность» (Confidence Score) и учим ИИ говорить «Нет»

ИИ никогда не признается, что он чего‑то не знает, если вы его об этом не попросите. Для него «не знать» — это паттерн с низкой вероятностью, он пытается его избегать. Нам нужно ввести этот паттерн в явном виде через шкалу оценки.

Для этого мы модифицируем структуру из Шага 1, добавив в неё поле uncertainty.

Важный нюанс: вы не просто просите «оцени вероятность». Это бесполезно, модель даст вам 95% на всём подряд. Вы должны задать критерии оценки.

Добавьте в системную инструкцию следующий блок:

Критерии оценки уверенности (важно!):

  • 0.0 — 0.3 (Низкая уверенность): Ты не находишь прямой информации в контексте, и твой вывод основывается исключительно на общих знаниях модели.

  • 0.4 — 0.7 (Средняя уверенность): Ты находишь косвенные подтверждения, но цифры или даты отсутствуют.

  • 0.8 — 1.0 (Высокая уверенность): Данные продублированы в нескольких источниках контекста или являются прямой цитатой из авторитетного документа.

Критическое правило: Если по какому‑либо из запрошенных пунктов твоя уверенность ниже 0.7, ты ОБЯЗАН в поле answer_to_user написать только одну фразу: «Информация для однозначного ответа отсутствует. Требуется уточнение источника.» Заполнять это поле собственными домыслами категорически запрещено!

Теперь ваш промпт для второго прохода (после сбора фактов) выглядит так:

User:

Возьми массив extracted_facts и массив logical_conclusions из предыдущего шага. Оцени каждый пункт по шкале уверенности. Заполни новую структуру:

{

«verified_answer»: «Твой итоговый ответ пользователю (только если все пункты имеют уверенность > 0.7)»,

“uncertainty_report”: {

«fact_1»: {«confidence»: 0.9, «reason»: «прямая цитата»},

«fact_2»: {«confidence»: 0.4, «reason»: «данные за 2022 год, вопрос за 2026»}

},

«final_decision»: «Принять» или «Отклонить» (на основе порога 0.7)

}

Здесь мы создаём «красную кнопку» в голове модели. Порог 0.7 становится механизмом безопасности. Модель знает: если она выдаст бред с низкой уверенностью, её «поймают». А так как в промпте явно прописано поощрение фразы «не знаю» (она становится легитимным действием), ИИ перестаёт бояться и начинает честно указывать на пробелы.

Шаг 3. Техника «Адвокат дьявола» (Внутренняя антигаллюцинаторная проверка)

А это самый мощный шаг. ИИ не может предсказать, что он сам же и солгал, в тот самый момент, когда пишет ответ. Но ИИ отлично умеет находить противоречия в чужом тексте. Мы воспользуемся этим.

Мы не даём первый ответ пользователю. Сначала мы отдаём сгенерированный JSON обратно в новое окно чата (или выполняем второй вызов API) с одной единственной задачей: разрушить собственную конструкцию.

Вот промпт для третьего (валидационного) шага:

System (Режим «Критик»):

Ты — строгий редактор‑скептик. Твоя единственная задача — найти логические ошибки, нестыковки и недоказанные утверждения в предоставленном ответе. Ты должен пытаться опровергнуть каждый факт.

User:

Вот черновик ответа, который подготовил мой ассистент: {вставьте сюда ваш JSON из Шага 2}.

Выполни проверку по трём пунктам:

Есть ли в verified_answer утверждение, которое не следует напрямую из extracted_facts? Если да, укажи это утверждение жирным.

  • Может ли logical_conclusion быть истолкован иначе? Предложи альтернативную интерпретацию.

  • Если бы ты был судьёй в суде, принял бы ты этот ответ как доказательство? Ответь «Да» или «Нет» и дай одно предложение, почему.

Что можно дальше сделать с результатом? Вы получаете JSON с замечаниями критика. Теперь у вас есть два варианта:

  • Если критик нашёл серьёзное противоречие, вы отправляете ИИ финальный промпт: «Учитывая замечания критика, перепиши итоговый verified_answer, устранив эти противоречия. Если противоречие неустранимо, замени ответ на „Недостаточно данных“„.“»

  • Если критик не нашёл ничего, вы со спокойной душой отдаёте ответ пользователю.

Шаг 4. Железобетонный бэкенд‑фильтр (Валидация, а не доверие)

Это шаг, который отделяет игру от настоящей инженерии. Вы не должны надеяться, что ИИ правильно заполнил JSON в Шаге 2. Вы должны отсечь некорректные ответы на серверной стороне ДО того, как они увидят свет.

Реализуйте простой скрипт‑валидатор. Его логика:

  1. Парсинг JSON. Если JSON не распарсился — ответ не отправляется, возвращается ошибка «Сбой генерации».

  2. Проверка наличия поля final_decision. Если там стоит «Отклонить» или значение confidence в uncertainty_report меньше заданного порога (например, 0.7) — вы автоматически заменяете содержимое поля verified_answer на шаблонную фразу: «Извините, на основе предоставленных данных я не могу дать точный ответ. Пожалуйста, уточните входные параметры.»

  3. (Опционально, но мощно). Простой подсчёт длины. Если verified_answer содержит менее 10 слов, а вопрос был сложный — скорее всего, ИИ срезал углы. Отклоняйте такие ответы.

Здесь никакой промпт‑инжиниринг не даёт 100% гарантии. Но когда вы на уровне кода говорите: «Если сомневаешься — не отвечай», вы превращаете вероятностную машину в детерминированный классификатор. Пользователь может получить отказ, но он никогда не получит ложную информацию. Репутация системы дороже, чем иллюзия всезнания.

Шаг 5. Собираем все слои в единый пайплайн (Пошаговый алгоритм действий)

Чтобы этот гайд, представленный в статье не остался теорией, вот четкая последовательность ваших действий при каждом запросе к ИИ. Не делайте это в одном промпте — разнесите по последовательным вызовам API.

Действие 1. Запрос фактов.

Отправляем запрос с инструкцией заполнить extracted_facts, logical_conclusions и missing_data. Запрещаем итоговый ответ.

Действие 2. Оценка и самоцензура.

Берём ответ из Действия 1 и отправляем новый запрос. В нём мы просим модель оценить уверенность по каждому из пунктов и принять решение о допуске (порог 0.7). Получаем JSON с final_decision.

Действие 3. Жесткий фильтр бэкенда.

На сервере читаем final_decision. Если «Отклонить» — сразу отдаём пользователю заранее заготовленную фразу о нехватке данных. Пайплайн прерывается. Дальше ИИ не вызываем (экономим токены и время).

Действие 4. Адвокат дьявола (если ответ принят).

Если ответ принят, отправляем JSON из Действия 2 на проверку критику (как в Шаге 3). Получаем замечания.

Действие 5. Финальная отшлифовка.

Отправляем критику обратно в модель с задачей исправить ошибки. Только теперь мы позволяем модели выдать красивый, связный текст verified_answer пользователю, потому что у неё есть железобетонная база из фактов и отказ от всех рискованных умозаключений.

Заключительное правило

Запомните главную аксиому этого гайда: ИИ — это не база знаний. ИИ — это компилятор. Вы даёте ему строгие типы (формат JSON), строгие условия (if confidence < 0.7 then refuse) и строгие тесты (критика). Как только вы перестаёте просить его «быть умным» и начинаете просить «быть точным в рамках заданных полей», галлюцинации перестают быть вашей проблемой. Они становятся просто статистическим шумом, который отсекается на первом же шаге вашего конвейера.

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

Продолжить тему можно на уроках OTUS:

Комментарии (0)