Почему ваш RAG даёт плохие ответы и что с этим делать
Большинство RAG-систем — это поиск по нарезанным кускам текста. Рассказываю, как за 4 шага превратить документы в настоящую Knowledge Base и получить 93% точности на межд-документальных запросах.
Я потратил полгода на то, чтобы понять одну вещь: RAG — это не про поиск. Это про знания. И разница между первым и вторым — пропасть.
Большинство строят RAG так: нарезал документы на чанки, закинул в векторную базу, подключил LLM — работает. На простых вопросах — действительно работает. Но стоит задать что-то сложнее, что-то, где ответ собирается из нескольких документов, — система рассыпается. Галлюцинирует, путает факты, игнорирует половину релевантных источников.
Проблема не в модели. Проблема в том, что вы построили поисковик, а нужна была база знаний.
Я покажу четыре шага, которые превращают набор документов в полноценную Knowledge Base для AI-агентов. Каждый шаг — конкретный, реализуемый, с измеримым результатом. В конце — 93% правильных ответов на сложных межд-документальных запросах по корпусу из 180 научных статей.
Стандартный RAG и почему он ломается
Классический пайплайн все знают: документы → chunking → embeddings → vector store → retrieval → LLM. Прост, понятен, запускается за вечер.
Вот только этот пайплайн делает три допущения, которые в реальности не выполняются:
Первое: что каждый чанк самодостаточен. Нет. Абзац из раздела «Результаты» научной статьи бесполезен без описания эксперимента из раздела «Методы».
Второе: что cosine similarity находит именно то, что нужно. Не всегда. Семантически похожий текст — это не обязательно релевантный ответ. Фрагмент, который упоминает CRISPR, и фрагмент, который описывает результаты эксперимента с CRISPR, — это разные вещи.
Третье: что все релевантные фрагменты можно найти одним поисковым запросом. Нельзя — если одна и та же сущность в разных документах называется по-разному, часть информации вы просто не увидите.
Моя первая версия RAG давала 38% правильных ответов на вопросах, требующих синтеза из нескольких источников. На простых однодокументных — около 72%. Разрыв красноречивый.
Дальше — четыре шага, каждый из которых закрывал конкретную слабость.
Шаг 1. Сохранить структуру документов
Chunking по 512 токенов с overlap — это как разрезать книгу на полоски и надеяться, что кто-то по полоске поймёт сюжет.
Научная статья имеет структуру: аннотация, введение, методы, результаты, обсуждение. Техническая документация — разделы, подразделы, примеры кода. Юридический договор — статьи, пункты, определения. Эта структура несёт смысл. Выбрасывать её — терять контекст.
Я перешёл на structure-aware parsing. Каждый чанк получает полную цепочку контекста:
- Из какого документа (название, авторы, дата, тип)
- Из какого раздела (Introduction → 2.3 Experimental Setup → Cell Culture)
- Какого типа контент (определение, данные, метод, вывод)
- Где именно в документе находится
Плюс — двухуровневая индексация. Мелкие чанки (300–400 токенов) для точного поиска, крупные блоки (целые секции) для передачи в LLM. Нашли маленький чанк — подтягиваем его родительскую секцию. Модель получает не огрызок текста, а цельный блок с контекстом.
Результат: точность на межд-документальных запросах выросла с 38% до 57%. Модель начала понимать, откуда и зачем каждый фрагмент.
Шаг 2. Семантически разметить контент
Векторный поиск находит похожее. Но вам нужно нужное. Это не одно и то же.
Когда пользователь спрашивает «какие побочные эффекты у метода X», ему нужны экспериментальные данные — конкретные цифры из конкретных исследований. А cosine similarity может вернуть обзорный абзац, где побочные эффекты упомянуты вскользь, потому что слова совпали.
Семантическая маркировка добавляет к каждому чанку структурированный слой метаданных:
Тип контента — это определение? Описание метода? Экспериментальный результат? Гипотеза? Вывод? Сравнение подходов?
Ключевые сущности — не просто «слова из текста», а конкретные объекты: гены, белки, методы, препараты, метрики.
Утверждения (claims) — какой тезис выдвигает этот фрагмент. «Off-target rate снижен до 0.3% при использовании модифицированной guide RNA» — это конкретное утверждение с конкретными цифрами.
Уровень доказательности — рецензированное исследование, препринт, мнение эксперта, теоретическая модель.
Я прогонял чанки через LLM с заточенным промптом для аннотирования. Да, это стоит токенов. Но делается один раз на этапе индексации и кардинально меняет качество retrieval.
Теперь при поиске можно не просто искать «похожий текст», а фильтровать: дай мне чанки типа experimental_result, содержащие сущность CRISPR off-target, с уровнем доказательности peer_reviewed. Это хирургическая точность вместо ковровой бомбардировки cosine similarity.
Результат: точность поднялась до 68%. Система перестала путать упоминания с доказательствами.
Шаг 3. Объединить сущности
Вот проблема, которая выглядит незначительной, пока не сталкиваешься с ней на реальных данных.
В моих 180 статьях белок p53 упоминался как «p53», «TP53», «tumor protein p53», «Li-Fraumeni syndrome protein» и ещё десятком вариантов. Один метод — как «ChIP-seq», «chromatin immunoprecipitation sequencing», «ChIP-sequencing assay».
Для стандартного RAG это разные вещи. Embedding'и для «p53» и «tumor protein p53» будут похожи, но не идентичны. А «Li-Fraumeni syndrome protein» — вообще далеко в векторном пространстве. Запрашиваете информацию про p53 — получаете ответ на основе 30–40% релевантных фрагментов. Остальные система не связывает.
Entity resolution — процесс приведения всех вариантов к одному каноническому ID:
Извлечение. LLM проходит по чанкам и достаёт именованные сущности. У меня получилось ~12 000 уникальных упоминаний.
Кластеризация. Группируем синонимы. Автоматически — по similarity эмбеддингов плюс словари синонимов из доменных баз (UniProt для белков, MeSH для медицинских терминов). Неочевидные случаи — ручная верификация.
Нормализация. Каждая сущность → канонический ID + массив алиасов. При индексации и поиске все формы приводятся к одному идентификатору.
12 000 упоминаний схлопнулись в 3 400 уникальных сущностей. Две трети были дубликатами. Две трети информации стандартный RAG терял просто потому, что одна и та же вещь называлась по-разному.
Результат: 79% точности. Скачок именно на межд-документальных запросах — система наконец собирала все фрагменты, а не случайную подвыборку.
Шаг 4. Связать документы между собой
Последний слой — и самый мощный. Здесь мы превращаем коллекцию документов в граф знаний.
Документы не существуют в вакууме. Статья A подтверждает выводы статьи B. Метод из статьи C — улучшенная версия метода из статьи D. Результаты статьи E опровергают гипотезу из статьи F, а статья G, опубликованная позже, разрешает противоречие.
Без явных связей агент может уверенно процитировать вывод, который был опровергнут через полгода в другой публикации. Он не знает про опровержение — оно в другом документе, и прямого текстового совпадения нет.
Я строил пять типов связей:
- supports / contradicts — подтверждает или опровергает тезис
- extends — развивает идею, добавляет новые данные
- uses_method_from — использует методологию из другой работы
- supersedes — более свежие данные по тому же вопросу
- same_topic_different_angle — та же тема, другой подход или датасет
Технически — это граф. Узлы — чанки с метаданными, рёбра — типизированные связи. Я использовал Neo4j, но для корпусов до 500 документов хватит PostgreSQL с JSONB.
Как это меняет retrieval: нашли релевантный чанк → проходим по графу на 1–2 шага → подтягиваем связанные фрагменты. Если найден результат эксперимента, автоматически добавляем: описание методологии, подтверждающие и опровергающие данные, более свежие публикации по теме.
В промпт LLM попадает не россыпь похожих текстов, а связная картина: вот результат, вот как его получили, вот кто подтвердил, вот кто оспорил, вот последние данные.
Результат: 93% точности. Именно граф связей дал агенту возможность отвечать на вопросы класса «какой метод показал лучшие результаты для X с учётом всех исследований» — тип запросов, на которых базовый RAG не имел шансов.
Полная картина
Базовый RAG — 38%. После добавления структуры документов — 57%. После семантической маркировки — 68%. После entity resolution — 79%. После графа связей — 93%.
Тестировал на 200 вопросах — от простых фактоидных до аналитических, требующих синтеза из 3–7 источников. Оценивали два эксперта-предметника. Каждый шаг давал стабильный прирост в 10–15 процентных пунктов.
Что из этого следует
Превращение документов в Knowledge Base — это инвестиция на этапе индексации. Прогон через LLM для разметки, построение entity resolver, создание графа связей — всё это стоит времени и денег.
Но считать надо иначе. Не «сколько стоит построить», а «сколько стоит система, которая врёт в 60% сложных случаев». Если агент принимает решения на основе RAG — цена ошибки перевешивает цену подготовки.
Мой совет: начните с первого шага. Структурный парсинг с сохранением иерархии можно реализовать за день. Вы увидите разницу сразу. Дальше наращивайте слой за слоем, замеряя результат на каждом этапе. Каждый шаг самоценен — даже если остановитесь на втором, система будет заметно лучше базового RAG.
Knowledge Base — это не поисковый индекс с LLM на выходе. Это структурированное, размеченное, связанное представление знаний. И именно это нужно агентам, чтобы давать ответы, которым можно доверять.