Техническая ветка для разработчиков Собрать руками
RAG: как давать модели знания компании
Почему LLM не знает про вашу CRM? Как подключить внутренние документы без переобучения? Рассказываем, как собрать RAG-пайплайн, который работает в продакшене — с кодом, ловушками и evals.
Что такое RAG и зачем он нужен
RAG (Retrieval-Augmented Generation) — архитектура, при которой LLM получает контекст не из обучения, а из внешнего источника в момент генерации ответа. Это позволяет:
- Использовать актуальные, приватные данные (базы знаний, документы, CRM-выгрузки).
- Избежать переобучения и снижения скорости работы модели.
- Контролировать источник ответа (важно для compliance и аудита).
Без RAG LLM «обобщает» — он не знает, что у вас в базе, пока вы не дадите ему этот контекст явно.
Пайплайн RAG: 4 этапа
flowchart TD
A[Источник данных: PDF, DB, Notion] --> B[Preprocessing]
B --> C[Векторизация (embedding)]
C --> D[Векторная БД (Chroma, Qdrant)]
D --> E[Поиск по релевантности]
E --> F[Контекст + запрос → LLM]
F --> G[Ответ с источниками]
Ключевая идея: разделяй и властвуй — обработка данных отдельно от генерации.
Минимальный RAG-пайплайн (Python + LangChain)
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# 1. Загрузка и разбиение
loader = TextLoader("docs/internal_knowledge.txt")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 2. Векторизация и хранение
vectorstore = Chroma.from_documents(
chunks,
embedding=OpenAIEmbeddings(),
persist_directory="./chroma_db"
)
# 3. Поиск + генерация
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
prompt = ChatPromptTemplate.from_template("""
Ответь на вопрос, используя только контекст.
Вопрос: {question}
Контекст: {context}
""")
llm = ChatOpenAI(model="gpt-4o-mini")
# Пайплайн
def rag_chain(question):
docs = retriever.invoke(question)
context = "\n\n".join([d.page_content for d in docs])
return llm.invoke(prompt.format(question=question, context=context))
print(rag_chain("Как оформлять отпуск по уходу за ребёнком?").content)
В продакшене замените `TextLoader` на реальные источники (PDF, API, базы), добавьте пост-обработку и evals.
Типичные ошибки (и как их избежать)
- Слишком крупные чанки → модель теряет контекст. Решение: чанки 300–600 токенов, с перекрытием 10–20%.
- Плохие эмбеддинги → поиск возвращает «похожее, но не то». Решение: использовать специализированные модели (e.g., `bge-small-en-v1.5` для русского — `cointegrated/rubert-tiny2`).
- Нет фильтрации шума → модель генерирует на основе устаревших/ложных данных. Решение: добавить пост-фильтрацию по релевантности (BM25, косинусная близость + метрики).
- Игнорирование метаданных → невозможно отследить источник. Решение: хранить `source`, `page`, `updated_at` и передавать в промпт.
Как проверить качество RAG: простой eval
Создайте тестовый набор из 10–20 вопросов с эталонными ответами и источниками. Для каждого вопроса:
- Получите ответ от RAG.
- Проверьте, есть ли источник в топ-3 релевантных чанках (precision@3).
- Оцените ответ по шкале: 0–1 («да/нет» по точности и полноте).
# Пример метрики
def eval_rag(question, expected_answer, expected_sources):
response = rag_chain(question)
retrieved = retriever.invoke(question)
sources_found = any(src in [d.metadata.get('source') for d in retrieved]
for src in expected_sources)
# Простая проверка: есть ли ключевые слова из эталона
has_keywords = all(kw.lower() in response.content.lower()
for kw in ["отпуск", "ребёнок", "закон"])
return int(sources_found and has_keywords)
# Результат: 0.8 — 80% правильных ответов
Цель: >0.7 по precision@3 и >0.6 по factual accuracy.
Чеклист перед запуском в продакшен
- [ ] Данные нормализованы (кодировка, HTML-очистка, структурирование).
- [ ] Чанки с перекрытием и метаданными (источник, дата, тип).
- [ ] Эмбеддинги протестированы на релевантность (визуализация через UMAP).
- [ ] Векторная БД с индексом (HNSW/IVF) и таймаутом поиска.
- [ ] Логирование запросов + ретраи при таймаутах.
- [ ] Промпт с инструкцией «если в контексте нет ответа — скажи об этом».
- [ ] Метрики: latency (p95 < 1.5s), precision@3, user satisfaction (A/B).
Следующий шаг
После базового RAG — переходите к:
- Агенты с tool-calling — RAG как инструмент агента.
- Тестирование AI-агента — как оценивать RAG в составе агентной системы.
- От промпта к production — что ещё нужно добавить (logging, safety, guardrails).
Не забудьте: RAG — это не «включил и забыл». Это цикл: мониторинг → аудит → улучшение чанков и эмбеддингов.