RAG на практике: поиск по документам с LLM
RAG (Retrieval-Augmented Generation) — архитектурный паттерн, который решает одну из главных проблем LLM: они не знают о ваших внутренних документах. Вместо дорогостоящего fine-tuning мы учим модель искать нужную информацию в базе знаний прямо во время запроса.
Что такое RAG и зачем он нужен
Классический LLM отвечает только на основе знаний, заложенных при обучении. RAG добавляет этап retrieval: перед генерацией ответа система ищет релевантные фрагменты из вашей базы документов и передаёт их в контекст модели.
Схема работы:
- Пользователь задаёт вопрос
- Вопрос преобразуется в embedding (векторное представление)
- В векторной базе данных ищутся похожие фрагменты документов
- Найденные фрагменты + вопрос передаются в LLM
- LLM генерирует ответ на основе найденного контекста
Embeddings: превращаем текст в векторы
Embedding — числовой вектор, кодирующий семантический смысл текста. Похожие по смыслу тексты имеют близкие векторы в многомерном пространстве.
Популярные модели для русского языка:
intfloat/multilingual-e5-large— хороший баланс качества и скоростиdeepvk/USER-bge-m3— одна из лучших для русскогоtext-embedding-3-smallот OpenAI — через API
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('intfloat/multilingual-e5-large')
embeddings = model.encode(['Как настроить NFS сервер?'])
Векторные базы данных: Chroma vs Milvus
Chroma — простой старт, идеален для прототипов и небольших коллекций:
import chromadb
client = chromadb.PersistentClient(path='./chroma_db')
collection = client.get_or_create_collection('docs')
collection.add(
documents=['текст документа'],
embeddings=[[0.1, 0.2, ...]],
ids=['doc_001']
)
results = collection.query(query_embeddings=[query_vec], n_results=5)
Milvus — production-решение для больших объёмов (миллионы векторов), поддерживает горизонтальное масштабирование, несколько индексов (HNSW, IVF_FLAT), фильтрацию по метаданным.
Chunking: стратегии разбивки документов
От качества разбивки документов зависит 50% качества всей RAG-системы:
- Fixed-size chunking: разбивка по N символов с overlap. Просто, но теряет смысловые границы
- Sentence splitting: разбивка по предложениям. Лучше сохраняет контекст
- Recursive character splitting: иерархическое разбивание по `
→
→.` — рекомендуется как baseline
- Semantic chunking: разбивка по изменению embedding — дорого, но качественно
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=64,
separators=['
', '
', '. ', ' ']
)
chunks = splitter.split_text(document_text)
LangChain pipeline
Полный RAG pipeline на LangChain:
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import Ollama
vectorstore = Chroma(persist_directory='./chroma_db',
embedding_function=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={'k': 5})
llm = Ollama(model='llama3')
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=retriever,
return_source_documents=True
)
result = qa_chain('Как настроить Prometheus alerting?')
Оценка качества RAG
Метрики для оценки системы:
- Faithfulness — насколько ответ соответствует найденным документам (не галлюцинирует)
- Answer Relevancy — насколько ответ релевантен вопросу
- Context Recall — все ли нужные документы были найдены
Фреймворк RAGAS автоматизирует оценку:
pip install ragas
Деплой через FastAPI
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Query(BaseModel):
question: str
@app.post('/ask')
async def ask(query: Query):
result = qa_chain(query.question)
return {
'answer': result['result'],
'sources': [d.metadata for d in result['source_documents']]
}
RAG — не серебряная пуля, но при правильной настройке chunking и embedding-модели даёт впечатляющие результаты для корпоративного поиска по документам.
Попробовал USER-bge-m3 для русскоязычной базы знаний — качество заметно лучше, чем multilingual-e5-large, особенно на технических текстах. Правда, и памяти кушает больше.