КЛАСТЕР 05 · RAG ЦИТАТЫ И АНТИГАЛЛЮЦИНАЦИИ ~18 МИНУТ
День 25 · из 35

Цитаты
и галлюцинации

Финал кластера RAG. У нас отличный поиск, точные результаты, чистый контекст. И всё равно модель иногда отвечает «не из источников». Сегодня — про последний инженерный слой: как заставить модель опираться только на найденное и как это проверять.

Суть урока

Даже идеально найденный контекст не гарантирует, что модель будет на него опираться. Она может смешать его со своими «знаниями из обучения», досочинить детали, перепутать источники. Лекарство — цитирование: модель обязана при каждом утверждении указывать, из какого чанка оно взято. Это меняет три вещи: модель структурно вынуждена опираться на источники, пользователь видит проверяемые ссылки, твоя система может программно проверить, что ответ построен на найденном.

Почему контекста недостаточно

После Дней 22-24 у тебя есть отличный поиск: документы хорошо разбиты, эмбеддинги ловят смысл, реранкер выдаёт точно подходящие чанки. В контексте модели — те самые 5 фрагментов, в которых лежит ответ на вопрос.

Кажется, дело сделано: модель прочитает, ответит. На практике — нет. Вот что реально может случиться:

  • Модель смешивает найденное со «своими знаниями». В контексте написано «X стоит 100 рублей». Модель помнит из обучения, что в среднем X стоит около 80. В ответе пишет «80 рублей» — потому что «так больше похоже на правду».
  • Модель додумывает связи. В контексте — два независимых факта. Модель находит их в одном ответе и логически выводит третий — которого в источниках нет.
  • Модель перепутывает источники. Чанк A говорит про продукт X, чанк B — про продукт Y. В ответе характеристики X приписываются Y.
  • Модель отвечает уверенно при пустом контексте. Если поиск ничего не нашёл — она всё равно отвечает «из общих знаний», не предупредив тебя.

Это всё галлюцинации в RAG-контексте. Они отличаются от обычных галлюцинаций (когда модель просто выдумывает) тем, что правильный ответ был у неё под носом, но она его не использовала или использовала неправильно.

Контекст модели — это предложение, не обязательство. Модель не «обязана» использовать найденное; она просто видит его наряду со своими внутренними знаниями. Сделать привязку обязательной — отдельная инженерная задача.

Почему это особенно опасно в RAG

В обычной генерации (без RAG) пользователь понимает, что модель отвечает «по памяти», и относится к ответам с осторожностью. В RAG-системе — наоборот. Когда продукт говорит «вот мои источники», пользователь доверяет ответу больше, чем «голой LLM». Если в этом доверенном режиме модель тихонько досочиняет — последствия серьёзнее.

Это и есть главная этическая и продуктовая проблема RAG: пользователь думает, что говорит с базой знаний, а на самом деле — с моделью, которая иногда смешивает базу со своим.

Цитаты как механизм привязки

Главный инструмент, который заставляет модель «оставаться в источниках» — требование цитировать. Каждое фактическое утверждение в ответе должно сопровождаться ссылкой на конкретный чанк, из которого оно взято.

Как работает на уровне промпта

В промпт сборки RAG-ответа (то, что собираешь после поиска) ты включаешь:

  1. Нумерованные фрагменты: каждый чанк получает чёткий ID — [1], [2], [3]... В тексте промпта они так и подаются: «Фрагмент [1]: ...текст...».
  2. Инструкцию цитировать: «В ответе после каждого утверждения указывай, из какого фрагмента оно взято в формате [N]. Если информации в фрагментах недостаточно для уверенного ответа, скажи об этом прямо.»
  3. Запрет на «общие знания»: «Используй только информацию из приведённых фрагментов. Не добавляй того, чего там нет.»

В ответе ты получаешь что-то вроде:

Без цитирования

Какой ответ может прийти

Модель уверенно пишет ответ, смешивая найденное со своим. Проверить нельзя.

Платёж не проходит обычно из-за исчерпания лимита или истекшей карты. В нашей системе после трёх неудач блокировка снимается через 24 часа.
С цитированием

Что приходит в идеальной версии

Каждое утверждение явно привязано к источнику. Если что-то «из общих знаний» — модель должна сказать «не нашёл».

Платёж не проходит из-за исчерпания лимита [1] или истекшей карты [2]. В нашей системе после трёх неудач блокировка снимается через 24 часа [3].

Что меняется внутри модели

Когда модель обязана при каждом утверждении ставить номер источника, у неё меняется поведение на уровне генерации токенов:

  • Самопроверка. Перед написанием утверждения модель «прикидывает», есть ли оно во фрагментах. Если нет — она не может его подкрепить ссылкой, и инструкция её останавливает.
  • Меньше «смешивания». Знания из обучения теряют преимущество — у них нет номера фрагмента. Модели проще написать факт из фрагмента, чем из «памяти».
  • Признание неполноты. Когда инструкция явно разрешает сказать «информации недостаточно», модель чаще так и поступает — вместо досочинения.

На практике это даёт заметный прирост точности: галлюцинации уменьшаются в 2-3 раза, ответы становятся более скромными, но честными.

Структурированные цитаты

Для системы с UI ещё лучше — попросить модель выдать цитаты в структурированном виде (через structured outputs из Дня 2):

  • { "answer": "...", "citations": [{"text": "...", "source": "doc_id_1", "exact_quote": "..."}] }

Это даёт тебе ещё больше контроля:

  • В UI можно показать ссылки кликабельными — нажал на «[1]» в ответе, открылся источник.
  • В коде можно проверить, что exact_quote правда есть в указанном чанке — найти подстроку.
  • Метрики — посчитать, сколько утверждений в ответе процитированы, какова «citation coverage».

Программная верификация

Цитирование — это самодисциплина модели. Иногда она всё равно ошибается: ставит цитату не к тому утверждению, придумывает цитаты на несуществующих фрагментах, цитирует точно — но утверждение в ответе не подтверждается этим текстом.

Поэтому над цитатами ставится программный или LLM-как-судья слой проверки. Это «второе мнение», которое смотрит на финальный ответ + контекст и оценивает: правда ли ответ построен на найденном?

Три уровня верификации

Уровень 1

Существование цитат

Простой код. Каждая ссылка [N] в ответе — проверь, есть ли такой чанк во входе модели. Если ссылка [7], а чанков было только 5 — это галлюцинация.

Уровень 2

Точные подстроки

Если модель цитирует «exact_quote» — твой код проверяет, что эта подстрока правда есть в указанном чанке. Поиск по тексту, никакой LLM не нужен.

Уровень 3

LLM-судья

Отдельный LLM-вызов: «вот ответ, вот источники. Каждое утверждение в ответе — подкреплено ли источниками? Отметь те, что нет.» Самый дорогой и точный.

Что делать с непрошедшими проверку

Когда верификация показала проблемы — есть несколько стратегий:

  • Не показывать пользователю. «Не нашёл достаточной информации в источниках». Самый честный, но иногда раздражающий вариант.
  • Перезапустить с уточнённым промптом. «Предыдущий ответ содержал неподкреплённые утверждения [N1, N2]. Перепиши, опираясь только на источники.» Часто помогает.
  • Пометить ненадёжные части. Показать пользователю ответ, но визуально выделить утверждения без подкрепления: «*Это утверждение не найдено в источниках*».
  • Расширить поиск. Возможно, в базе есть нужные данные, но не попали в top-5. Запустить второй поиск с переформулированным запросом.

Выбор стратегии зависит от цены ошибки. Для медицинского/юридического ассистента — только первый вариант. Для бытового чат-бота — третий или четвёртый.

i
Связь с Кластером 03
Верификация цитат — это инвариант в смысле Дня 14: «каждое утверждение в ответе должно быть подкреплено источником». Когда инвариант нарушен — это сигнал, что надо реагировать (День 15: автономия / код / человек). RAG-системы для серьёзных доменов почти всегда работают с такими инвариантами на финальной стадии.

Решения AI-инженера

Развилка 1. Когда строгие цитаты, когда мягкие

Цитирование можно требовать с разной жёсткостью:

  • Жёстко: каждое предложение должно содержать ссылку, иначе ответ не принимается. Подходит для медицинских/юридических ассистентов, корпоративной поддержки.
  • Мягко: цитировать желательно, но модель сама решает где это нужно. Ответ получается более естественным, но менее проверяемым. Подходит для бытовых чат-ботов, обучающих систем.
  • Контекстно: цитировать только утверждения с конкретными фактами (цифры, имена, даты), общие фразы можно без цитат. Балансированный вариант для большинства случаев.

Подбирается под продукт. Если пользователь работает с критическими данными — жёстко. Если он учится или общается — мягко.

Развилка 2. Когда говорить «не знаю»

Самая сложная инструкция для модели — «если в источниках недостаточно информации, скажи об этом прямо». Сложная потому, что модель по природе склонна отвечать. Признать «не знаю» — это против её обученной полезности.

Что помогает:

  • Дать конкретные формулировки: «скажи «я не нашёл этой информации в доступных источниках»» — точная фраза.
  • Дать few-shot пример того, как модель должна признаваться в неполноте.
  • Программно проверять: если в ответе нет ни одной цитаты, и при этом ответ — не «не знаю», — отклонять и перезапускать.

Это инвариант: пустой контекст или нерелевантный → ответ должен это явно отметить. Часть архитектуры, не «вежливое предложение».

Концептуальные грабли

Модель может ставить [1] где угодно, не глядя, действительно ли утверждение подкреплено фрагментом 1. Цитата без верификации — это «декорация», создающая иллюзию надёжности без реальной проверки.

Без программной верификации (хотя бы простейшего «существуют ли такие номера») цитаты не дают защиты, только видимость. Это особенно опасно: пользователь верит, потому что «вижу ссылки».

«Возьмём отдельную модель, она объективно оценит». В реальности судья страдает теми же проблемами, что основная модель: тоже может галлюцинировать «всё хорошо» там, где плохо, или наоборот.

Лучше — комбинировать программные проверки (точные, недвусмысленные) с LLM-судьёй (для тонких случаев). И тестировать судью на «золотом наборе» сложных случаев, как любой другой компонент системы.

Модель ответила верно, но не из источников, а «из своей памяти». Программная проверка покажет: «утверждение не подкреплено цитатой». Соблазн — закрыть глаза, потому что «фактически правильно».

Не закрывать. Сегодня правильное «из памяти», завтра — неправильное оттуда же. Если система не подкрепляет ответы источниками — это не RAG, это обычная LLM с прикрученным поиском, который ничего не гарантирует.

«Цитаты — это внутренний механизм, пользователю незачем». Это потеря возможности. Пользователь, который видит источники, может: (1) проверить сомнительные утверждения, (2) изучить тему глубже, (3) доверять системе осознанно — а не «слепо».

Современные хорошие RAG-продукты (Perplexity, ChatGPT с поиском, корпоративные ассистенты) показывают источники как первый класс UI. Это не «техническая деталь», это часть продукта.

Что ты теперь умеешь

Кластер 05 — это переход от «модель отвечает по своим знаниям» к «модель отвечает на твоих данных, с проверяемой привязкой». Пять уроков:

Сводная карта кластера 05

RAG: от поиска до проверяемого ответа

  • 21 RAG — это про две стены: знания и окно. Модель не знает твоих данных, контекст не резиновый. RAG — мост между ними.
  • 22 Эмбеддинги и чанкинг — фундамент. Текст → вектор. Чанки правильного размера. Метаданные с первого дня.
  • 23 Первый pipeline — простой, но дырявый. Запрос → вектор → топ-N → контекст. Работает на 30-50% — этого недостаточно.
  • 24 Воронка: recall → rerank → фильтры. Эмбеддинги ловят полноту, реранкер — точность, фильтры — структуру. Серьёзный RAG — двухступенчатый.
  • 25 Цитаты и верификация — последний слой. Модель обязана опираться на источники. Программная и LLM-проверка. Прозрачность для пользователя.

Куда дальше

Облачные API дороги, требуют интернета, отправляют данные наружу. Иногда это неприемлемо: личные документы, корпоративные секреты, оффлайн-сценарии. В Кластере 06 — про локальные LLM:

Кластер 06

Локальные LLM

Когда нужна приватность и автономность. Что такое квантизация, какие модели реально работают на твоей машине. Локальный RAG, локальный агент.

Кластер 07

Практические ассистенты

Финал курса. Собираем всё в реальные продукты: ассистент разработчика, ревью кода, поддержка, работа с файлами.

Если только один тейкэвей из кластера 05
RAG — это не «поиск + LLM». Это проектирование воронки: какие данные индексировать, как чанкировать, что фильтровать, как ранжировать, как заставить модель опираться на найденное и как проверить. Качество RAG-системы — это качество каждого из этих этапов, а не одного.

Практика

Эксперимент 01 · Поймай галлюцинацию в продакшен-RAG

Perplexity / ChatGPT с web search

Задай вопрос, в котором ты эксперт, специально сложный (с цифрами, именами, датами). Внимательно проверь ответ: каждое утверждение — есть ли оно реально в указанных источниках? В среднем в одном ответе на 4-5 утверждений 1-2 либо неверно процитированы, либо без источника вовсе. Это даст живое ощущение, насколько эта проблема актуальна.

Эксперимент 02 · Спроектируй верификацию для своего RAG

На бумаге

Возьми идею RAG-системы (своей или из примеров). Распиши: какие программные проверки ты бы запускал над финальным ответом? Минимум 3 проверки. Какие — через LLM-судью? В каких случаях ответ отклоняется и перезапускается? Это и есть «уровень надёжности» твоей системы, продуманный до запуска.

Эксперимент 03 · Промпт-практика

Заставь Claude / ChatGPT цитировать

В плейграунде составь промпт: даёшь модели 3-5 текстовых фрагментов на любую тему (можешь скопировать из википедии), нумеруешь их, задаёшь вопрос. В инструкции — обязательное цитирование в формате [N]. Прогон. Затем — без инструкции цитировать, тот же вопрос, те же фрагменты. Сравни ответы. Заметишь два эффекта: с цитированием ответ короче (меньше «воды»), и точнее привязан к источникам. Это даёт ощущение, насколько простая инструкция меняет поведение модели.