Финал кластера RAG. У нас отличный поиск, точные результаты, чистый контекст. И всё равно модель иногда отвечает «не из источников». Сегодня — про последний инженерный слой: как заставить модель опираться только на найденное и как это проверять.
Даже идеально найденный контекст не гарантирует, что модель будет на него опираться. Она может смешать его со своими «знаниями из обучения», досочинить детали, перепутать источники. Лекарство — цитирование: модель обязана при каждом утверждении указывать, из какого чанка оно взято. Это меняет три вещи: модель структурно вынуждена опираться на источники, пользователь видит проверяемые ссылки, твоя система может программно проверить, что ответ построен на найденном.
После Дней 22-24 у тебя есть отличный поиск: документы хорошо разбиты, эмбеддинги ловят смысл, реранкер выдаёт точно подходящие чанки. В контексте модели — те самые 5 фрагментов, в которых лежит ответ на вопрос.
Кажется, дело сделано: модель прочитает, ответит. На практике — нет. Вот что реально может случиться:
Это всё галлюцинации в RAG-контексте. Они отличаются от обычных галлюцинаций (когда модель просто выдумывает) тем, что правильный ответ был у неё под носом, но она его не использовала или использовала неправильно.
Контекст модели — это предложение, не обязательство. Модель не «обязана» использовать найденное; она просто видит его наряду со своими внутренними знаниями. Сделать привязку обязательной — отдельная инженерная задача.
В обычной генерации (без RAG) пользователь понимает, что модель отвечает «по памяти», и относится к ответам с осторожностью. В RAG-системе — наоборот. Когда продукт говорит «вот мои источники», пользователь доверяет ответу больше, чем «голой LLM». Если в этом доверенном режиме модель тихонько досочиняет — последствия серьёзнее.
Это и есть главная этическая и продуктовая проблема RAG: пользователь думает, что говорит с базой знаний, а на самом деле — с моделью, которая иногда смешивает базу со своим.
Главный инструмент, который заставляет модель «оставаться в источниках» — требование цитировать. Каждое фактическое утверждение в ответе должно сопровождаться ссылкой на конкретный чанк, из которого оно взято.
В промпт сборки RAG-ответа (то, что собираешь после поиска) ты включаешь:
[1], [2], [3]... В тексте промпта они так и подаются: «Фрагмент [1]: ...текст...».В ответе ты получаешь что-то вроде:
Модель уверенно пишет ответ, смешивая найденное со своим. Проверить нельзя.
Каждое утверждение явно привязано к источнику. Если что-то «из общих знаний» — модель должна сказать «не нашёл».
Когда модель обязана при каждом утверждении ставить номер источника, у неё меняется поведение на уровне генерации токенов:
На практике это даёт заметный прирост точности: галлюцинации уменьшаются в 2-3 раза, ответы становятся более скромными, но честными.
Для системы с UI ещё лучше — попросить модель выдать цитаты в структурированном виде (через structured outputs из Дня 2):
{ "answer": "...", "citations": [{"text": "...", "source": "doc_id_1", "exact_quote": "..."}] }Это даёт тебе ещё больше контроля:
exact_quote правда есть в указанном чанке — найти подстроку.Цитирование — это самодисциплина модели. Иногда она всё равно ошибается: ставит цитату не к тому утверждению, придумывает цитаты на несуществующих фрагментах, цитирует точно — но утверждение в ответе не подтверждается этим текстом.
Поэтому над цитатами ставится программный или LLM-как-судья слой проверки. Это «второе мнение», которое смотрит на финальный ответ + контекст и оценивает: правда ли ответ построен на найденном?
Простой код. Каждая ссылка [N] в ответе — проверь, есть ли такой чанк во входе модели. Если ссылка [7], а чанков было только 5 — это галлюцинация.
Если модель цитирует «exact_quote» — твой код проверяет, что эта подстрока правда есть в указанном чанке. Поиск по тексту, никакой LLM не нужен.
Отдельный LLM-вызов: «вот ответ, вот источники. Каждое утверждение в ответе — подкреплено ли источниками? Отметь те, что нет.» Самый дорогой и точный.
Когда верификация показала проблемы — есть несколько стратегий:
Выбор стратегии зависит от цены ошибки. Для медицинского/юридического ассистента — только первый вариант. Для бытового чат-бота — третий или четвёртый.
Цитирование можно требовать с разной жёсткостью:
Подбирается под продукт. Если пользователь работает с критическими данными — жёстко. Если он учится или общается — мягко.
Самая сложная инструкция для модели — «если в источниках недостаточно информации, скажи об этом прямо». Сложная потому, что модель по природе склонна отвечать. Признать «не знаю» — это против её обученной полезности.
Что помогает:
Это инвариант: пустой контекст или нерелевантный → ответ должен это явно отметить. Часть архитектуры, не «вежливое предложение».
Модель может ставить [1] где угодно, не глядя, действительно ли утверждение подкреплено фрагментом 1. Цитата без верификации — это «декорация», создающая иллюзию надёжности без реальной проверки.
Без программной верификации (хотя бы простейшего «существуют ли такие номера») цитаты не дают защиты, только видимость. Это особенно опасно: пользователь верит, потому что «вижу ссылки».
«Возьмём отдельную модель, она объективно оценит». В реальности судья страдает теми же проблемами, что основная модель: тоже может галлюцинировать «всё хорошо» там, где плохо, или наоборот.
Лучше — комбинировать программные проверки (точные, недвусмысленные) с LLM-судьёй (для тонких случаев). И тестировать судью на «золотом наборе» сложных случаев, как любой другой компонент системы.
Модель ответила верно, но не из источников, а «из своей памяти». Программная проверка покажет: «утверждение не подкреплено цитатой». Соблазн — закрыть глаза, потому что «фактически правильно».
Не закрывать. Сегодня правильное «из памяти», завтра — неправильное оттуда же. Если система не подкрепляет ответы источниками — это не RAG, это обычная LLM с прикрученным поиском, который ничего не гарантирует.
«Цитаты — это внутренний механизм, пользователю незачем». Это потеря возможности. Пользователь, который видит источники, может: (1) проверить сомнительные утверждения, (2) изучить тему глубже, (3) доверять системе осознанно — а не «слепо».
Современные хорошие RAG-продукты (Perplexity, ChatGPT с поиском, корпоративные ассистенты) показывают источники как первый класс UI. Это не «техническая деталь», это часть продукта.
Кластер 05 — это переход от «модель отвечает по своим знаниям» к «модель отвечает на твоих данных, с проверяемой привязкой». Пять уроков:
Облачные API дороги, требуют интернета, отправляют данные наружу. Иногда это неприемлемо: личные документы, корпоративные секреты, оффлайн-сценарии. В Кластере 06 — про локальные LLM:
Когда нужна приватность и автономность. Что такое квантизация, какие модели реально работают на твоей машине. Локальный RAG, локальный агент.
Финал курса. Собираем всё в реальные продукты: ассистент разработчика, ревью кода, поддержка, работа с файлами.
Задай вопрос, в котором ты эксперт, специально сложный (с цифрами, именами, датами). Внимательно проверь ответ: каждое утверждение — есть ли оно реально в указанных источниках? В среднем в одном ответе на 4-5 утверждений 1-2 либо неверно процитированы, либо без источника вовсе. Это даст живое ощущение, насколько эта проблема актуальна.
Возьми идею RAG-системы (своей или из примеров). Распиши: какие программные проверки ты бы запускал над финальным ответом? Минимум 3 проверки. Какие — через LLM-судью? В каких случаях ответ отклоняется и перезапускается? Это и есть «уровень надёжности» твоей системы, продуманный до запуска.
В плейграунде составь промпт: даёшь модели 3-5 текстовых фрагментов на любую тему (можешь скопировать из википедии), нумеруешь их, задаёшь вопрос. В инструкции — обязательное цитирование в формате [N]. Прогон. Затем — без инструкции цитировать, тот же вопрос, те же фрагменты. Сравни ответы. Заметишь два эффекта: с цитированием ответ короче (меньше «воды»), и точнее привязан к источникам. Это даёт ощущение, насколько простая инструкция меняет поведение модели.