Как вы уменьшаете галлюцинации в RAG?
Краткий тезис
Галлюцинации в RAG — это ситуация, когда LLM генерирует информацию, которой нет в предоставленном контексте (retrieved documents). Это главная проблема RAG в production, потому что пользователь теряет доверие к системе. Уменьшение галлюцинаций требует многоуровневого подхода: улучшение retrieval (чтобы контекст был релевантным), улучшение промпта (чтобы модель не выдумывала), постобработка (проверка фактов) и настройка генерации (низкая температура). Комбинация этих методов может снизить галлюцинации с 20-30% до 2-5%.
Ключевая идея Галлюцинации — это не бинарная проблема (есть/нет), а спектр. Нужно измерять faithfulness и систематически уменьшать её.
1. Термин: Галлюцинация (Hallucination) в RAG
Что это LLM генерирует утверждение, которое не следует из контекста (retrieved documents), даже если это утверждение само по себе правдиво.
Пример галлюцинации
Контекст (retrieved): "Компания была основана в 2010 году. Штаб-квартира находится в Москве."
Вопрос: "Где находится штаб-квартира компании?"
Ответ LLM: "Штаб-квартира компании находится в Москве, где работает 5000 сотрудников."
^^^^^^^^^^^^^^^^^^^^^^^^
ГАЛЛЮЦИНАЦИЯ (нет в контексте)
Термин «Faithfulness» Метрика, измеряющая долю утверждений в ответе, которые можно подтвердить из контекста. (см. вопрос 16)
Почему LLM галлюцинируют в RAG
| Причина | Объяснение |
|---|---|
| Обучение на интернете | LLM обучена отвечать всегда (даже если не знает) |
| Противоречивый контекст | Контекст содержит и релевантную, и нерелевантную информацию |
| Плохой промпт | Инструкция не запрещает галлюцинации явно |
| Высокая температура | Больше случайности → больше галлюцинаций |
| Слишком сложный вопрос | LLM пытается "додумать" ответ |
2. Уровень 1: Retrieval-level (улучшение качества контекста)
2.1 Увеличить top-k и улучшить retrieval
Идея Чем больше релевантного контекста видит LLM, тем меньше нужно додумывать.
До (плохо
retrieved_chunks = vector_search(query, top_k=3) # слишком мало
После (лучше
retrieved_chunks = vector_search(query, top_k=10) # больше контекста
# + reranking (cross-encoder) для отбора лучших
Цифра Увеличение top-k с 3 до 10 может снизить галлюцинации на 30-50%, но увеличивает латенси и стоимость.
Ограничение Слишком большой top-k (>20) может добавить шума, что увеличивает галлюцинации (модель путается в противоречиях).
2.2 Обрезать нерелевантные чанки через threshold
Идея Не передавать в LLM чанки с низкой релевантностью (score ниже порога).
results = vector_search(query, top_k=20)
threshold = 0.7 # cosine similarity
# Оставляем только релевантные чанки
filtered_chunks = [r for r in results if r.score > threshold]
if not filtered_chunks:
# Если нет релевантных чанков — отказ от ответа
return "Информация не найдена в документах."
Термин «Dynamic threshold» Порог может меняться в зависимости от запроса (например, для коротких запросов порог ниже).
Цифра Threshold 0.7 отсекает 30-50% чанков с низкой релевантностью, снижая галлюцинации на 20-30%.
2.3 Гибридный поиск (vector + BM25)
Идея Некоторые галлюцинации возникают из-за того, что retrieval не нашёл точные термины. Гибридный поиск улучшает recall.
- Что такое гибридный поиск и когда он нужен|6. Что такое гибридный поиск и когда он нужен|6. Что такое гибридный поиск и когда он нужен|6|См. вопрос 6 для деталей.
3. Уровень 2: Prompt-level (инструкции модели)
3.1 Явный запрет галлюцинаций
Плохой промпт (провоцирует галлюцинации
Ответь на вопрос на основе контекста.
Хороший промпт (явный запрет
Ты — ассистент, который отвечает ТОЛЬКО на основе предоставленного контекста.
ПРАВИЛА:
1. Если информация есть в контексте — ответь, используя её.
2. Если информации НЕТ в контексте — НЕ ВЫДУМЫВАЙ. Скажи: "Информация не найдена".
3. Не добавляй свои знания из обучения.
4. Если контекст противоречив — укажи на это.
Контекст: {context}
Вопрос: {question}
Ответ:
Термин «System prompt» Начальная инструкция, которая задаёт поведение модели на всю сессию.
3.2 Запрашивать цитаты (citation)
Идея Принуждать LLM указывать источник каждого факта. Если модель не может найти цитату — она не должна писать этот факт.
Промпт с цитатами
Для каждого утверждения в ответе укажи источник в формате [doc:N], где N — номер документа из контекста.
Пример:
Контекст:
[doc:1] iPhone 15 стоит от 799 долларов.
[doc:2] iPhone 15 имеет 48-МП камеру.
Вопрос: "Сколько стоит iPhone 15 и какая у него камера?"
Ответ: iPhone 15 стоит от 799 долларов [doc:1] и имеет 48-МП камеру [doc:2].
Постобработка Проверяем, что каждая цитата действительно существует в контексте. Если citation указывает на несуществующий документ — это галлюцинация.
Цифра Цитаты снижают галлюцинации на 40-60% (модель реже выдумывает, если нужно предоставить источник).
3.3 Few-shot примеры (примеры правильных ответов)
Идея Показать модели примеры ответов, где она НЕ галлюцинировала.
Промпт с few-shot
Пример 1:
Контекст: "Столица Франции — Париж."
Вопрос: "Какая столица у Франции?"
Ответ: "Столица Франции — Париж." (правильно, только из контекста)
Пример 2:
Контекст: "Собаки — домашние животные."
Вопрос: "Какой любимый цвет у собак?"
Ответ: "Информация не найдена в контексте." (отказ от ответа)
Теперь твоя очередь...
Почему работает Модель учится на примерах — видит, что в случае отсутствия информации нужно отказываться, а не выдумывать.
4. Уровень 3: Generation-level (настройки генерации)
4.1 Self-reflection (модель проверяет себя)
Идея После генерации ответа, модель проверяет, можно ли подтвердить каждое утверждение из контекста.
Pipeline
# Шаг 1: обычная генерация
response = llm.generate(query, context)
# Шаг 2: self-reflection — модель проверяет свой ответ
reflection_prompt = f"""
Проверь, можно ли подтвердить каждое утверждение из ответа, используя контекст.
Контекст: {context}
Ответ: {response}
Выведи для каждого утверждения:
- "VERIFIED" если подтверждается
- "NOT_VERIFIED" если нет
Если есть NOT_VERIFIED, исправь ответ, убрав эти утверждения.
"""
refined_response = llm.generate(reflection_prompt)
# Шаг 3: возвращаем исправленный ответ
return refined_response
Термин «Self-reflection» (саморефлексия Модель анализирует свой собственный вывод и исправляет ошибки.
Цифра Self-reflection снижает галлюцинации на 30-50%, но удваивает количество LLM-вызовов (дороже, медленнее).
4.2 Контрастные промпты (contrastive prompting)
Идея Попросить модель сгенерировать два ответа: один с контекстом, другой без, и сравнить.
# Ответ с контекстом
with_context = llm.generate(query, context=real_context)
# Ответ без контекста (только знания модели)
without_context = llm.generate(query, context="")
# Если ответы сильно различаются — возможно, модель галлюцинирует
# Или: оставляем только те факты, которые есть в with_context, но НЕТ в without_context
Термин «Contrastive decoding» Сравнение распределений вероятностей с контекстом и без для выявления галлюцинаций.
4.3 Низкая температура (temperature)
Идея Галлюцинации возникают чаще при высокой температуре (больше случайности). Для фактологических RAG-задач лучше низкая температура.
| Temperature | Эффект | Риск галлюцинаций |
|---|---|---|
| 0.0 (greedy) | Детерминированный ответ, один и тот же на один запрос | Низкий |
| 0.1-0.3 | Мало случайности, подходит для фактов | Низкий-средний |
| 0.5-0.7 | Баланс, для креативных задач | Средний |
| 0.8-1.0 | Высокая случайность, для креатива | Высокий |
Рекомендация для RAG temperature = 0.1 или 0.2.
Код
response = llm.generate(query, context, temperature=0.1)
5. Уровень 4: Post-processing (проверка после генерации)
5.1 Проверка именованных сущностей (NER)
Идея Извлечь из ответа все именованные сущности (имена, даты, числа, места) и проверить, есть ли они в контексте.
Термин «NER» (Named Entity Recognition Модель, выделяющая сущности: PERSON (Иван), DATE (2025), LOCATION (Москва), NUMBER (5000).
import spacy
nlp = spacy.load("ru_core_news_sm")
def check_entities(response, context):
response_entities = [ent.text for ent in nlp(response).ents]
context_entities = [ent.text for ent in nlp(context).ents]
hallucinated_entities = []
for entity in response_entities:
if entity not in context_entities:
hallucinated_entities.append(entity)
if hallucinated_entities:
# Удаляем или помечаем галлюцинации
return remove_entities(response, hallucinated_entities)
return response
Пример:
Контекст: "Компания основана в 2010 году."
Ответ: "Компания основана в 2010 году Иваном Ивановым."
NER из ответа: ["2010", "Иван Иванов"]
NER из контекста: ["2010"]
→ "Иван Иванов" — галлюцинация, удаляем
5.2 Проверка чисел и дат
Идея Проверить, что числа и даты в ответе присутствуют в контексте.
import re
def check_numbers(response, context):
# Извлекаем все числа из ответа
response_numbers = re.findall(r'\d+', response)
context_numbers = re.findall(r'\d+', context)
for num in response_numbers:
if num not in context_numbers:
# Число-галлюцинация
return f"Контекст не содержит число {num}. Ответ не подтверждён."
return response
6. Полный пайплайн (рекомендация для production)
def rag_with_hallucination_prevention(query):
# ===== Уровень 1: Retrieval =====
chunks = vector_search(query, top_k=15)
# Фильтрация по threshold
relevant_chunks = [c for c in chunks if c.score > 0.65]
if not relevant_chunks:
return "Информация не найдена в документах." # отказ
# ===== Уровень 2: Prompt =====
prompt = f"""
Ты — ассистент. Отвечай ТОЛЬКО на основе контекста.
Если информации нет — скажи "Информация не найдена".
Для каждого факта указывай источник [doc:N].
Контекст: {relevant_chunks}
Вопрос: {query}
Ответ:
"""
# ===== Уровень 3: Generation =====
response = llm.generate(prompt, temperature=0.1)
# ===== Уровень 4: Post-processing =====
# Проверка цитат
if not check_citations(response, relevant_chunks):
return "Ответ содержит неподтверждённые утверждения. Пожалуйста, проверьте источники."
# Проверка NER
response = check_entities(response, relevant_chunks)
return response
7. Сравнение методов (эффективность)
| Метод | Снижение галлюцинаций | Дополнительная латенси | Дополнительная стоимость | Сложность |
|---|---|---|---|---|
| Увеличение top-k | 20-30% | +20% | +20% | Низкая |
| Threshold фильтрация | 20-30% | 0% | 0% (меньше токенов) | Низкая |
| Хороший промпт | 30-50% | 0% | 0% | Низкая |
| Цитаты | 40-60% | 0% | +10-20% (больше токенов) | Низкая |
| Few-shot | 20-40% | 0% | +20-50% (токены) | Низкая |
| Self-reflection | 30-50% | +100% | +100% | Средняя |
| Низкая temperature | 10-20% | 0% | 0% | Низкая |
| NER проверка | 10-30% | +5% | 0% | Средняя |
| Комбинация всех | 70-90% | +50-100% | +50-100% | Высокая |
Рекомендация Начать с простых методов (промпт, threshold, низкая temperature) — они дают большой эффект за малые деньги. Self-reflection и NER подключать, если галлюцинации остаются критичной проблемой.
8. Пет-проект для закрепления
Задача Измерить частоту галлюцинаций в вашем RAG и уменьшить их с помощью 3 методов.
Инструменты Python, RAGAS (faithfulness), любой LLM
Шаги
- Замерить baseline faithfulness на 50-100 примерах (через RAGAS)
- Внедрить 3 метода (в порядке увеличения сложности):
-
- Как бы вы спроектировали RAG-систему для 10 000 документов с разной структурой|1. Как бы вы спроектировали RAG-систему для 10 000 документов с разной структурой|1. Как бы вы спроектировали RAG-систему для 10 000 документов с разной структурой|1|Метод 1 Улучшить промпт (явный запрет + цитаты)
- 2 Как вы решаете проблему lost in the middle при работе с длинными контекстами|2 Как вы решаете проблему lost in the middle при работе с длинными контекстами|2 Как вы решаете проблему lost in the middle при работе с длинными контекстами|2|Метод 2 Добавить threshold фильтрацию (score > 0.65)
- 3 Какие стратегии chunking'а вы знаете и когда какую применяете|3 Какие стратегии chunking'а вы знаете и когда какую применяете|3 Какие стратегии chunking'а вы знаете и когда какую применяете|3|Метод 3 Добавить self-reflection (один дополнительный LLM-вызов)
-
- После каждого метода замерить faithfulness снова
- Посчитать, насколько улучшилась метрика
- Замерить дополнительную латенси и стоимость
Ожидаемый результат Вы увидите, как faithfulness растёт от 0.6-0.7 до 0.85-0.95, и поймёте, какой метод даёт лучший trade-off для вашего случая.
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 5 | Оценка retrieval (влияет на контекст) |
| 6 | Гибридный поиск (улучшает retrieval) |
| 8 | Обработка запросов без ответа (отказ вместо галлюцинации) |
| 16 | Faithfulness — метрика галлюцинаций |
| 67 | Prompt injection (галлюцинации от вредоносных запросов) |
| 96 | Предотвращение галлюцинаций в production |
17. Как вы уменьшаете галлюцинации в RAG|17. Как вы уменьшаете галлюцинации в RAG|17. Как вы уменьшаете галлюцинации в RAG|17 полностью разобран. Переходим к вопросу 18, когда будете готовы|Вопрос 17 полностью разобран. Переходим к вопросу 18, когда будете готовы]]
Навигация
- Предыдущий: 16
- Следующий: 18
- Индекс: 00. Индекс разборов