English translation is not available yet. Showing Russian content.
Настроить contextual retrieval (Anthropic стиль)
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить contextual retrieval (Anthropic стиль)
1. Цель задачи
Научиться обогащать чанки документов информацией о контексте (заголовок раздела, соседние чанки, краткое резюме) перед индексацией и поиском. Это повышает релевантность retrieval на неоднозначных запросах, где базовая семантика чанка не отражает его место в документе. Вы реализуете подход, описанный в статье Anthropic о contextual retrieval, и оцените его влияние на recall.
Ключевой результат Recall@10 на наборе ambiguous queries (неоднозначных запросов) увеличивается на 20% относительно baseline (обычные чанки без обогащения).
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Набор документов для индексации | Открытый датасет (Wikipedia, arXiv, собственный корпус) — минимум 50 документов |
| Векторная база данных | Qdrant (бесплатная версия) или FAISS на локальной машине |
| Baseline retrieval pipeline | Код для chunking, эмбеддинга (all-MiniLM-L6-v2), поиска по cosine similarity |
| Тестовый набор ambiguous queries | 30–50 запросов, где ответ зависит от контекста (сгенерировать вручную или из SQuAD 2.0) |
| Метрики качества | Recall@k, Hit Rate (k=5,10) — Python скрипт расчёта |
Если нет реального инструмента — симулируем:
- Скачать датасет wiki_qa или SQuAD 2.0 (можно через datasets библиотеку).
- Разбить каждый документ на чанки по 512 токенов без перекрытия (baseline).
- Сгенерировать ambiguous queries: взять вопрос из SQuAD, но удалить ключевые сущности, чтобы без контекста чанка было непонятно. Например:
- Оригинал: «Какова высота Эвереста?»
- Ambiguous: «Какова его высота?» (без указания объекта).
- Вручную составить mapping: для каждого ambiguous query — правильный чанк (ground truth) на основе полного контекста документа.
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.10+ | Основной язык |
| Эмбеддинги | sentence-transformers (all-MiniLM-L6-v2) | Преобразование текста в векторы |
| Векторная БД | Qdrant (клиент qdrant-client) или FAISS | Хранение и поиск векторов |
| Chunking & preprocessing | LangChain (RecursiveCharacterTextSplitter) или NLTK | Разбиение документов |
| Contextual enrichment | LLM (через API OpenAI/Anthropic) или локальная модель (LLaMA 3.1) | Генерация контекстного префикса |
| Оценка | scikit-learn, pandas, matplotlib | Расчёт метрик и визуализация |
| Управление экспериментами | MLflow (опционально) | Логирование параметров и метрик |
4. Этапы выполнения
Этап 1: Подготовка данных и baseline (1 час)
Действия
-
Загрузить и подготовить датасет
- Скачать 50–100 документов (например, статьи arXiv, разделы Wikipedia).
- Сохранить каждый документ в отдельный .txt файл.
-
Разбить документы на чанки
- Использовать RecursiveCharacterTextSplitter с параметрами: chunk_size=512, chunk_overlap=50.
- Сохранить чанки в DataFrame с колонками: doc_id,
chunk_id, text,chunk_index.
-
Построить baseline retrieval pipeline
from sentence_transformers import SentenceTransformer model = SentenceTransformer('all-MiniLM-L6-v2') chunk_embeddings = model.encode(chunks['text'].tolist()) -
Создать тестовый набор ambiguous queries
- Составить 30–50 запросов (см. п.2).
- Для каждого запроса вручную указать
ground_truth_chunk_ids(1–2 правильных чанка).
Ожидаемый результат этапа
Этап 2: Разработка стратегии обогащения контекстом (1.5 часа)
Действия
-
Выбрать тип контекстной информации
- Вариант A (рекомендуемый): Добавить к чанку заголовок документа + заголовок раздела (если есть).
- Вариант B (Anthropic‑style): Добавить короткое резюме (2–3 предложения), сгенерированное LLM.
- Вариант C Добавить текст предыдущего и следующего чанка (скользящее окно).
-
Написать функцию обогащения
def enrich_chunk(doc, chunk, position='single'): if position == 'single': context = f"[Doc: {doc['title']}]\n{chunk['text']}" elif position == 'window': context = f"{prev_chunk}\n---\n{chunk}\n---\n{next_chunk}" return context -
Интеграция с LLM (опционально, для варианта B):
- Использовать openai.ChatCompletion или локальную модель.
- Prompt: «Сгенерируй краткое описание контекста этого чанка в рамках документа: {chunk_text}. Документ: {doc_title}.»
- (Для экономии токенов можно применять LLM только к первому чанку раздела.)
-
Создать enriched version чанков
- Применить выбранную стратегию ко всем чанкам.
- Сохранить обогащённые тексты в новой колонке
enriched_text.
Ожидаемый результат этапа
- Функция обогащения (Python код).
- Датафрейм с обогащёнными текстами.
Этап 3: Интеграция обогащения в retrieval pipeline (1 час)
Действия
-
Построить новый индекс на обогащённых текстах
- Закодировать
enriched_textтой же моделью эмбеддингов. - Создать отдельную коллекцию в Qdrant (например,
contextual_retrieval).
- Закодировать
-
Настроить поиск
- Использовать те же самые ambiguous queries (без изменений).
- После поиска получить
chunk_id, затем маппинг обратно к исходномуchunk_id(если используете enriched‑тексты только для индексации, но ответы возвращаете из обычных чанков).
-
Написать скрипт A/B тестирования
def evaluate(pipeline, queries, ground_truth, k=10): recalls = [] for q, gt in zip(queries, ground_truth): results = pipeline.search(q, k=k) retrieved_ids = [r.id for r in results] recall = len(set(gt) & set(retrieved_ids)) / len(gt) recalls.append(recall) return np.mean(recalls)
Ожидаемый результат этапа
- Вторая коллекция эмбеддингов.
- Функция
evaluate().
Этап 4: Оценка на ambiguous queries и сравнение с baseline (1 час)
Действия
-
Запустить оценку enriched pipeline
-
Визуализировать улучшение
- Построить гистограмму распределения recall для обеих версий.
- Вычислить среднее улучшение:
(enriched - baseline) / baseline * 100%.
-
Проанализировать случаи без улучшения
- Выписать 3–5 запросов, где recall не вырос.
- Определить, почему обогащение не помогло (например, слишком длинный контекст, шум).
Ожидаемый результат этапа
- Метрики enriched pipeline.
- График сравнения.
- Аналитический комментарий.
Этап 5: Итерация и оптимизация (1 час)
Действия
-
Попробовать альтернативные стратегии обогащения
- Если использовали только заголовок — добавить соседние чанки.
- Если использовали LLM‑резюме — уменьшить длину промпта.
-
Повторить оценку для каждой стратегии
- Ведение лога в таблице (см. ниже).
-
Выбрать лучшую стратегию
-
Задокументировать окончательный pipeline
- Оформить как Python‑скрипт с аргументами (--index, --strategy, --k).
Таблица сравнения стратегий
| Стратегия | Recall@10 | Hit Rate@10 | Примечание |
|---|---|---|---|
| Baseline (без обогащения) | 0.45 | 0.78 | |
| Только заголовок документа | 0.52 | 0.83 | +15% recall |
| Заголовок + соседние чанки | 0.54 | 0.85 | +20% recall – цель достигнута |
| LLM‑резюме (3 предложения) | 0.55 | 0.84 | +22%, но дороже по токенам |
Ожидаемый результат этапа
- Финальная конфигурация обогащения.
- Отчёт с выводами.
5. Критерии приемки (Definition of Done)
- Baseline‑пайплайн воспроизводится и даёт стабильные метрики (отклонение < 2%).
- Реализована хотя бы одна стратегия обогащения контекстом.
- Enriched‑пайплайн протестирован на тех же ambiguous queries.
- Recall@10 на enriched‑пайплайне превышает baseline не менее чем на 20%.
- Код задокументирован (docstrings, README).
- График сравнения метрик приложен.
- Описаны неудачные случаи и возможные причины.
- Pipeline опубликован в репозитории (GitHub/GitLab) с инструкцией по запуску.
6. Ожидаемый результат
Основной артефакт Python‑ноутбук (contextual_retrieval.ipynb) или скрипт, содержащий:
- Загрузку данных и разбиение на чанки.
- Baseline‑оценку.
- Функцию обогащения (с выбором стратегии).
- Построение enriched‑индекса.
- Функцию оценки.
- Сравнительную таблицу метрик.
- Графики распределения recall.
Дополнительные результаты
- Набор ambiguous queries в формате JSON.
- Файл с grounding‑truth (
ground_truth.json). - Лог эксперимента (MLflow или CSV).
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Obscure ambiguous queries сложно придумать | Использовать готовые датасеты (например, wiki_qa с удалёнными сущностями) |
| LLM‑обогащение требует API‑ключей | Заменить на эвристику (заголовок + соседние чанки) — это бесплатно |
| Размер enriched‑чанков превышает лимит модели эмбеддинга (512 токенов) | Обрезать контекст до 200 токенов, приоритет — заголовок |
| Recall улучшился, но точность (precision) упала | Добавить фильтрацию по score (threshold) или использовать hybrid search |
| Результаты невоспроизводимы из‑за случайности | Зафиксировать seed: np.random.seed(42), torch.manual_seed(42) |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| Подготовка данных и baseline | 1 час |
| Разработка стратегии обогащения | 1.5 часа |
| Интеграция в pipeline | 1 час |
| Оценка и сравнение | 1 час |
| Итерация и оптимизация | 1 час |
| Итого | 5.5 часов |
Примечание Если используется LLM через API, добавьте время на отладку промптов (~30 мин).
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 15 | Как выбрать размер чанка для RAG? |
| 42 | Что такое HNSW и как он влияет на retrieval? |
| 78 | Как оценить качество retrieval (Recall, MRR, NDCG)? |
| 123 | Способы улучшения retrieval в RAG |
| 201 | Влияние embedding модели на поиск |
| 305 | Prompt engineering для обогащения контекста |
| 410 | A/B тестирование в поисковых системах |
| 512 | Работа с FAISS в production |
| 678 | Ambiguous queries: детекция и обработка |
| 789 | Оценка влияния контекста на релевантность |
10. Чек-лист самопроверки
- Я проверил, что baseline‑метрики воспроизводятся при повторном запуске.
- Я убедился, что ambiguous queries действительно неоднозначны (без контекста ответ не очевиден).
- Моя функция обогащения корректно обрабатывает краевые случаи (первый/последний чанк).
- Я сравнил enriched‑результаты с baseline на одних и тех же запросах.
- Я задокументировал выбранную стратегию и обосновал её преимущество перед альтернативами.