Начало всех начальных начал
Добрый день, уважаемые хабропоселенцы;‑) Сегодня мы будем говорить, снова о хакатонах и разработке RAG‑моделей, вернее моделей с RAG‑подходами и наших попытках выйти за рамки простого векторного поиска.
Нам необходимо было создать интеллектуальный pipeline RAG‑системы, которая по пользовательскому запросу находит релевантные фрагменты в корпусе данных. И, конечно же, мы «запилили» своё «модное» решение, о котором вам спешим рассказать в этой статье. Мы проиллюстрируем, как выстраивали архитектуру, какие модели тестировали, на чём остановились и почему, именно такой подход оказался для нас наиболее удачным. Покажем, как работает весь пайплайн — от чанкования документов до гибридного поиска и поделимся результатами бенчмарков и планами развития системы в дальнейшем. Всех заинтересованных лиц приглашаю по традиции под кат;‑)
Задачка, стеки и инфра
Говоря, по направлениям, здесь было всё стандартно: Data Science, NLP, AI Engeneering, ML, Frontend, Backend, Fullstack, Machine Learning. По формату первый этап онлайн, второй офлайн, который проходил в Москве.
По таймлайну была неделька на сборку проекта, потом чекапы и проверка ещё неделька, потом давалось ещё 10 дней для финалистов на допил проектов и финалка “вылетала” на начало декабря. По стеку значилось следующее Python 3.8+; Jupyter / Colab / IDE; Pandas, Numpy; Matplotlib; Scikitlearn; CatBoost / XGBoost / LightGBM / PyTorch.
Неповторимый оригинал решения и железный цех
Исходя из поставленной задачи, мы задумались над концептом, которое нам стоило “запилить” и затем как-то приумножить. Так вот, мы решили, что текущая постановка может быть больше чем, просто, лайтовая интересная, и мы пошли дальше: решили соединить два мегасильных концепта Retrieval-Augmented Generation и графы знаний. Такой гибрид позволил бы системе не просто находить релевантные фрагменты текста, а понимать структуру информации, улавливать взаимосвязи и давать ответы, которые гораздо ближе к тому, как рассуждает реальный человек.
Для начала мы пошли в сторону архитектуры и пайплайна и осветили, так сказать, общий план работы над задачей, которую необходимо было поднять. Итак, в основе нашего проекта лежала модельная архитектура, причём, мы здесь сознательно отказались от линейных пайплайнов в пользу системы, которая формировала граф связей прямо на “лету”, то бишь, на “горячую”. Этот подход позволил нам сразу, динамически, адаптироваться к родственным документам, расширяющимся базам знаний и источникам данных.
За ключевые моменты мы выбрали Ingestion & Chunking. Вначале документы поступали в систему, проходили один из трёх алгоритмов “нарезки”, при этом каждый чанк получал собственный профит метаданных: web_id, url, kind, chunk_text. Затем полученные тексты преобразовывались в векторы – это принципиально. Мы, здесь, сразу выполняли L2-нормализацию для корректности будущих сравнений, эти моменты укладывались у нас для Embeding Generation. Далее шла индексация, полученные эмбединги, мы упаковывали в FAISS, причём, мы остановились на связке HNSW + IndexFlatIP, как на наиболее сбалансированном решении по времени ответа и качеству поиска. На финалке мы гордо дошли до Graph Construction и здесь мы вышли за привычные рамки и сделали классификацию через NLI. Следующим моментом, который мы учли в своей работы был Hybrid Search и здесь участвовало сразу два механизма: FAISS (векторный уровень), TF-IDF (точные совпадения), Cross-Encoder (точное переранжирование). Метаданные “жили” вне индекса, чтобы FAISS оставался максимально лёгким.
На следующем этапе нас ждали алгоритмы чанкования и построение графа: наш взор упал на рассмотрение стразу трёх возможных алгоритмов. При этом мы не стали ограничиваться одним подходом, так как разные документы требуют разных стратегий, а именно: фиксированная длина – простой и надёжный метод (600 слов + 100 overlap); рекурсивное чанкование – документ уменьшает сам себя, пока не уложится в лимит; семантическое чанкование - (самая интересная стратегия, по нашему мнению) близкие по смыслу предложения объединяются в чанк, не нарушая логики текста.
Так уже хорошо, движемся дальше ;-)
Чтобы граф был не просто набором рёбер, а полноценной структурой знаний, мы использовали NLI-модель MoritzLaurer/mDeBERTa-v3-base-mnli-xnli. Она определяла характер связи между чанками (Strong, Medium, Weak). То есть, у нас было не просто похожие фрагменты, а фрагменты с различной степенью смысловой близости, и это очень влияло на финальное качество поиска.
Тесты, тесты, тесты и кофе, кофе, кофе
Наступил черёд испытать наш космолет подход в серьёзной битве и оценить модели по всем трём критериям: скорость → качество на русском → ресурсопотребление. Для начала мы начали с эмбеддингов. За основу мы взяли ai-forever/ru-en-RoSBERTa. Он оказался победителем среди эмбедеров, так как у него значились:
Билингвальность (отлично держит контекст и в RU, и в EN сегментах);
Лёгкость (Она значительно быстрее тяжелых аналогов, что критично при пересчёте графа);
Качество (Высокая точность семантического поиска в задачах, похожих с нашими).
Мы также сравнили его с intfloat / multilingual-e5-base и sentence-transformers / paraphrase-multilingual-*: они давали хорошие метрики, но оказались слишком медленными на больших корпусах данных. Также мы рассматривали Giga-Embeddings и крупные instruct-модели, но они были больше ориентированы на генерацию, а не на быстрый векторный поиск.
Следующим этапом мы пошли к reranking. Здесь мы подхватили DiTy/cross-encoder-russian-msmarco, который идеально подходил под русскоязычный домен и особенно оказался хорош для финансовой тематики. Из плюсов у него значились, то, что модель уже оптимизирована именно под русский язык, что даёт прирост точности сортировки топ-кандидатов.
Cross-Encoder подход: Оценивает пару (query, chunk) напрямую, что точнее косинусного расстояния. Оптимизация: Лёгкая интеграция с кэшированием (_cached_predict) и батчингом. Здесь же мы также сравнили их с конкурентами IlyaGusev/cross-encoder-rubert-base-msmarco: Тоже отличная модель, но в наших тестах оказалась тяжелее и медленнее на CPU.
Мультиязычные Cross-Encoder'ы: На финансовой тематике и русском языке часто показывают результаты хуже специализированных.
Далее мы приступили к выбору LLM. Мы прогоняли множество моделей, которые нашли на HuggineFace и GitHub, в том числе и достаточно экзотических, но по ряду тестов остановились на Qwen3-MAX 8B. В целом, модель оказалась более чем хорошая, и это был оптимальный выбор для Query Expansion по ряду параметров:
Во-первых: Баланс (8B параметров) - достаточная генеративная мощность для качественного перефразирования, но при этом "влезает" в доступные GPU-ресурсы с хорошей скоростью;
Во-вторых: Модель корректно работает с финансовой терминологией, русским языком (критично для банка) и легко помещается в доступные GPU. Наконец, почему не GPT-4 / LLaMA-2-13B: Слишком ресурсоёмкие и медленные для интерактивного RAG (задержка ответа неприемлема). Маленькие модели (3B): Не обеспечивали нужного качества генерации и точности в финансовом контексте.
Испытания в поле и fine-tune на «горячую»
Наконец, мы подошли к самому затейному, а именно, описанию, как работает поиск (Step-by-Step). Итак, когда пользователь отправляет запрос, система запускает последовательность шагов:
Query Expansion: Qwen3-MAX генерирует 3–5 альтернативных формулировок и ключевые термины;
Parallel Search: FAISS (семантика), TF-IDF (точное совпадение текста);
Merge: Результаты объединяются;
Reranking. Cross-Encoder пересортировывает кандидатов и выводит наиболее релевантные; 5. Final Output. Возвращаются лучшие чанки.
На выходе у нас получился полный набор артефактов: graph.json – структура графа; vectors.faiss – индекс; embeddings.npy – эмбеддинги; chunks.jsonl – тексты; tfidf.pkl – TF-IDF; expanded_queries.csv – история расширения запросов. Таким образом, мы создали внутренний LLM-бенчмарк, который показал высокую корреляцию с метриками Альфа-Банка, причём система стабильно сохранила качество при переходе от публичного лидерборда к приватному, что говорит о хорошей обобщающей способности.
В дальнейшем мы хотели бы масштабировать нашу систему, “прикрутить” к ней чат-бот и аналитику. При этом уже у нас есть задел на масштабирование: FAISS-шардирование или переход на Milvus / Weaviate и инкрементальная индексация новых документов. По интерфейсу и чат-боту:
Vector Chat Memory (память диалогов);
Мультиканальность (Web, Mobile, мессенджеры);
Персонализация и объяснимость;
Подсветка источников.
По части аналитики: A/B-тестирование разных комплектов моделей, LLM-мониторинг качества данных, интеграция с внешними источниками знаний.
Работа над системой показала, насколько мощной становится связка RAG + графовые структуры. Благодаря графу мы не просто ищем похожие фрагменты – система понимает контекст, связи и смысловые переходы между частями знаний. С внедрением гибридного поиска и LLM мы получили гибкую, расширяемую и устойчивую платформу, которая уже сейчас превосходит классические RAG-решения по качеству. Мы смотрим на эту систему как на фундамент для будущих интеллектуальных продуктов.
Над статье и хакатоном работали: Миронов В.О., Шипилов Е.П., Кудрявцев Я.И., Кирнасов Н.Т., Апухтин В.Б., Дубовицкий Ф.В.
