中文翻译暂不可用,显示俄语原文。

Настроить query expansion с LLM и BM25

ТЕХНИЧЕСКОЕ ЗАДАНИЕ: Настроить query expansion с LLM и BM25

1. Цель задачи

Научиться улучшать recall поиска за счёт генерации синонимов и релевантных терминов к пользовательскому запросу с помощью LLM. Расширенный запрос подаётся в BM25, что позволяет находить документы, не содержащие точных слов исходного запроса. Цель — добиться прироста recall@10 не менее чем на 15% относительно baseline BM25, не ухудшив precision более чем на 5%.

Ключевой результат Рабочий пайплайн query expansion + BM25, измеренный прирост recall на тестовом наборе запросов.


2. Исходные данные

Что нужноОткуда взять
Корпус документов (не менее 1000)Дамп Wikipedia (первые 1000 статей), собственный набор новостей / инструкций, или любой текстовый датасет
Тестовые запросы (20–30 штук)Случайно сгенерировать из документов (взять заголовки или ключевые фразы) и слегка перефразировать, чтобы слова расходились с текстом
Baseline BM25Реализовать самостоятельно (или взять Whoosh / Elasticsearch) и прогнать на тестовых запросах
LLM для генерации расширенийOpenAI API (GPT-4o) или локальная модель (Mixtral 8x7B, Llama 3 70B) через Ollama / vLLM
Разметка релевантности (ground truth)Для каждого запроса вручную отметить до 10 релевантных документов из корпуса (или автоматически — по точному совпадению слов, но лучше manual)

Если нет реального инструмента — симулируем:

  1. Корпус: возьмите 100–200 текстов из проекта data/raw вашего pet-проекта или датасет ms_marco (10 документов на запрос).
  2. Тестовые запросы: используйте 10 вопросов из датасета TREC или Natural Questions.
  3. LLM: используйте gpt-3.5-turbo (очень дёшево, достаточно 50 запросов). Если нет API — возьмите flan-t5-large через Hugging Face.
  4. Ground truth: используйте существующие разметки MS MARCO или TREC.

3. Технологический стек

КомпонентИнструментыНазначение
Язык программированияPython 3.10+Весь пайплайн
Корпус и поискWhoosh или Elasticsearch (on-prem)BM25 индексация и поиск
LLMOpenAI API / vLLM / Hugging Face TransformersГенерация синонимов и расширений
Измерение метрикnumpy, pandas, scikit-learnRecall@k, Precision@k, MPR
Загрузка данныхdatasets (Hugging Face) / requestsПолучение корпуса
Логированиеlogging / wandbОтслеживание экспериментов

4. Этапы выполнения

Этап 1: Подготовка корпуса и baseline BM25 (1 час)

Действия

  1. Скачать корпус из датасета ms_marco (колонка passages) или загрузить свои документы в формате JSONL (каждая строка — {"id": "...", "text": "..."}).
  2. Проиндексировать корпус в Whoosh:
    from whoosh.index import create_in
    from whoosh.fields import Schema, TEXT, ID
    schema = Schema(id=ID(stored=True), content=TEXT(stored=True))
    ix = create_in("indexdir", schema)
    writer = ix.writer()
    for doc in corpus:
        writer.add_document(id=doc['id'], content=doc['text'])
    writer.commit()
    
  3. Написать функцию bm25_search(query, k=10), возвращающую список (doc_id, score).
  4. Подготовить тестовый набор запросов и ground truth:
    • Запросы: 20–30 штук, каждый — строка из 3–7 слов.
    • Для каждого запроса известны ID релевантных документов (можно взять из той же разметки MS MARCO qrels).
  5. Запустить поиск для всех запросов:
    results = {}
    for q in queries:
        results[q] = [doc_id for doc_id, _ in bm25_search(q, k=10)]
    
  6. Посчитать baseline recall@10 (len(set(results[q]) & set(relevant[q])) / len(relevant[q])) и precision@10.

Ожидаемый результат этапа Измеренные baseline метрики (например, recall@10 = 0.45, precision@10 = 0.60). Функции bm25_search и evaluate.

Этап 2: Реализация query expansion через LLM (1.5 часа)

Действия

  1. Разработать промпт для генерации расширения запроса. Пример:
    You are a search query expansion assistant. Given a user query, generate up to 5 alternative formulations, synonyms, or related terms that could help retrieve relevant documents. Output only a comma-separated list, no explanations. Query: {query}
    
  2. Реализовать функцию expand_query(query, llm_backend):
    • Вызвать LLM (через OpenAI API или локально).
    • Распарсить ответ (список слов/фраз).
    • Склеить исходный запрос и расширения в одну строку (например, original_query synonym1 synonym2 ...).
  3. Протестировать на 3–5 запросах вручную: убедиться, что синонимы осмысленные.
  4. Обработать крайние случаи:
    • Если LLM возвращает пустой ответ — использовать только исходный запрос.
    • Если ответ слишком длинный (>100 слов) — обрезать до первых 10 токенов.

Ожидаемый результат этапа Функция expand_query, протестированная на выборке. Логи примеров расширений.

Этап 3: Интеграция расширенного запроса с BM25 (30 минут)

Действия

  1. Создать функцию search_with_expansion(query, k=10):
    • Вызвать expand_query(query).
    • Вызвать bm25_search(expanded_query, k=k).
  2. Запустить её на всех тестовых запросах.
  3. Посчитать метрики recall@10, precision@10.

Ожидаемый результат этапа Значения метрик после expansion (например, recall@10 = 0.53, precision@10 = 0.55). Сравнение с baseline.

Этап 4: Оптимизация и анализ (1 час)

Действия

  1. Выяснить, какие запросы выиграли больше всего, а какие проиграли.
  2. Настроить параметры:
    • Количество генерируемых синонимов (попробуйте 3, 7, 10).
    • Стратегия объединения: OR-буст исходных слов, взвешенная сумма.
    • Фильтрация плохих расширений (n-граммы, stopwords).
  3. Проверить альтернативный промпт, который просит LLM выделить ключевые понятия, а не синонимы.
  4. Если precision упал >5% — ввести вес: исходный запрос подаётся с бустом (например, original^3 synonym1 synonym2), если BM25 поддерживает boosting (Whoosh — нет, Elasticsearch — да). Для Whoosh можно повторять исходные слова несколько раз.

Ожидаемый результат этапа Достигнутый прирост recall ≥15% при падении precision ≤5%. Зафиксированные гиперпараметры.

Этап 5: Документирование и выводы (30 минут)

Действия

  1. Оформить отчёт: таблица с результатами (baseline vs expanded), примеры успешных и неуспешных запросов.
  2. Написать краткое описание использованного промпта и настроек.
  3. Собрать код в единый скрипт query_expansion.py.

Ожидаемый результат этапа Файл RESULTS.md с метриками, выводами, скриптом.


5. Критерии приемки (Definition of Done)

  • Baseline recall@10 и precision@10 зафиксированы.
  • Реализована функция expand_query, вызывающая LLM.
  • Пайплайн поиска с expansion даёт recall@10 ≥ baseline + 15% (абсолютных процентных пунктов? Лучше относительный прирост: если baseline = 0.45, то target ≥ 0.45*1.15 = 0.5175).
  • Precision@10 упал не более чем на 5% (от baseline, например с 0.60 до не ниже 0.57).
  • Все тестовые запросы обработаны без ошибок.
  • Код и промпт задокументированы в README проекта.

6. Ожидаемый результат

  • Основной артефакт Python‑скрипт query_expansion_pipeline.py, который принимает корпус, индексирует, прогоняет baseline и expansion, выводит метрики.
  • Дополнительно Файл RESULTS.md с таблицей метрик по каждому запросу и общими цифрами.
  • Пример содержания RESULTS.md
    ## Результаты query expansion (BM25 + LLM)
    | Метрика | Baseline | Expanded | Δ |
    |---------|----------|----------|----|
    | Recall@10 | 0.45 | 0.53 | +17.8% |
    | Precision@10 | 0.60 | 0.58 | -3.3% |
    
  • Опционально Сравнение разных LLM (GPT-4o vs Mixtral) и разных k.

7. Возможные сложности и их решение

СложностьРешение
LLM генерирует нерелевантные синонимыУточнить промпт: «only domain-specific synonyms for {domain}»; добавить few-shot examples; отфильтровать по частотности в корпусе.
Увеличение времени ответа из-за вызова LLMКешировать результаты расширения для повторяющихся запросов; использовать async вызовы.
BM25 не поддерживает boosting отдельных слов (Whoosh)Повторить несколько раз оригинальные слова в строке запроса.
Модель слишком дорогая (OpenAI)Использовать локальную модель (Mixtral 8x7B, Llama 3 8B) через Ollama; ограничить количество запросов.
Precision упал сильно (шумы)Добавить реранжировку: после BM25 top-100 переранжировать с помощью кросс-энкодера.

8. Бюджет времени (оценка)

ЭтапВремя (часы)
1. Подготовка корпуса и baseline1
2. Реализация query expansion через LLM1.5
3. Интеграция с BM250.5
4. Оптимизация и анализ1
5. Документирование0.5
Итого4.5

Примечание: Для первого раза рекомендуется заложить +1 час на отладку LLM запросов и чистку данных.


9. Связанные вопросы из базы знаний

ВопросТема
17BM25 и его варианты
28Query expansion (ручные тезаурусы)
44Query expansion с LLM (данная задача)
56Оценка retrieval: Recall, Precision, MAP
73Индексирование текстов (Whoosh / Lucene)
89Промпт-инжиниринг для генерации терминов
102Реранжировка результатов поиска
115Domain adaptation текстовых моделей
134Data drift в запросах пользователей

10. Чек-лист самопроверки

  • Я оценил baseline BM25 на тестовых запросах до внедрения расширения.
  • Убедился, что LLM даёт осмысленные расширения (визуально проверил 5 примеров).
  • Проверил, что итоговый recall@10 действительно ≥ baseline + 15%, а precision упал не более чем на 5%.
  • Реализовал обработку ошибок LLM (timeout, пустой ответ).
  • Задокументировал все шаги и результаты в едином файле.