Проблема: почему обычные LLM "галлюцинируют" и как это исправить
Вы наверняка сталкивались с ситуацией, когда ChatGPT или другая языковая модель уверенно отвечает на вопрос, но при этом выдумывает факты. Это явление называется "галлюцинацией" (hallucination) и является фундаментальной проблемой современных LLM. Модели обучены на огромных объемах данных, но у них нет доступа к актуальной или специфической информации.
Ключевая проблема: LLM не имеют доступа к вашим внутренним документам, актуальным данным или специфическим знаниям вашей компании. Они работают только на том, что было в обучающей выборке.
Именно эту проблему решает Retrieval-Augmented Generation (RAG) — архитектурный подход, который дополняет языковые модели внешними источниками знаний. Вместо того чтобы полагаться только на внутренние знания модели, RAG:
- Извлекает релевантные документы из вашей базы знаний
- Добавляет их в контекст запроса
- Генерирует ответ на основе как внутренних знаний модели, так и внешних источников
Решение: архитектура RAG — как это работает под капотом
RAG система состоит из трех основных компонентов, которые мы сегодня реализуем:
| Компонент | Назначение | Что используем |
|---|---|---|
| Векторная база | Хранение и поиск документов по семантическому сходству | FAISS (Facebook AI Similarity Search) |
| Эмбеддинг-модель | Преобразование текста в числовые векторы | Sentence Transformers |
| Языковая модель | Генерация ответов на основе контекста | OpenAI GPT или локальная модель |
Пошаговый план: создаем RAG систему за 15 минут
Теперь перейдем к практической части. Мы создадим полноценную RAG систему, которая сможет отвечать на вопросы по вашим документам.
1 Подготовка окружения и установка зависимостей
Создайте новую директорию и виртуальное окружение:
mkdir rag-system && cd rag-system
python -m venv venv
source venv/bin/activate # На Windows: venv\Scripts\activate
Установите необходимые библиотеки:
pip install langchain langchain-openai faiss-cpu sentence-transformers python-dotenv
Создайте файл .env для хранения API ключей:
echo "OPENAI_API_KEY=your_api_key_here" > .env
2 Подготовка документов и создание векторной базы
Создайте файл documents.txt с вашими документами. Для примера:
Компания TechCorp была основана в 2015 году.
Основной продукт — облачная платформа для аналитики данных.
Штаб-квартира находится в Сан-Франциско.
Количество сотрудников: 250 человек.
Годовой доход: 50 миллионов долларов.
Теперь создайте скрипт create_vectorstore.py:
import os
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from dotenv import load_dotenv
load_dotenv()
# Загружаем документы
def create_vector_store():
# Загрузка документов
loader = TextLoader("documents.txt", encoding="utf-8")
documents = loader.load()
# Разделение на чанки
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
)
chunks = text_splitter.split_documents(documents)
# Создание эмбеддингов и векторного хранилища
embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
vectorstore = FAISS.from_documents(chunks, embeddings)
vectorstore.save_local("faiss_index")
print(f"Создано {len(chunks)} чанков")
print("Векторное хранилище сохранено в 'faiss_index'")
return vectorstore
if __name__ == "__main__":
create_vector_store()
Важно: Размер чанка (chunk_size) — критический параметр. Слишком маленькие чанки теряют контекст, слишком большие — содержат лишнюю информацию. Начните с 500-1000 символов и экспериментируйте.
3 Создание RAG цепи с LangChain
Создайте основной файл rag_system.py:
import os
from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from dotenv import load_dotenv
load_dotenv()
class RAGSystem:
def __init__(self):
# Загрузка векторного хранилища
self.embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2"
)
self.vectorstore = FAISS.load_local(
"faiss_index",
self.embeddings,
allow_dangerous_deserialization=True
)
# Инициализация LLM
self.llm = ChatOpenAI(
model="gpt-3.5-turbo",
temperature=0.3, # Низкая температура для более точных ответов
api_key=os.getenv("OPENAI_API_KEY")
)
# Создание кастомного промпта
prompt_template = """Используй следующие фрагменты контекста, чтобы ответить на вопрос.
Если ты не знаешь ответа, просто скажи, что не знаешь, не пытайся выдумывать.
Контекст: {context}
Вопрос: {question}
Точный и подробный ответ:"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# Создание RAG цепи
self.qa_chain = RetrievalQA.from_chain_type(
llm=self.llm,
chain_type="stuff",
retriever=self.vectorstore.as_retriever(
search_kwargs={"k": 3} # Берем 3 наиболее релевантных документа
),
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True
)
def query(self, question: str):
"""Запрос к RAG системе"""
result = self.qa_chain.invoke({"query": question})
return {
"answer": result["result"],
"sources": result["source_documents"]
}
# Пример использования
if __name__ == "__main__":
rag = RAGSystem()
# Тестовые вопросы
questions = [
"Когда была основана компания TechCorp?",
"Сколько сотрудников в компании?",
"Какой основной продукт у компании?"
]
for question in questions:
print(f"\nВопрос: {question}")
response = rag.query(question)
print(f"Ответ: {response['answer']}")
print(f"Источники: {len(response['sources'])} документов")
4 Запуск и тестирование системы
Запустите систему командой:
python rag_system.py
Вы должны увидеть ответы на ваши вопросы, основанные на документах из documents.txt.
Нюансы и возможные ошибки
1. Качество эмбеддингов
Модель all-MiniLM-L6-v2 — хороший баланс между скоростью и качеством. Для production:
- Для английского:
all-mpnet-base-v2(лучшее качество) - Для русского:
sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2 - Для скорости:
all-MiniLM-L6-v2(самый быстрый)
2. Стратегии поиска
| Стратегия | Описание | Когда использовать |
|---|---|---|
| Similarity Search | Поиск по косинусному сходству | Большинство случаев |
| MMR (Maximal Marginal Relevance) | Баланс релевантности и разнообразия | Когда нужны разные аспекты темы |
| Self-Query | Поиск с фильтрацией по метаданным | Структурированные данные |
3. Распространенные ошибки
Ошибка 1: Слишком большие чанки. Решение: уменьшите chunk_size до 500-1000 символов.
Ошибка 2: LLM игнорирует контекст. Решение: улучшите промпт, явно указав "Используй только предоставленный контекст".
Ошибка 3: Медленный поиск. Решение: используйте FAISS с индексом IVF для больших баз данных.
Продвинутые улучшения
После того как базовая система работает, рассмотрите эти улучшения:
1. Добавление метаданных
# При создании чанков добавляйте метаданные
from langchain.schema import Document
chunks_with_metadata = []
for i, chunk in enumerate(chunks):
doc = Document(
page_content=chunk.page_content,
metadata={
"source": "documents.txt",
"chunk_id": i,
"timestamp": "2024-01-01"
}
)
chunks_with_metadata.append(doc)
2. Переписывание запросов (Query Rewriting)
# Улучшение запросов перед поиском
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
rewrite_prompt = PromptTemplate(
input_variables=["question"],
template="""Перепиши этот вопрос для лучшего поиска в базе знаний:
Оригинальный вопрос: {question}
Переписанный вопрос:"""
)
rewrite_chain = LLMChain(llm=llm, prompt=rewrite_prompt)
improved_question = rewrite_chain.run(question=original_question)
3. Reranking результатов
Используйте модель для переранжирования найденных документов:
from sentence_transformers import CrossEncoder
# Инициализация модели для reranking
reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')
# Ранжирование пар запрос-документ
pairs = [[question, doc.page_content] for doc in retrieved_docs]
scores = reranker.predict(pairs)
# Сортировка по убыванию релевантности
ranked_docs = [doc for _, doc in sorted(zip(scores, retrieved_docs), reverse=True)]
FAQ: ответы на частые вопросы
Можно ли использовать локальную LLM вместо OpenAI?
Да, абсолютно. Вместо ChatOpenAI можно использовать:
from langchain_community.llms import LlamaCpp
llm = LlamaCpp(
model_path="./models/llama-7b.gguf",
temperature=0.3,
n_ctx=2048,
)
Как обрабатывать PDF, Word и другие форматы?
LangChain поддерживает множество загрузчиков:
from langchain_community.document_loaders import (
PyPDFLoader,
Docx2txtLoader,
UnstructuredHTMLLoader,
CSVLoader
)
# Для PDF
loader = PyPDFLoader("document.pdf")
# Для Word
loader = Docx2txtLoader("document.docx")
Как масштабировать на миллионы документов?
Для больших объемов данных:
- Используйте Pinecone или Weaviate вместо FAISS
- Реализуйте пагинацию и батчинг при индексации
- Используйте GPU для вычисления эмбеддингов
- Рассмотрите иерархическую индексацию
Заключение
Вы только что создали полноценную RAG систему с нуля! Теперь у вас есть основа, которую можно расширять и улучшать. Ключевые моменты для запоминания:
- Качество чанков важнее всего — правильно разделяйте документы
- Промпт-инжиниринг критически важен — явно указывайте LLM использовать контекст
- Метаданные упрощают фильтрацию и отладку
- Тестируйте с реальными вопросами пользователей
RAG — это не серебряная пуля, но мощный инструмент для создания интеллектуальных систем, которые действительно "знают" ваши данные. Начните с простой реализации, как в этом гайде, и постепенно добавляйте сложность по мере необходимости.
Весь код из этой статьи доступен для копирования и запуска. Экспериментируйте, меняйте параметры, адаптируйте под свои нужды. Удачи в создании умных систем!