RAG с semantic chunking (кластеризация эмбеддингов предложений)
ТЕХНИЧЕСКОЕ ЗАДАНИЕ: RAG с semantic chunking (кластеризация эмбеддингов предложений)
1. Цель задачи
Реализовать метод семантического разбиения (semantic chunking) текста на основе кластеризации эмбеддингов предложений. Встроить полученные чанки в RAG-конвейер и сравнить качество retrieval с наивным chunking по фиксированной длине. Основной фокус — обеспечить смысловую целостность чанков (каждый чанк — логически завершённый фрагмент).
Ключевой результат Работающий семантический чанкер, интегрированный в RAG, с метриками retrieval (hit rate, MRR, recall@k) не ниже, чем у baseline фиксированной длины, и качественной оценкой целостности через человеческую валидацию.
2. Исходные данные
| Что нужно | Откуда взять |
|---|---|
| Корпус текстов (10–20 документов, 500–2000 слов каждый) | Любые открытые статьи на русском/английском (Wikipedia, Habr, научные публикации) |
| Baseline chunking (фиксированная длина 256 токенов, overlap 25) | Реализовать самостоятельно с помощью langchain.text_splitter.RecursiveCharacterTextSplitter |
| Набор вопросов для тестирования retrieval (20–30 шт.) | Составить вручную по содержанию корпуса |
| Векторное хранилище | ChromaDB / FAISS (in-memory) |
| Embedding модель | intfloat/multilingual-e5-small или all-MiniLM-L6-v2 |
Если нет реального инструмента — симулируем:
- Взять любой текстовый файл (например,
docs.txt) и разбить на предложения через nltk.sent_tokenize или spacy. - Для кластеризации использовать scikit-learn без GPU — все эксперименты проводить на CPU с небольшим корпусом.
- Если нет готового RAG-пайплайна — собрать минимальный с помощью LangChain и ChromaDB (установка через pip).
3. Технологический стек
| Компонент | Инструменты | Назначение |
|---|---|---|
| Язык программирования | Python 3.10+ | Разработка |
| Обработка текста | spacy / nltk, sentence-transformers | Токенизация, эмбеддинги предложений |
| Кластеризация | scikit-learn (KMeans, DBSCAN) | Группировка предложений по смыслу |
| Chunking (baseline) | LangChain RecursiveCharacterTextSplitter | Базовое разбиение |
| Векторная БД | ChromaDB / FAISS | Хранение и поиск чанков |
| RAG orchestration | LangChain (или DIY) | Построение цепочки retrieval |
| Оценка retrieval | RAGAS (или ручной расчёт) | Метрики hit rate, MRR, recall@k |
| Визуализация | matplotlib, seaborn | Графики кластеризации и распределения чанков |
4. Этапы выполнения
Этап 1: Подготовка данных и baseline chunking (1 час)
Действия
-
Загрузка корпуса
- Прочитать 10–15 текстов из папки
docs/. - Для каждого документа сохранить исходный текст в список
raw_docs.
- Прочитать 10–15 текстов из папки
-
Разбиение на предложения
-
- Использовать RecursiveCharacterTextSplitter(chunk_size=256, chunk_overlap=50).
- Разбить каждый документ, сохранить чанки в список
baseline_chunks. - Векторизовать baseline чанки эмбеддинговой моделью и загрузить в ChromaDB (коллекция baseline).
Ожидаемый результат этапа
- Корпус предложений (sentences.json).
- Заполненная векторная БД с baseline чанками.
- Скрипт
baseline_chunking.py.
Этап 2: Семантический chunking через кластеризацию (2–3 часа)
Действия
-
Получить эмбеддинги предложений
- Загрузить модель SentenceTransformer('all-MiniLM-L6-v2').
- Для всех предложений рассчитать эмбеддинги: embeddings = model.encode(sentences).
- Нормализовать эмбеддинги (L2) для косинусной близости.
-
Кластеризация предложений
- Вариант A (KMeans):
- Определить оптимальное число кластеров через
silhouette_scoreв диапазоне[2, min(20, N_sentences/2)]. - Зафиксировать лучшее
k.
- Определить оптимальное число кластеров через
- Вариант B (DBSCAN):
- Подобрать
eps(0.3–0.6) методом перебора на малом корпусе. - Использовать metric='cosine'.
- Подобрать
- Выбрать один вариант (рекомендуется DBSCAN, т.к. не требует фиксации числа кластеров).
- Вариант A (KMeans):
-
Формирование чанков из кластеров
- Для каждого кластера собрать предложения в порядке их появления в исходном документе.
- Объединить в один чанк (конкатенация с пробелом).
- Если кластер слишком мал (<3 предложений) — присоединить к ближайшему кластеру по среднему эмбеддингу.
- Если кластер слишком велик (>15 предложений) — разбить пополам, но сохранить семантическую целостность (по границе соседних предложений с минимальным расстоянием между блоками).
-
Загрузка semantic чанков в векторную БД
- Создать коллекцию
semantic_chunks. - Векторизовать каждый чанк и сохранить метаданные (doc_id, начало/конец).
- Создать коллекцию
Ожидаемый результат этапа
- Ноутбук semantic_chunking.ipynb с визуализацией кластеров (2D t-SNE).
- Скрипт
semantic_chunker.py. - ChromaDB с коллекцией
semantic_chunks.
Этап 3: Интеграция с RAG-пайплайном (1–2 часа)
Действия
-
Построить RAG с двумя источниками
- Использовать LangChain: VectorStoreRetriever для каждой коллекции.
- Параметры:
search_kwargs={"k": 3}.
-
Написать функцию query_evaluation
- Вход: список вопросов, имя коллекции.
- Выход: словарь с retrieved документами, скорами.
-
Сгенерировать ответы (опционально)
- Подключить простую LLM (локально через Ollama или OpenAI API) для генерации ответа на основе retrieved чанков.
- Использовать промпт:
Ответь на вопрос на основе следующих фрагментов. Если информации недостаточно, скажи "не знаю". Фрагменты: {context} Вопрос: {question}
Ожидаемый результат этапа
- Функция
run_retrieval(questions, collection_name). - CSV-файл
retrieval_results.csvс полями:question, collection, top3_chunks, scores.
Этап 4: Оценка качества retrieval (1–2 часа)
Действия
-
Собрать ground truth
- Для каждого вопроса вручную указать, какой документ (и приблизительно какие предложения) содержат ответ.
- Например,
{"question": "...", "relevant_docs": [doc_id], "relevant_chunks": [chunk_ids]}.
-
Рассчитать метрики
hit_rate@3: бинарная метрика — попал ли хотя бы один релевантный чанк в top-3.MRR@3: обратный ранг первого релевантного результата.recall@3: доля релевантных чанков среди retrieved.- Реализовать вручную или через
ragas.metrics.
-
Сравнение с baseline
- Запустить
run_retrievalдля обеих коллекций. - Построить таблицу:
- Запустить
| Метрика | Baseline (fixed) | Semantic (clustering) | Delta |
|---|---|---|---|
| hit_rate@3 | 0.75 | 0.80 | +0.05 |
| MRR@3 | 0.65 | 0.70 | +0.05 |
| recall@3 | 0.50 | 0.55 | +0.05 |
- Качественная оценка семантической целостности
- Взять 10 случайных чанков из semantic и baseline.
- Попросить двух коллег оценить каждый чанк по шкале 1–5: насколько он является логически завершённым фрагментом.
- Посчитать средний балл и дисперсию.
Ожидаемый результат этапа
metrics_comparison.csv.- График распределения баллов целостности.
Этап 5: Итоговый отчёт и рефакторинг (1 час)
Действия
-
Описать архитектуру
- Схема процесса: документ → предложения → эмбеддинги → кластеризация → чанки → retrieval.
- Обосновать выбор алгоритма кластеризации и параметров.
-
Собрать выводы
-
Подготовить презентацию
- 5–7 слайдов: постановка, методы, результаты, демонстрация.
Ожидаемый результат этапа
- Файл
REPORT.md. - Презентация
pres.pdf(опционально). - Чистый репозиторий с кодом, требованиями и инструкцией по запуску.
5. Критерии приемки (Definition of Done)
- Реализован скрипт
semantic_chunker.py, который принимает текст и возвращает список чанков. - Кластеризация выполняется без фиксации числа кластеров (DBSCAN) или с автоматическим подбором K.
- Есть функция сравнения с baseline (фиксированная длина).
- Получены метрики hit_rate@3, MRR@3, recall@3 для обеих стратегий.
- Результаты сравнения представлены в виде таблицы и графика.
- Проведена качественная оценка целостности чанков (минимум 2 рецензента).
- Код оформлен в виде модульной структуры:
src/,data/,notebooks/. - Написаны requirements.txt или pyproject.toml с зависимостями.
- Векторная БД воспроизводится с нуля по инструкции.
- В коде есть docstring и комментарии к ключевым шагам.
6. Ожидаемый результат
Основной артефакт Репозиторий с реализацией семантического чанкера и сравнением retrieval качества.
Содержание
src/semantic_chunker.py— классSemanticChunkerс методамиfit,transform.src/baseline_chunker.py— baseline реализация.src/retrieval_eval.py— расчёт метрик.data/— корпус и ground truth.notebooks/exploration.ipynb— визуализация кластеризации.REPORT.md— отчёт с выводами.
Дополнительные результаты
- Рабочий RAG-пайплайн, готовый к использованию с новой информацией.
- Документация по запуску.
7. Возможные сложности и их решение
| Сложность | Решение |
|---|---|
| Неоптимальное число кластеров (KMeans) | Использовать DBSCAN с автоматическим определением; для KMeans — silhouette score. |
| Кластер содержит предложения из разных документов | При формировании чанка группировать предложения по doc_id, не смешивать. |
| Очень маленький чанк (1–2 предложения) | Объединять с соседним кластером по минимальному расстоянию между центроидами. |
| Высокая вычислительная стоимость на больших корпусах | Ограничить корпус 10 документами; использовать batch encoding. |
| Неоднозначность ground truth для метрик | Использовать несколько аннотаторов и majority voting. |
| Сложность настройки DBSCAN (eps) | Нормализовать эмбеддинги и подбирать eps через поиск по сетке на малом корпусе. |
8. Бюджет времени (оценка)
| Этап | Время |
|---|---|
| 1. Подготовка данных и baseline | 1 час |
| 2. Семантический chunking (кластеризация) | 2.5 часа |
| 3. Интеграция с RAG | 1.5 часа |
| 4. Оценка качества | 2 часа |
| 5. Итоговый отчёт и рефакторинг | 1 час |
| Итого | 8 часов |
Примечание Для первого раза рекомендуется заложить +2–3 часа на отладку и настройку параметров кластеризации.
9. Связанные вопросы из базы знаний
| Вопрос | Тема |
|---|---|
| 12 | Основы векторных эмбеддингов |
| 34 | Sentence transformers: обзор моделей |
| 56 | Методы кластеризации в Scikit-learn |
| 78 | Метрики оценки retrieval (HR, MRR, Recall) |
| 91 | LangChain: работа с текстовыми сплиттерами |
| 112 | DBSCAN vs KMeans для текстовых данных |
| 145 | Оценка качества RAG с помощью RAGAS |
| 167 | Принципы chunking в RAG-системах |
| 189 | Визуализация эмбеддингов (t-SNE, UMAP) |
| 204 | Оптимизация скорости инференса эмбеддингов |
10. Чек-лист самопроверки
- Я получил корректные эмбеддинги предложений (формат numpy array, размерность 384–768).
- Кластеризация даёт кластеры, соответствующие тематическим сдвигам в тексте (визуально проверено).
- Чанки, сформированные из кластеров, не содержат предложений из разных исходных документов.
- Я сравнил метрики retrieval и увидел, что semantic chunking не хуже baseline (или лучше).
- Код покрыт комментариями и запускается из корня репозитория одной командой
python main.py.