Привет, Хабр! Это Андрей Носов, AI-архитектор в компании Raft, проектирую и внедряю высоконагруженные RAG-системы на предприятиях. Сегодня я расскажу о вызовах, которые мы преодолеваем каждый день, создавая такие системы, и сделаю акцент на чанкинге.

Чанкинг — это один из методов, который позволяет представить текст в виде смысловых отрезков, с которыми далее будет работать LLM. Правильное деление на эти отрезки на входе и определяет в большей мере скорость, качество и цену работы всей системы поиска. Начнём с корня этой проблемы.

Обозначим проблему

Обозначим направления, в которых мы будем работать. Сегодня поговорим только о двух возможностях применения больших языковых моделей — это MedTech и LegalTech. Они наиболее востребованные на рынке в текущий момент в плане систем поиска.

Такой выбор направлений связан с глобальным трендом на работу с профессиональными знаниями, о котором говорят Gartner и OpenAI.

Это знания, которые нельзя просто так «загрузить» в LLM. Они узкоспециализированные, постоянно обновляются и содержат сложную терминологию. Если просто дообучить модель на таких данных, они «растворятся» в её общих знаниях, и извлечь их с нужной точностью будет практически невозможно.

Здесь на помощь приходят интеллектуальные системы, такие как RAG (Retrieval-Augmented Generation) и KAG (Knowledge-Augmented Generation). Сегодня подробнее рассмотрим RAG.

Особенности RAG, которые будем изучать:

  1. Высокая специфичность и терминология. RAG-системы позволяют индексировать документы с узкоспециализированной терминологией, создавая базу знаний, по которой можно выполнять точный семантический поиск. Это выгодно отличает их от общих LLM, которые могут не знать специфических терминов.

  2. Сложные структуры и иерархии. Мы посмотрим типы чанкинга, которые позволяют их разложить в виде описательных систем.

  3. Актуальность и версионность. Любую версию можно менять как минимум в двух вариациях, поддерживая либо версионность, либо обновляемость системы. Пару слов про термины. Под обновлением я подразумеваю необходимость просто добавить один документ, а версионность — это когда нужно пересобрать индекс целиком для того, чтобы дальше осуществлять поиск.

  4. Высокая цена ошибки. С помощью RAG мы нивелируем последствия ошибок, потому что имеем очень большую референциальную систему ссылок на основе метаданных, которые привязываем к индексам, чтобы найти нужный вариант с обоснованием. Таким образом, система как будто б�� делегирует всю свою ответственность обратно человеку.

  5. Контекстуальная зависимость. Поддерживается достаточно легко, но всё упирается в тип данных, которые поступают в систему, контексты и связи между этими данными. Эти проблемы напрямую отсылают к теме статьи — чанкинг.

  6. Отслеживаемость и проверяемость. RAG позволяет отследить источник информации, предоставляя ссылки на исходные документы. Это критически важно для проверки фактов и повышения доверия к ответам системы, особенно в юридической и медицинской сферах.

Области применения

Главные вызовы для RAG находятся как минимум в двух направлениях. Это юридические и медицинские ИИ-помощники, то есть LegalTech и MedTech соответственно. Задачи, которые нужно решить следующие:

LegalTech 

  • Анализ договоров 

  • Проверка соответствия нормативам 

  • Поиск прецедентов 

  • Поиск нарушений процедур 

MedTech

  • Поддержка принятия решений (CDSS)

  • Анализ медицинских карт (EHR)

  • Анализ клинических рекомендаций

  • Фармаконадзор

Частая задача, которая есть практически на всех предприятиях — это сократить издержки человеческого фактора при поиске несоответствий в договорах. На крупных предприятиях существуют отделы, которые отслеживают и ведут различные судебные издержки. В нашем случае такие прецеденты составляли экономию до 90%, то есть с условно 70 млн рублей заявленных до 700 млн рублей выявленных (цифры были выявлены в ходе анализа упущенной выгоды на одном из крупных промышленных предприятий федерального уровня, назавание которого по понятным причинам здесь не приводится).

В MedTech цена ошибки — это человеческая жизнь или отложенная человеческая жизнь, если говорить языком фармацевтов. В нашем случае фармацевты берут клинические рекомендации и рекомендации дополнительных сообществ для того, чтобы продвигать тот или иной фармпрепарат к применению. Обычно на эти процедуры уходит до двух лет. В кейсе, который будем описывать, система позволяет принимать решения и собирать консорциумы в автоматическом режиме, а человек в этом случае является контролёром, который решения аппрувит. Такую систему лечебно-этических комитетов мы буквально через пару дней будем представлять. Она позволяет сокращать путь и участвует во многих номинациях на текущий момент, как в GigaChat, так и в соседней конференции. Но здесь речь не об этом. 

Итак, с направлениями работы понятно, теперь нам необходимо выстроить правильные эксперименты.

Эксперименты

Для начала определимся с терминами. Они описаны на иллюстрации ниже.

Термины разберём дальше прямо на схеме.

Для эксперимента берём RAG-систему. У неё есть два основных пайплайна (плана действий):

Исходя из схем выше нам нужно:

  1. По пайплайну слева наполнить документами свою базу.

Мы берём определенный текст, который разбиваем на смысловые кусочки и правильно складываем в нашу базу данных. Обязательно с отсылкой к метаданным, чтобы у нас была фактологическая привязка.

Процесс выглядит следующим образом. Берём неструктурированный текст, разбиваем его какими-то подготовленными скриптами, которые написали сами или с помощью моделей. Далее нужно превратить получившиеся кусочки текста в машиночитаемый язык с помощью модели векторизации (о ней подробнее скажу чуть позже). После этого складываем полученный результат в базу знаний, это в нашем случае векторная база.

Первый этап пройден, документы готовы. 

  1. Далее нам нужно по пайплайну справа понимать сообщения пользователя, чтобы их можно было сравнивать с теми документами, которые лежат в базе.

Этот этап идёт уже в онлайн-режиме. Когда пользователь делает свой запрос, нам нужно превратить запрос в некое векторное пространство. У нас работает или обычная модель векторизации или адаптивная модель векторизации, в зависимости от того, какое решение мы приняли. Далее срабатывает Retrieval-пайплайн. В данном случае это пайплайн сравнения поступающего вектора и вектора, который лежит в базе данных. После этого на этапе сравнения извлекается ряд похожих векторов из базы данных, и мы имеем так называемый Top-k, который является отражением всех знаний, связанных с вопросом пользователя. По сути, это контекст, который мы подаём в LLM-модель для того, чтобы она уже приукрасила финальный результат.

Какие узкие места здесь сразу видны?

  • Если у нас на этапе чанкирования смыслы извлечены неверно в первом или во втором случае

Соответственно, вся модель уже будет по Recall всегда показывать неверные чанки, то есть мы никогда не найдем те чанки, которые нам нужны. Ответ будет априори неверный.

  • Если мы применили неправильную векторизацию

Что это значит? В данном случае мы сильно сжали векторные смыслы и получили достаточно сжатые представления. Из-за этого у нас пропадают синонимия, антонимия и прочие дополнительные лингвистические особенности.

  • В плане извлечения контекста

Если мы задали модели недостаточно контекста, когда получили Top-k, или этот контекст был изначально сломан, то генеративная модель падает в очень низкую метрику Faithfulness, то есть галлюцинирует. И финальный ответ заранее становится неудачным.

Теперь вы видите, сколько нужно пройти правильных шагов, чтобы поиск случился верным. Самый первый из этих этапов, конечно, чанкинг — ему сегодня отведена особая роль.

Почему мы выбрали RAG, а не подавали всё в контекст

Вы спросите, зачем нам этот чанкинг, у нас же языковые модели и так большие, у них большой контекст? Зачем эти мучения с хождением по нескольким шагам — берём целый документ, закидываем в модель и спрашиваем всё, что нам нужно.

Но есть маленький нюанс.

Длинный контекст страдает от очень многих вещей.

  • Точность и фактология

  • Работа с актуальными и специфичными знаниями

Точность и фактология ограничены обучением модели. Сейчас большинство LLM сфокусированы на 2023 годе, то есть буквально отстают на два года по изучаемым фактам. Ни в LegalTech, ни в MedTech эта задержка неприемлема, потому что такие ответы вызовут очень большие жертвы.

  • Масштабируемость базы знаний

Масштабировать такую базу знаний достаточно тяжело. Нам придется отфайнтюнить целую модель. Обычно модель размера GPT учится за 6 месяцев. Если у вас есть время каждые полгода делать тонкую настройку всякий раз, когда вы хотите туда внести какое-то изменение, я вам завидую.

  • Задержки (legacy)

При всем прочем мы имеем, конечно, большие задержки. Если у нас недостаточно мощности и GPU, ответы будут прилетать крайне медленно. 

Дополнительные проблемы:

  • Стоимость: вычислительная и API

  • Цитирование и проверяемость

  • Ограничения «дообучения»

  • Сложность настройки конт��кста

Все мы их встречали, когда пользовались большими языковыми моделями, особенно если использовали zero-shot-промты.

Из-за этих проблем мы и выбрали RAG.

А теперь немного о том, как всё работает на практике.

Кейс из MedTech: поиск обоснований в клинических рекомендациях

В кейсе MedTech всё выглядело следующим образом.

Я вкратце уже рассказал, что у нас есть рекомендации, которые поступают на вход, их нужно сравнить с теми, что лежат в базе, и LLM должна выдать структурированный ответ на основе шаблона, который заложен у неё в промте. Этот паттерн является одновременно рекомендацией к действию.

Чтобы получить такой результат в реальной жизни, приходится собирать до 5−6 комиссий в год, чтобы провести все согласования. В данном случае этот процесс заменён коротким пайплайном.

Проблема здесь состоит в том, что врачу нужно найти подтверждение для диагноза на сотнях страниц рекомендаций. Обычно материалы врачам поступают пакетами, в которых содержится до 10 документов, каждый из них объёмом до 310 страниц. Человеку под силу прочитать столько, наверное, за год. Именно поэтому они делают свою работу медленно, а мы ускоряем как минимум в 5 раз этот процесс, учитывая все дополнительные рутины.

Кейс из LegalTech: анализ юридических предписаний (Compliance)

В LegalTech есть процедура сравнения с требованиями 115 ФЗ. Есть риски по каждому договору, которые необходимо выявить на этапе заключения. Чтобы найти слабые места, необходимо прочитать документ со всеми приложе��иями. Как правило, — это примерно 300 страниц. Сам документ может быть небольшим, но приложение к нему со всеми схемами сильно увеличивает объём, и чем больше проект, тем больше листов в дополнительных материалах.

Проверить такие документы вручную, конечно, сложно. Люди выполняют проверку настолько качественно, насколько могут, но ошибки здесь неизбежны. Потери до 90%, о которых я говорил в самом начале, случаются практически у всех. Это незаметная часть айсберга, которая всплывает на поверхность, когда мы ускоряем процесс и видим все нюансы. 

Как оценить качество работы RAG

Давайте отвлечёмся и поговорим о том, как оценить насколько вообще эффективно работает RAG.

У нас есть процессы, но как нам понять, что система работает также как человек, лучше, или хуже его? В этом случае мы используем так называемые автометрики.

Базово мы используем принцип RAGAS, который позволяет выявлять 4 основных автометрики, формируемых на основе создания золотого бенчмарка.

Поясню, что такое золотой бенчмарк. Мы создаем некий датасет, в нашем случае из 100 единиц по 3 задачам. Далее рассмотрю, как он выглядит, чтобы у вас была полная картина. Этот датасет загоняем в CI/CD pipeline. Каждый раз, когда мы пушим новое обновление, происходит сравнение с этим бенчмарком, выявляются основные метрики, и по этим метрикам мы понимаем, насколько хороша наша система.

Сам бенчмарк помогают пр��ектировать врачи или юристы. Так мы отсекаем неактуальности материалов или вопросы, почему метрики отклоняются от заявленных. Если нам нужно обновить бенчмарк, это занимает немного времени, его просто поправить в текстовом режиме.

Выглядит бенчмарк как схема, с тремя уровня сложности принятия решений. Покажу на примерах, как они выглядят.

Есть запрос, ожидаемый ответ и ID чанков. 

ID чанков нужны, в первую очередь, как метаинформация для того, чтобы проверять фактологичность всей системы. Нам нужно понимать, откуда система взяла информацию, из какой клинической рекомендации, из какого договора, потому что все договоры и рекомендации могут в базе пересекаться. ID чанков в этом случае помогают вернуть историю в своё русло.

Усложнение задачи идёт по принципу добавления новых атрибутов, которые мы выявляем.

Самая сложная задача заключается в том, что нам необходимо получать еще какой-то логический output, то есть выявлять логическую структуру и на её основе сделать вывод. 

Но ещё раз подчеркиваю, что это только структура логики, а не структура семантики смысла, то есть больше структура документа.

Аналогичный бенчмарк у нас создан для LegalTech. Как пример:

Когда мы начинаем работу с заказчиком, обычно реализуем подобные задачи, чтобы появилось доверие к тому, что мы делаем.

Базовый RAG пайплайн 

Вспомним базовый пайплайн, который был выше.

Напомню, что в RAG есть два пути: сначала нам нужно сложить документы в векторизованном виде в базу, а затем прогонять каждый раз Retrieval для того, чтобы сравнивать с этой базой. Обычно в основе всех систем лежит чанкинг на базе fixed size. Это когда берут большое сплошное полотно текста и рубят его на несколько равных частей. При этом все смыслы перемешаны и нарушены. Это абсолютно против логики, но, тем не менее, до сих пор является единственной стратегией, которая имплементирована и применяется.

Что даёт эта стратегия в базовом режиме?

  • Низкий Recall – теряем важные чанки

В нашем случае Recall близится к тому, что мы выявляем чуть больше нужных нам чанков, чем ненужных.

  • Низкий Precision  – «мусор» в контексте

В плане precision получается паритет, то есть наша точность «угадали - не угадали».

  • Faithfulness – LLM «выдумывает». Что касается галлюцинаций (Faithfulness), то при получении нерелевантного контекста LLM склонна его игнорировать и генерировать ответ на основе своих общих знаний. Это приводит к тому, что ответ выглядит правдоподобно, но не основан на фактах из исходных документов. В наших экспериментах метрика Faithfulness падала до 30%. Такая ситуация подрывает саму идею RAG — фактологическую точность — и возвращает нас к проблеме «длинного контекста», когда мы не можем проверить, откуда модель взяла информацию.

Но как только мы углубляемся и более осознанно начинаем разбираться, а где же тогда ломается запрос, какие метрики в нас прилетают, всё становится предельно ясно. Представим себе небольшой базовый документ, где вверху есть подстрочники с текста, где-то в серединке вставлена таблица, внизу стоит подпись  —  обычный стандартный документ. Здесь есть, где сломаться, есть три объекта.

Если мы возьмём обычный docling и вытянем оттуда всё текстом, у нас получится каша слов со спецсимволами, который мы тут же разрубим ещё по кусочкам. У нас и так был фарш, а здесь получится фарш вдвойне, и мы просим LLM, опираясь на него, ответить правильно. В этом случае  всё будет не так, ведь нарушены три основных закона:

  1. Не учитывается мультимодальность

Каждый элемент — текст, таблица, подпись — это отдельная модальность, которую нужно обрабатывать отдельным объектом, отдельным леером, отдельным layout’ом.

2. Неоднозначные запросы

Здесь как будто не учитывается семантика, значит, существует неоднозначность. 

3. Синонимия

Синонимия очень сильно влияет на выдачу. Чтобы нивелировать это влияние, нужна какая-то иерархичность и преемственность.

Опираясь на эти выводы, мы решили поэкспериментировать.

Перед опытом, как всегда, встала дилемма. Мы хотим получить максимально качественный результат, но это может потребовать слишком много вычислительных ресурсов. Наша задача была сохранить баланс между качеством и Latency (временем ответа системы).

Наша цель — не только повысить качество ответов, но и сохранить низкую задержку и высокую производительность (RPS, Requests Per Second). Для этого мы провели оптимизацию по всем компонентам RAG-пайплайна.

  • Улучшение Embedder
    intfloat/multilingual-e5-large → ai-forever/FRIDA

Мы отказались от стандарта использования для эмбеддингов. Перешли на FRIDA. GigaEmbeddings сейчас просто его в себя включает и становится ещё более классным, но тяжёлым, к сожалению.

  • Настройка Vector DB
    Faiss → Chroma → Weaviate

На старте мы отказались от супергибкого Faiss, потому что всегда есть опасность, что плохо считая, мы настроим неправильную индексацию. Плюс Faiss — это просто индекс, а не база, а нам нужна была именно база с последующим шардированием, репликацией, чтобы можно было сделать какое-то продакшен решение. Chroma здесь тоже был не вариант, это был промежуточный шаг.

В итоге пришли к Weaviate. Здесь можно в один ряд, наверное, поставить Weaviate, Milvus, Qdrant, у них плюс-минус одинаковый функционал. Qdrant — это единственная база, которая имеет промышленный апрув, все остальные — ресёрчи. Мы остановились на Weaviate, потому что он хорошо совместим с Gemma, а Gemma имеет классную обобщающую способность по русскому языку, которая впоследствии нам даст внушительный прирост качества ответов.

  • Модели и промпт-инжиниринг
    Qwen 2.5-7B→ Llama 3.1-8B → Gemma 3-4В

Также мы улучшили промпт-инжиниринг, поработав с тремя моделями: Qwen, Llama, Gemma. Наверное, вы заметите, что они маленькие. Я люблю маленькие модели, а для наших задач эти размеры подходили. 

  • Оптимизация чанкинга
    10 стратегий

Переходим к самому главному — оптимизация стратегий чанкинга. Мы выявили 10 стратегий:

Десятую (гибридную) мы нарисовали сами, часть взяли из LangChain, часть обсуждали с коллегами по цеху. Сегодня по ним пробежимся, посмотрим плюсы и минусы, и какой гибрид в итоге мы собрали.

  1. Фиксированный размер

Здесь стоит обратиться сразу же к лингвистике — о, как неожиданно! Еще в начале ХХ века в Пражской лингвистической школе был разработан принцип актуального разделения предложений на две основные составляющие:

  • Тема — объект, 

  • Рема — всё, что его описывает, все дополнительные характеристики. 

Правильное членение заключается в том, чтобы выявить тему и вокруг неё навесить как можно больше ремы. Здесь полнота чанка определяется полнотой ремы. Если мы справились с задачей хорошо, у нас максимально качественные, осмысленные кусочки, которые мы будем искать, и искать их становится действительно осмысленно понятно.

Если мы делаем деление по фиксированному размеру, допустим, «моя кошка классная», мы выявили объект (кошка) и её рему (классная), но упустили «и я её люблю». Любовь на самом деле тоже является частью ремы, но мы её выкинули. В данном случае у нас будет лежать отдельный чанк «и я её люблю», её, может быть, и не кошку. 

С такими потерями мы мириться не хотели, и поэтому стали искать другие стратегии.

2. По предложениям

Естественный путь — это просто поделить текст по предложениям, называется этот метод sentence-based.

Сложность в том, что мысль может раскрываться не в одном предложении. Например, у Льва Толстого она развивается на 300 страниц и в одно предложение слегка не вх��дит. Это шутка, но в договорах мысль тоже развивается на несколько предложений, далеко не всегда содержится в одном. Получается, что здесь тоже теряем рему, если разделяем таким образом. Но не всегда. Всё зависит от входящих данных. Если ваш дата-аналитик сказал, что у нас вполне себе взвешенные предложения, почему бы нет? Это очень классный лёгкий способ.

3. По смыслу

Следующая стратегия оптимизации связана со смыслом. Это уже очень близко к тому, как думает человек. Допустим, нам нужно собрать рему вокруг одной темы, и мы это делаем.

Мы берём одно предложение и сравниваем его со всеми остальными. Дальше по определённому порогу формируем кластеры близких предложений. У нас получаются близкие кластеры, и мы говорим, что каждый кластер является отдельным чанком. То есть набор близких предложений является отдельным чанком. Когда мы найдём в нашей базе плюс-минус соответствие, это будет хорошо.

Плохо будет, когда у нас ответы содержатся в разных абзацах или в разных документах. Я уже говорил, что мы любим экономить на пространстве и кладём несколько документов в одну базу. В этом случае на выходе будет путанная информация, хотя и близкая по смыслу, как будто бы ответ есть, но в нём отсутствуют важные детали.

4. Рекурсивный чанкинг

Рекурсивный чанкинг немного исправляет в базе чанкинг фиксированного размера. Здесь происходит следующее.

У нас есть определённая длина, которую мы задаём в токенах. К этой длине мы пытаемся собрать чанки в определённом порядке. Мы берём хорошо структурированный документ — есть выделение глав, подглав, параграфов и прочих вещей и рекурсивно проходим его, пока не набирается длина 500 по каждому кусочку. Так мы строим псевдо-иерархическую модель, которая нам даёт понимание, как документ выстроен. Но при этом из-за того, что мы сократили длину, всё равно часть смысла оказалась утеряна. Поэтому нам этот способ тоже не особо подходит, кроме случая, когда мы имеем действительно хорошо структурированный документ.

5. Скользящее окно 

Скользящее окно тоже призвано исправить огрехи fixed size. Идея такая. 

На скользящем окне добавляем к fixed size контекст. То есть у нас как будто бы создаётся дополнительный контекст и проходит окошечком. Функционал overlap (наложение окон) создает иллюзию, что мы еще и никакую информацию не пропустили. Но проблемы всё те же самые, что и в fixed size — связность.

6. Иерархический чанкинг

Самый древний способ — иерархический. Он заключается в том, что мы сначала строим антологию: граф, гипертекст или ещё что-то, как это все в интернете выстроено. Именно оттуда это всё сюда перекочевало.

Но строить такие системы дорого, ненадёжно, вычисления на графах является MP задачей до сих пор. Всё это отталкивает, чанкинг таким образом пр��водить достаточно дорого. 

7. По темам

Также можно чанкинг делать по стохастике. Когда определяем смысловые поля, какой-нибудь алгоритм например LDA, как в данном случае,  позволяет сделать тематическое моделирование.

Так достаточно просто вычислять смысловые чанки, чтобы можно было их использовать. Мы далее применим эту идею: базово пройтись сначала по определению, кластеризовать весь наш документ, а только потом принимать какие-то решения по нему. В оторванном виде это ещё чанкинг не до конца. 

8. По модальности

Рассмотрим документ, с которого начинали, он состоит из трёх частей: текст, табличка и подпись. Вот как можно провести здесь чанкирование по модальности:

Нам понадобится модель для анализа изображений и текста, например, BLIP (Bootstrapping Language-Image Pre-training), чтобы сначала выделить layout — структурные области документа, в которых мы затем уже будем описывать содержимое. Такие зоны могут быть текстовые, табличные, картиночные и многие другие.

  • Текст. Здесь все понятно — мы «направляем» на него парсер и извлекаем текст.

  • Таблица. Сначала нужно понять, является ли таблица текстовой («читаемой») или представляет собой изображение («нечитаемой»). Если она читаемая, проходим парсингом с помощью библиотеки unstructured. Библиотека docling здесь не подходит, так как может выдавать артефакты. Unstructured на выходе дает markdown, который мы спокойно потом можем передавать в модель — она любит этот формат.

  • Изображение. Здесь мы, конечно, текст с картинки не читаем, поэтому делаем описательную часть этой картинки. Именно описание потом пойдёт в LLM.

Таким образом, у нас со всех трёх layout получается сборная информация со всеми описаниями, которые мы кладем в JSON и дальше передаём на получение эмбеддинга.

Далее две самые интересные стратегии чанкинга.

9. Агентный подход

Этот подход можно реализовать, создав собственного агента на основе workflow-движка. В таком workflow можно настроить узлы (ноды), каждый из которых будет отвечать за определённый тип чанкинга из тех, что я перечислил выше. У каждого типа текста будет свой workflow, по которому агент будет ходить и автоматически определять наилучшую стратегию чанкинга. Классно, дорого, богато, но настраивать трудно.

.

Пока мы только пробуем этот подход, но результаты не очень хорошие. Вероятно дело в том, что модели, которые мы используем на выходе, либо чатовые, либо инстрактовые, а это диалого-ориентированные модели. Если их использовать на документо- ориентированные части, то случаются очень низкие выбросы по Faithfulness, которые мы увидим чуть дальше. Но пока что это наша гипотеза.

Итак, барабанная дробь!

10. Гибридный чанкинг

Достаточно простой подход, который мы реализовали. 

Мы взяли на входе эвристики в вид�� регулярных выражений, которые позволили нам определить структуру документа в виде глав. На первом шаге мы определяем логическую структуру. Дальше провели тематическое моделирование и кластеризацию. После этого спускаемся в семантическую структуру в тот самый LDA. Или прикручиваем небольшую модель-классификатор, допустим, RuBERT, которая будет определять, хороший это или плохой чанк. Это очень маленькая модель, мы сразу встраиваем её, чтобы сохранить ресурсы и низкую latency. Модель определяет, правильно или неправильно разделён тот или иной кусочек. Таким образом, мы предсказываем два выхода: плохое разделение или хорошее разделение. Это помогает нам двинуться дальше.

Гибридный подход у нас вполне быстрый — посмотрим, насколько эффективный.

Results

Мы провели 10 экспериментов на наших реальных кейсах.  Обязательно смотрели сразу по трём метрикам (напоминаю, автометрики по CI/CD прикручены) и latency.

Наиболее перспективными на наших типах документов в LegalTech и в MedTech показали себя следующие типы:

  • Semantic-based

  • Иерархичный — тот, который сложный в предобработке из-за составления антологии)

  • Наш гибрид.

Осталось всё это упаковать в структуру, как и было заявлено, в HighLoad архитектуру.

Подробно об интеграции в HighLoad архитектуру

Для этого нам понадобится несколько инструментов, так называемых кубиков. Мы берём эти кубики, имплементируем. 

Сначала необходимо сделать переиндексацию.

Здесь понадобится эмбеддер и база данных, которую версионируем и в которую будем складывать индексы.

На втором этапе нужно реализовать пайплайн Ingestion, где документы необходимо предобработать и обработать.

Для предобработки у нас стоят базовые лингвистические библиотеки. 

Для парсинга используется docling. Если документ сложный, то unstructured. Потому что в Docker docling весит 700MB, а unstructured 16. Мы его пробовали ужимать, ужали до 6, но с небольшими потерями. Когда TF пережали в TFLite, получили потери.

Дальше нужно настроить очереди для того, чтобы хорошо всё работало.

Мы понимаем, что в модель подается всё в режиме батчинга, синхронный режим она кушает хорошо, поэтому, асинхронный мы сделали только для функций, которые исполняются. Все очереди у нас работают синхронно, потому что иначе происходит зажёвывание контекста.

После этого мы подумали о чанкинге.

Кэширование и дедупликация нам позволили сэкономить на том, чтобы не гонять запросы туда-сюда по 10 раз. Мы понимаем, что документы типовые, значит, и запросы по ним будут типовые.

По итогу мы настроили базу Weaviate, которая нам позволила из коробки добавить и шардинг, и репликацию.

В итоге мы имеем высоконагруженную систему, которая позволила на сегодняшний момент реализовать достаточно крупные кейсы, которые выходят на уровень министерств для того, чтобы можно было их дальше масштабировать.

И помогли нам в этом Weaviate, Gemma и немножко удачи. Несколько советов:

  1. Hybrid и Hierarchical — лидеры для сложных документов.

  2. Weaviate и Gemma — чемпионское сочетание.

  3. Проектируйте систему с учётом масштабируемости и асинхронности.

  4. Измеряйте метрики для итеративной оптимизации.

Скрытый текст

Весной 2026 года снова встретимся на HighLoad++ — главной конференции о высоконагруженных системах, где собираются инженеры, архитекторы и те, кто двигает индустрию вперёд. Вас ждут доклады от практиков, живые разборы сложных кейсов и обмен опытом без лишнего официоза. Присоединяйтесь, чтобы узнать, как масштабируют сервисы миллионы пользователей — и поделиться своими находками!