Вы собрали RAG-пайплайн. Залили документы. Скормили LLM. И в ответ получили кашу из случайных фактов, перемешанных с галлюцинациями. Знакомо? Если да — эта статья для вас. Я перестану вешать лапшу про «мощь RAG» и покажу три конкретных места, где ваш поиск превращается в шум. И дам инструменты, чтобы это исправить.
Причина №1. Чанкинг — вы режете документы, как салат ножом
Самая частая ошибка — взять фиксированный размер чанка (скажем, 512 токенов) и порезать все документы подряд. Результат: один чанк содержит середину предложения, а его логическое продолжение — в соседнем. LLM получает фрагменты без контекста и выдаёт бред.
Проблема усугубляется, когда хочется сэкономить на количестве векторов. Вы ставите чанк 1024 токена, но text-embedding-3-large от OpenAI (да, на май 2026 года это всё ещё одна из лучших моделей) работает оптимально с чанками до 512. Превышение — шум.
Как НЕ надо делать:
Разбить по 500 токенов без пересечения. Чанк A: «...компания объявила о запуске нового продукта...» Чанк B: «...который позволит сократить издержки на 30%». Без контекста чанк B бесполезен.
1 Исправление: семантический чанкинг + перекрытие
Перестаньте резать по токенам. Используйте разделители — абзацы, заголовки, маркдаун-секции. В идеале — семантический сплиттер, который соединяет предложения по эмбеддингам.
Проверенный подход (спасибо LangChain за RecursiveCharacterTextSplitter):
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=100,
separators=["\n\n", "\n", ".", " "]
)
docs = splitter.split_documents(raw_documents)Но ещё лучше — семантический чанкинг на основе потоков (Lillian 2025). Он строит граф связности между предложениями и режет только там, где резко меняется тема. Если документы сложные (технические спецификации, юридические контракты) — это must-have.
Как проверить что чанкинг работает? Возьмите 10 случайных чанков из вашей базы — и попробуйте понять, о чём каждый, без контекста. Если текст обрывается посередине идеи — переделывайте.
Причина №2. Эмбеддинги — игра в испорченный телефон
Вы используете text-embedding-ada-002 (OpenAI), BGE-small или E5-base. Они отлично работают на датасетах типа MS MARCO. Но ваш домен — медицинские протоколы или бухгалтерские проводки. И тут модель пасует: синонимы не распознаёт, термины путает.
Проблема подробна расписана в моей статье «Эмбеддинги — слепое пятно RAG». Но напомню кратко: косинусная близость между эмбеддингами не равна семантической близости. Пример: фраза «увеличение ликвидности» и «высокая ликвидность» могут быть далеко по расстоянию, хотя значения близки.
2 Исправление: domain-adapted fine-tuning или реранкинг
Если документы узкоспециализированные — fine-tune на своих данных. Благо, техники contrastive learning (SimCSE, Sentence-BERT) требуют всего пары сотен пар (запрос-документ).
Альтернатива — не париться с обучением, а поставить реранкер. BGE-Reranker-v2 или Cohere Rerank переоценивают top-20 чанков и выдают реально релевантные.
Вопрос: стоит ли тратить время на fine-tune? Если у вас < 10 тысяч запросов — реранкинг проще. Если больше — учите свою модель. Подробно про математику и ограничения — в моём материале «Математический потолок RAG».
Причина №3. Ретривал — когда похоже не значит релевантно
Вы нашли 10 чанков с максимальной косинусной близостью. Скормили их LLM. Ответ всё равно неверный. Почему? Потому что «похожесть» — не то же самое что «релевантность». Часто первый чанк — это общие фразы, а нужная информация — на десятом месте с меньшим скором.
Проблема усугубляется при росте базы. Если у вас 100 000 документов, шум увеличивается. Я разбирал это в статье «Когда RAG начинает врать».
3 Исправление: гибридный поиск + MMR + реранкинг
Только плотных эмбеддингов недостаточно. Добавьте BM25 (sparse retrieval). Они отлично находят точные вхождения терминов, которые эмбеддинги могут пропустить. Соединяйте результаты двух retrieval-веток через взвешенную сумму или блендер.
from langchain.retrievers import EnsembleRetriever
sparse_retriever = BM25Retriever.from_documents(docs)
dense_retriever = VectorStoreRetriever(vectorstore=vectorstore)
ensemble = EnsembleRetriever(
retrievers=[sparse_retriever, dense_retriever],
weights=[0.3, 0.7]
)Но даже гибрид может давать дубликаты по смыслу. Тут помогает MMR (Maximum Marginal Relevance) — он отбирает релевантные, но при этом разнообразные чанки. Это особенно важно, если LLM склонна повторять одну мысль несколько раз.
И наконец — реранкинг. Он стоит дороже по времени, но для production незаменим. Ставьте реранкер поверх ensemble-результатов, топ-5 будет золотом.
Кстати, о production-пайплайнах: у меня есть целый roadmap — «RAG 2026: От гибридного поиска до production». Там расписаны все этапы, включая мониторинг.
Бонус: как измерять качество поиска
Без метрик вы летите вслепую. Минимальный набор:
- Hit Rate — в скольких процентах запросов правильный ответ оказался среди top-k ретрива.
- MRR (Mean Reciprocal Rank) — насколько высоко ранжирован правильный ответ.
- NDCG — учитывает порядок и релевантность.
Сделайте датасет из 50–100 запросов с вручную размеченными правильными чанками. Прогоняйте после каждого изменения чанкера, модели или стратегии ретрива. Тогда вы увидите не «кажется, стало лучше», а цифры.
Последний совет
Не пытайтесь достичь 100% точности ретрива. LLM сама может отфильтровать мусор, если дать ей достаточный контекст и хороший промпт. Но если на входе — шум, LLM ничего не спасёт. Сфокусируйтесь на precision@5: пусть лучше будет мало, но качественных чанков. Тогда и результаты будут адекватными.
Если интересно, как RAG эволюционирует в 2026 году — почитайте «Agentic RAG против Classic RAG». Там описано, как добавить в пайплайн самокоррекцию и итеративные запросы.