За пределами наивного RAG
Ограничения базового поиска
«За пределами наивного RAG» — бесплатный урок AI Prompt Engineering на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Prompt Engineering, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Prompt Engineering содержит 4 уроков всего.
Что делает наивный RAG
Наивный RAG — это базовый подход: разбить документы на фрагменты, получить для них векторы, сохранить векторы, получить вектор запроса, извлечь k наиболее близких фрагментов по косинусному сходству, вставить фрагменты в промпт и сгенерировать ответ. Это сильная отправная точка, которая при масштабировании даёт предсказуемые сбои.
Понимание этих сценариев отказа необходимо для освоения передовых методов (переранжирование, сжатие, переписывание запроса), рассматриваемых в этом курсе.
def naive_rag(query, k=5):
q = embed(query)
chunks = vector_store.search(q, k) # top-k by cosine
context = '\n\n'.join(c.text for c in chunks)
return llm('Context:\n' + context + '\n\nQ: ' + query)Полнота и точность поиска
Наивный поиск k наиболее близких результатов оптимизирует необработанное векторное сходство, смешивая релевантность с поверхностной семантической близостью. Возникает компромисс: маленькое значение k может привести к пропуску ответа (низкая полнота), а большое значение k переполняет контекст отвлекающими фрагментами (низкая точность).
Сходство векторов представлений, определяющее поиск, является грубым заменителем истинной релевантности и лежит в основе нескольких последующих проблем.
# The core dilemma
# small k -> may miss the gold chunk (recall problem)
# large k -> distractors crowd context (precision + cost problem)
# Advanced RAG decouples 'retrieve many' from 'use few'Проблема несоответствия векторных представлений
Запросы и документы часто существуют в разных языковых регистрах: короткий вопрос и длинный повествовательный фрагмент. Векторы биэнкодера могут поместить релевантный ответ далеко от вопроса, потому что они сформулированы по-разному (проблема несоответствия словаря).
Это приводит к таким методам, как переписывание запроса и HyDE, которые изменяют запрос так, чтобы перед поиском привести его в соответствие с пространством документов.
# Query: 'how do I revoke a token?'
# Doc: 'Token invalidation is performed via the /sessions endpoint.'
# Lexically and semantically distant -> bi-encoder may miss it
sim = cos(embed('how do I revoke a token?'),
embed('Token invalidation via /sessions')) # may be lowПотеря информации в середине
Даже если нужный фрагмент найден, загрузка множества фрагментов вызывает эффект потери информации в середине: модель уделяет недостаточно внимания содержимому, расположенному в центре длинного контекста. Правильный фрагмент, затерянный на третьем месте из десяти, может быть фактически проигнорирован.
Это объясняет необходимость переранжирования (поместить лучший фрагмент туда, куда модель обращает внимание) и сжатия (уменьшить контекст, чтобы ни один фрагмент не оказался затерян).
# Retrieval rank != attention rank
# Place the highest-relevance chunk at the START or END,
# never stranded in the middle of a large concatenation.Чувствительность к отвлекающим фрагментам
LLM чувствительны к нерелевантному контексту. Добавление правдоподобных, но неверных фрагментов может сбить ответ с правильного пути, даже если правильный фрагмент тоже присутствует. Больший объём найденного контекста не всегда лучше.
Именно поэтому важна точность: компактный, переранжированный и сжатый контекст часто превосходит большую подборку слабо связанных фрагментов.
# Empirically: appending a single highly-similar but WRONG chunk
# can flip a previously-correct answer. RAG quality depends on
# keeping distractors OUT, not just getting the gold chunk IN.Проблемы разбиения на фрагменты
Разбиение на фрагменты фиксированного размера разрывает идеи в середине предложения, отделяет утверждение от подтверждающих его данных и удаляет структурный контекст (какой раздел, какой документ). Фрагмент, который связно читается отдельно, может оказаться бесполезным или вводящим в заблуждение без окружающего контекста.
Передовые конвейеры используют разбиение с учётом структуры, перекрытие, расширение до родительского документа и метаданные, чтобы сохранить смысл.
def structure_aware_chunks(doc, max_tokens=400, overlap=50):
sections = split_by_headings(doc) # respect document structure
chunks = []
for sec in sections:
for c in sliding_window(sec.text, max_tokens, overlap):
chunks.append(Chunk(c, meta={'section': sec.title}))
return chunksПробелы поиска только по смыслу
Поиск только по плотным векторам не справляется с задачами на точное совпадение: идентификаторами, кодами ошибок, редкими именами собственными и названиями программных интерфейсов. Именно здесь пользователи ожидают буквальной точности. Гибридный поиск объединяет сигналы плотного (семантического) и разреженного (BM25/по ключевым словам) поиска, охватывая оба типа совпадений.
Слияние по обратному рангу — простой и надёжный способ объединить два ранжированных списка без настройки веса.
def rrf(dense_ranks, sparse_ranks, k0=60):
scores = {}
for ranks in (dense_ranks, sparse_ranks):
for rank, doc_id in enumerate(ranks):
scores[doc_id] = scores.get(doc_id, 0) + 1.0 / (k0 + rank)
return sorted(scores, key=scores.get, reverse=True)Устаревший и непроверяемый контекст
Наивный RAG не учитывает актуальность или происхождение данных. Он может найти устаревшие документы и не предоставляет встроенного способа связать утверждения с источниками, что подрывает доверие и затрудняет обнаружение галлюцинаций.
Передовые системы добавляют метаданные (временная метка, источник, версия), фильтруют по ним и требуют, чтобы генератор цитировал идентификаторы фрагментов, благодаря чему ответы можно проверить.
def filtered_retrieve(q, after_date):
cands = vector_store.search(embed(q), k=50)
fresh = [c for c in cands if c.meta['date'] >= after_date]
return fresh # then re-rank; generator must cite c.idНет обратной связи — нет адаптации
Наивный RAG выполняет поиск вслепую: он не может понять, когда поиск завершился неудачей, не может решить, что поиск вообще не нужен, и не умеет повторять попытку. Передовые подходы добавляют проверку релевантности, условный поиск и многошаговый (агентный) поиск, который переформулирует запрос, если результаты выглядят слабыми.
Конвейер превращается в цикл с самооценкой, а не в один последовательный проход.
def adaptive_rag(q):
chunks = retrieve(q)
if relevance_score(q, chunks) < 0.4:
q2 = rewrite_query(q) # reformulate and retry
chunks = retrieve(q2)
if relevance_score(q, chunks) < 0.4:
return 'I could not find this in the sources.'
return generate(q, chunks)Набор передовых методов RAG
Если объединить все эти недостатки, передовой конвейер выстраивает несколько уровней: разбиение с учётом структуры и метаданных, гибридный поиск с высокой полнотой, кросс-энкодер-переранжировщик для повышения точности, сжатие контекста для соответствия ограничениям и фокусировки, переписывание запроса / HyDE для устранения несоответствия и условие релевантности с цитатами.
В следующих уроках разбирается каждый уровень. Сквозная идея такова: сначала выполнять широкий поиск, затем тщательно фильтровать и уточнять результаты.
def advanced_rag(q):
cands = hybrid_retrieve(rewrite_query(q), k=50) # high recall
top = rerank(q, cands)[:8] # precision
ctx = compress(q, top) # focus + fit
return generate_with_citations(q, ctx) # verifiableИзмеряйте до оптимизации
Сначала определите, с каким сбоем Вы на самом деле столкнулись, и только потом добавляйте новые компоненты. Отдельно измеряйте полноту поиска при k (найден ли вообще эталонный фрагмент) и точность ответа (использует ли его генератор). Для проблемы полноты и проблемы точности нужны разные исправления.
Собирайте метрики обеих частей; не добавляйте переранжировщик, если настоящая проблема связана с разбиением на фрагменты или несоответствием запроса.
def diagnose(eval_set):
return {
'recall@5': recall_at_k(eval_set, k=5), # retrieval health
'recall@50': recall_at_k(eval_set, k=50), # ceiling with rerank
'answer_acc': answer_accuracy(eval_set), # generation health
}Быстрая проверка
Определите причину сбоя RAG.
Итоги
Основные выводы:
- Наивный RAG (разбиение на фрагменты, построение векторов, выбор k лучших результатов, добавление в промпт, генерация) — сильный базовый подход с предсказуемыми сбоями.
- Сходство биэнкодера — грубый заменитель релевантности; несоответствие языковых регистров запроса и документа снижает полноту.
- Больше контекста не значит лучше: чувствительность к отвлекающим фрагментам и потеря информации в середине ухудшают ответы при увеличении k.
- Проблемы разбиения на фрагменты, пробелы поиска только по смыслу, устаревание и отсутствие обратной связи ограничивают наивный RAG.
- Передовой RAG сначала выполняет широкий поиск, а затем фильтрует результаты: использует гибридный поиск, переранжирование, сжатие, переписывание запроса и проверку релевантности. Перед оптимизацией отдельно измеряйте полноту и точность ответа.
Часто задаваемые вопросы
Урок «За пределами наивного RAG» бесплатный?
Да — полный текст урока «За пределами наивного RAG» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Prompt Engineering, подпишись на CoddyKit PRO. Курс AI Prompt Engineering содержит 4 уроков всего.
Чему я научусь в уроке «За пределами наивного RAG»?
Ограничения базового поиска Ты практикуешь AI Prompt Engineering с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Prompt Engineering?
Предыдущий опыт не требуется. AI Prompt Engineering на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «За пределами наивного RAG»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Prompt Engineering?
Да. Каждый урок AI Prompt Engineering включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- За пределами наивного RAG
- Переранжирование найденных фрагментов
- Сжатие контекста
- Переписывание запросов и HyDE