Как вы предотвращаете галлюцинации в production RAG системе?
Краткий тезис
Галлюцинации в RAG — это ответы, которые противоречат предоставленным документам или содержат вымышленные факты. Предотвращение строится на многоуровневой защите: pre-flight (проверка, что ретрив нашёл релевантные документы), post-flight (LLM сама оценивает свой ответ на faithfulness), пороговые значения (отказ отвечать при низкой уверенности) и цитирование источников (core technique — резко снижает галлюцинации). Дополнительно используются пользовательский фидбек и метрики для мониторинга.
1. Термин «галлюцинация» в контексте RAG
Галлюцинация (hallucination) — генерация LLM-ответа, который не подтверждается переданными документами (context) или противоречит фактам из них. В RAG галлюцинации делятся на два типа:
- Intrinsic — ответ противоречит документам, переданным в контекст (например, LLM пишет «температура плавления 100°C», а в документе — 150°C).
- Extrinsic — ответ содержит факты, не присутствующие в документах (даже если они верны в реальном мире, они не должны быть сгенерированы, если нет подтверждения в ретриве).
Причины галлюцинаций в RAG
- Ретрив не нашёл нужных документов (retrieval miss).
- В документе есть противоречивая информация.
- LLM «переобучилась» на своих знаниях и игнорирует контекст.
- Длинный контекст приводит к эффекту lost in the middle — модель забывает середину.
2. Многоуровневая архитектура предотвращения
Система защиты строится как pipeline с точками контроля перед генерацией и после неё.
| Уровень | Где происходит | Задача |
|---|---|---|
| Pre-flight | Перед передачей документов LLM | Проверить, что ретрив вернул хотя бы один релевантный документ |
| Во время генерации | В промпт-инжиниринге | Явно требовать цитирования, указывать инструкцию «не придумывай» |
| Post-flight | После получения ответа | LLM сама оценивает faithfulness своего ответа; дополнительно — отдельный классификатор |
| Порог уверенности | После пост-обработки | Если уверенность ниже threshold — вернуть «не знаю» |
| Обратная связь | После выдачи пользователю | Кнопка «Ответ неверен», логгирование, дообучение |
3. Pre-flight: проверка наличия релевантных документов
Перед тем как отправлять документы в LLM, система проверяет, есть ли в ретрив-результатах хотя бы один документ с достаточно высокой relevance score (например, cosine similarity > 0.7).
Реализация
def pre_flight(query: str, retrieved_docs: List[Document], threshold: float = 0.7) -> bool:
"""Возвращает False, если нет документов выше порога."""
for doc in retrieved_docs:
if doc.score >= threshold:
return True
return False
Если pre-flight возвращает False, можно:
- Переформулировать запрос и повторить ретрив.
- Ответить: «Извините, я не нашёл информации по вашему вопросу».
- Отправить запрос на ручную обработку (human-in-the-loop).
Почему это важно если нет релевантного контекста, LLM почти гарантированно начнёт галлюцинировать, опираясь на свои внутренние знания. Pre-flight — самый дешёвый способ отсечь такие случаи.
4. Post-flight: self-reflection и проверка faithfulness
После генерации ответа LLM сама (или отдельная модель) проверяет, насколько каждое утверждение (claim) в ответе подтверждается документами.
Self-reflection (Self-RAG): Включает в промпт дополнительные шаги:
- «Для каждого предложения ответа укажи, есть ли прямое подтверждение в контексте. Если нет — отметь как не подтверждённое».
- Или запускает второй вызов LLM с тем же контекстом и вопросом: «Верно ли, что ответ A полностью соответствует контексту? Ответь yes/no».
Faithfulness score вычисляется как доля подтверждённых утверждений. Инструмент RAGAS предоставляет готовую метрику faithfulness.
Пример кода для проверки через отдельный LLM-вызов:
def check_faithfulness(answer: str, context: str, llm) -> float:
prompt = f"""
Контекст: {context}
Ответ: {answer}
Для каждого утверждения в ответе определи, есть ли оно в контексте.
Верни JSON: {{"faithful": true/false, "unsupported_claims": [list]}}
"""
result = llm.invoke(prompt)
# разбор JSON
return result["faithful"] # 0 или 1 в простейшем случае
Если faithfulness < порога (например, 0.8), система может:
- Попросить LLM переписать ответ с опорой только на контекст.
- Вернуть «не уверен» или попросить пользователя уточнить.
5. Пороговые значения и отказ от ответа
Даже после post-flight может быть ситуация, когда LLM выдаёт ответ с низкой уверенностью. Используется confidence threshold.
Методы получения confidence
- Логарифмические вероятности (logprobs) первого токена LLM.
- Специальный промпт: «Оцени свою уверенность в ответе от 0 до 1».
- Агрегирование нескольких сэмплов (self-consistency).
Пример политики
| Confidence | Действие |
|---|---|
| ≥ 0.9 | Отвечаем с цитатами |
| 0.6 – 0.9 | Отвечаем, но добавляем дисклеймер «Возможно, информация неполная» |
| < 0.6 | «Я не уверен в ответе. Рекомендую обратиться к первоисточнику.» |
Отказ от ответа снижает риск галлюцинаций, но увеличивает количество неотвеченных запросов. Необходим баланс.
6. Цитирование источников (core technique)
Ключевой приём заставить LLM явно указывать в ответе, из какого документа взята каждая часть информации.
Реализация в промпте
Ты — ассистент, отвечающий строго по предоставленным документам.
Для каждого факта укажи идентификатор документа (источник [1], [2] и т.д.).
Не используй информацию, которой нет в документах.
LLM генерирует ответ, содержащий ссылки на источники. После генерации можно:
- Отфильтровать ответы, где цитаты отсутствуют.
- Проверить, что каждая цитата действительно соответствует тексту документа (через простой поиск подстроки или семантическое сравнение).
Почему это работает когда модель знает, что каждое утверждение будет проверено по источнику, она меньше склонна «выдумывать». Исследования (например, в статье «How to cite in RAG») показывают снижение галлюцинаций на 40–70%.
Пример формата ответа
Температура плавления алюминия — 660°C [1].
Плотность — 2.7 г/см³ [1, c. 15].
7. Пользовательский фидбек и human-in-the-loop
Production-система должна собирать обратную связь от пользователей для постоянного улучшения.
- Кнопки фидбека «Верно», «Неверно», «Не уверен».
- Логгирование сохранять запрос, контекст, ответ, оценку пользователя.
- Активное обучение если много «неверно» для определённого типа запросов — дообучать или корректировать ретрив.
Также можно внедрить human-in-the-loop для критических доменов (медицина, юриспруденция): если confidence < 0.8, ответ сначала проверяет человек.
8. Дополнительные техники
8.1 Reranking и фильтрация контекста
Перед отправкой в LLM отфильтровать документы с низким score, оставить только top-3–5. Использовать cross-encoder для reranking — он более точный, чем эмбеддинги.
8.2 Knowledge Graph и entity grounding
Связать извлечённые сущности с базой знаний (Wikidata, внутренний граф) — если сущность не найдена, ответ отбрасывается.
8.3 Adversarial validation
Создать набор тестовых запросов, на которые groud truth известен, и прогонять их перед релизом. Метрика — faithfulness и answer correctness.
8.4 Dynamic context reduction
Если контекст слишком большой, LLM может «забыть» часть — использовать релевантность и длину контекста, автоматически сжимать повторяющуюся информацию.
9. Инструменты и метрики для мониторинга
| Инструмент | Что даёт |
|---|---|
| RAGAS | Метрики faithfulness, answer relevance, context precision |
| DeepEval | Faithfulness, hallucination detection |
| Guardrails AI | Правила проверки на галлюцинации (например, наличие цитат) |
| LangChain Callbacks | Логирование каждого шага, возможность добавить custom check |
Пример дашборда мониторинга
- % запросов, где pre-flight отклонил.
- Средний faithfulness score.
- % галлюцинаций по пользовательскому фидбеку.
- Время response и задержки от проверок.
10. Компромиссы
| Приём | Плюсы | Минусы |
|---|---|---|
| Pre-flight | Дешево, отсекает очевидные miss | Может ложно отклонять при низком пороге |
| Post-flight | Высокая точность | Двойной вызов LLM → задержка + стоимость |
| Цитирование | Резко снижает intrinsic галлюцинации | Требует парсинга ответа, иногда ломает читаемость |
| Отказ при низком confidence | Безопасность | Снижает coverage (больше «не знаю») |
| Human-in-the-loop | Максимальная точность | Дорого, не real-time |
Идеальная система комбинирует все уровни с настраиваемыми порогами в зависимости от домена (финансы — высокий порог, чат-бот для погоды — низкий).
Пет-проект для закрепления
Задача Создать простой RAG-чат-бота на Streamlit, который предотвращает галлюцинации с помощью цитирования и self-reflection.
Инструменты LangChain, ChromaDB, OpenAI API (или локальная модель через Ollama), Streamlit.
Шаги:
- Загрузить небольшой набор документов (например, статьи Википедии).
- Реализовать pre-flight: проверять, есть ли документы с cosine similarity > 0.7.
- В промпт добавить требование: «Для каждого факта указывай номер источника [1], [2]…».
- После генерации написать функцию self-reflection: вызвать LLM ещё раз с вопросом «Соответствует ли ответ контексту? Ответь yes/no и объясни».
- Если self-reflection дал no — показать пользователю сообщение «Я не уверен в ответе».
- Добавить кнопки «Верно / Неверно» и логирование в CSV.
Ожидаемый результат Получится работающий прототип, где можно на тестовых вопросах увидеть, как система отказывается отвечать на неподтверждённые запросы и цитирует источники.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 5 | Метрики ретрива (влияют на pre-flight) |
| 7 | Компромисс скорость-точность при добавлении проверок |
| 10 | Self-reflection как основной метод post-flight |
| 13 | Качество чанков влияет на галлюцинации |
| 18 | Pre-flight и отказ от ответа |
Навигация
- Предыдущий: 95
- Следующий: 97
- Индекс: 00. Индекс разборов