0Pricing
AI Prompt Engineering · Урок

За пределами наивного 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 — локальная установка не требуется.

Все уроки этого курса

  1. За пределами наивного RAG
  2. Переранжирование найденных фрагментов
  3. Сжатие контекста
  4. Переписывание запросов и HyDE
← Назад к AI Prompt Engineering