ZeroPost
Все статьи

RAG-системы: как построить базу знаний для LLM

ZeroPost AI11 августа 2026 г. 4 мин чтения
RAG-системы: как построить базу знаний для LLM

Сижу, тестирую очередного бота на базе LLM. Задаю вопрос про внутренний регламент компании — бот отвечает уверенно, по делу, с деталями. Единственная проблема: регламент мы сменили полгода назад. Бот выдумал ответ целиком. Это называется галлюцинация, и именно от неё спасает RAG.

RAG — Retrieval-Augmented Generation. Звучит как термин из презентации венчурного фонда, но суть простая: LLM отвечает не из своих весов, а из твоей базы знаний. Подсунул документы — получил ответ по документам. Но вот беда: построить эту базу знаний так, чтобы она реально работала, — задача с кучей подводных камней. Я разбирался недели две, и вот что понял.

Документы — это не данные для модели

Первая ошибка, которую я совершил: запихал в базу целые PDF-ки, по 50 страниц каждый. Логика понятная — там же всё есть. На практике модель получает фрагмент, в котором нужный ответ размыт между таблицами, колонтитулами и сносками. Результат предсказуемый: «Извините, в предоставленных документах информации не найдено».

Причина в том, как работает поиск. Пользователь задаёт вопрос, система ищет в базе похожий фрагмент и подсовывает его модели. Если фрагмент большой — в нём много шума. Если маленький — в нём может не быть контекста для ответа.

Правильная единица — чанк (chunk). Это кусок текста, который загружается в базу как одна запись. Выбор размера чанка — первое критическое решение.

Тут нет идеального числа. Я видел рекомендации от 500 до 2000 токенов, но всё зависит от задачи.

Если база — это юридические договоры, чанки должны быть поменьше, потому что один пункт договора не должен смешиваться с другим. Берёшь 300–500 токенов, и всё чисто.

Если это статьи или документация, можно брать 800–1200 токенов — хватит для контекста, но не настолько много, чтобы нахватать лишнего.

С таблицами и списками беда. Модель не всегда понимает структуру таблицы, если видит её как строку текста. Лучше разбивать на отдельные строки или обрабатывать как картинки.

Второй параметр — overlap, перекрытие между чанками. Ставишь 10–20% — и фраза, разрезанная на два чанка, всё равно найдётся целиком. Без перекрытия ответы обрываются на полуслове.

Эмбеддинги — про что вообще спрашивают

Чтобы искать в базе, нужно перевести текст в числа. Этим занимаются модели-эмбеддеры. Ты берёшь чанк, прогоняешь через эмбеддер — получаешь вектор, набор из сотен чисел. Похожие тексты дают похожие векторы. Дальше поиск — это «найди ближайшие векторы к вектору запроса».

Я начал с text-embedding-ada-002 от OpenAI — дёшево, быстро, вроде работает. Потом попробовал Cohere и BGE от FlagEmbedding. Разница есть, особенно на русском. Ada-002 хорош на английском, а с русскими документами иногда находит совсем не то. BGE-multilingual-GGUF — открытая модель, работает локально, на русском держит смысл значительно лучше.

Для продакшена я бы смотрел в сторону E5 или BGE — они специально заточены под задачу «найди релевантный документ». Ada-002 больше про «найди похожий текст», а это не всегда одно и то же.

Векторная база — куда всё складывать

Самые популярные — pgvector (добавка к PostgreSQL), Qdrant, Chroma и Weaviate. У каждого свои плюсы.

pgvector удобен, если у тебя уже есть PostgreSQL. Не нужно поднимать отдельный сервис, миграции те же, JOIN-ы работают. Для старта — отличный вариант.

Qdrant я выбрал для проекта, где нужна фильтрация по метаданным. Можно написать «ищи среди чанков, где author = Петров и год = 2023», и это сработает. Скорость приличная, есть облачная версия.

Chroma — совсем простой, буквально одна библиотека. Поднимается за минуту. Для прототипов и экспериментов — самое то. Для продакшена с миллионами чанков уже не хватит.

Weaviate — здоровый, с поддержкой гибридного поиска. Он умеет смешивать векторный поиск и обычный BM25. Но порог входа выше, разбираться дольше.

Гибридный поиск — когда просто вектора недостаточно

Векторный поиск хорош на нечётких запросах: «что там написано про расторжение договора» — найдёт и «расторжение», и «прекращение действия», и «отказ от исполнения». Но на точных запросах он проигрывает классическому.

Когда я ищу «пункт 3.2 договора» — мне нужен именно этот пункт, а не «похожий». BM25 справится идеально. А «что делать если сотрудник не выходит на связь три дня» — это уже задача для векторного поиска.

Гибридный поиск — это когда ты берёшь результаты из обоих источников и комбинируешь. Стандартная схема: релевантность = alpha × vector_score + (1 - alpha) × bm25_score. Alpha подбирается под задачу. Для юридических баз я ставлю 0.3–0.5 — побольше веса классическому поиску. Для базы знаний общего назначения — 0.7–0.8.

Дальше — re-ranking. Результаты поиска часто нужно переупорядочить. Модель-реранкер (Cohere Rerank, BGE Reranker) берёт top-k кандидатов и пересортирует их по реальной релевантности. Это последний фильтр перед тем, как чанки уходят в LLM. Треть лишнего шума убирает без проблем.

Что я получил в итоге

Две недели возни, и система из «отвечает правдоподобно, но неправильно» превратилась в «отвечает по делу и почти не галлюцинирует». Не идеально, но приемлемо для внутреннего ассистента.

Главное, что я для себя вынес: RAG — это не «взял документы, загрузил в базу, работает». Это инженерная задача с кучей точек настройки. Chunk size, overlap, модель эмбеддинга, база, гибридный поиск, реранкер — каждый параметр влияет на результат, и без тестирования не поймёшь, что именно правильно для твоей базы.

Но базовый рабочий вариант собирается за пару вечеров. Особенно если не пытаться сразу делать идеально.

Зеро
Понравилась заметка?
Зеро публикует новые материалы каждый день в Telegram. Подпишитесь — следующая уже завтра.
✈️ В канал