Как вы обрезаете контекст, когда retrieved documents > контекстного окна LLM?

Краткий тезис

В RAG-системе retrieval часто возвращает больше документов (чанков), чем может вместить контекстное окно LLM. Проблема нельзя просто взять первые K чанков — можно потерять важную информацию. Решение нужно выбрать наиболее релевантные части контекста, сохраняя при этом смысловую целостность. Основные подходы: ранжирование (cross-encoder отбирает лучшие чанки), сжатие (LLMLingua удаляет неважные слова), суммирование (замена нескольких чанков их саммари), рекурсивное сокращение (обрезка до нужного размера). Самый простой (но грубый) метод — тримминг (обрезать длинные чанки).

Ключевая идея LLM имеет ограниченное контекстное окно (4k-128k токенов). Если retrieval вернул 20 чанков по 1000 токенов (20k токенов), а окно LLM — 8k токенов, нужно как-то ужать контекст без потери критической информации.


1. Термин: Контекстное окно LLM (Context window)

Что это Максимальное количество токенов, которое LLM может обработать за один раз.

МодельКонтекстное окно
GPT-3.54k-16k
GPT-4-turbo128k
Claude 3200k
Llama-38k-128k
Gemini 1.5 Pro2M

Термин «Токен» Базовая единица текста для LLM. 1 токен ≈ 0.75 слова на английском, ≈ 0.5 слова на русском.

Проблема Если retrieved documents весят больше контекстного окна, LLM либо обрежет контекст (loss between потеря информации), либо выдаст ошибку, либо упадёт (OOM — out of memory).


2. Подход 1: Ранжирование (Reranking) — оставляем top-k

Что это Используем cross-encoder (более точную модель релевантности) для переранжирования retrieved чанков и оставляем только top-k, которые влазят в контекстное окно.

Термин «Cross-encoder» Модель, которая принимает пару (запрос, документ) и выдаёт число — насколько документ релевантен запросу. Медленнее bi-encoder (который используется в векторной БД), но точнее.

Pipeline

1. Retrieval (bi-encoder, быстрый) → 100 чанков
2. Reranking (cross-encoder, точный) → скорим 100 чанков
3. Выбираем top-5 чанков (которые влазят в контекст)
4. Подаём top-5 в LLM

Код

from sentence_transformers import CrossEncoder

# Инициализация cross-encoder
cross_encoder = CrossEncoder('BAAI/bge-reranker-v2-m3')

# Шаг 1: retrieval вернул 100 чанков
retrieved_chunks = vector_search(query, top_k=100)

# Шаг 2: reranking
pairs = [(query, chunk["text"]) for chunk in retrieved_chunks]
scores = cross_encoder.predict(pairs)
top_chunks = [chunk for _, chunk in sorted(zip(scores, retrieved_chunks), reverse=True)[:5]]

Навигация