Пример поиска
Пример поиска

Исходная идея

Часто приходится искать в огромной куче документов какую нибудь частную, специфичную вещь. На данный момент, только лично у меня более 2Gb различных pdf файлов. Зачастую разбросанных не системно. И хотя обычно представляешь где искать, но это отнимает время. Захотелось иметь инструмент ускоряющий поиск.

Для запуска всего этого есть сервер в домашней сетке
  • AMD Ryzen 7, RTX 5060ti 16Gb, 32Gb ОЗУ

  • Ubuntu 24.04, ollama v0.12.3. nvidia v580.95.05. CUDA v13.0, docker qdrant/qdrant:latest

Первая мысль: “a не до обучить ли какую ни будь сетку на данных из документов”.

Но первые же эксперименты показали, то даже просто формирования искусственного набора QA (вопрос+ответ) на 16Gb GPU не реально.

Формирование одной записи QA по одному чанку – это 3..10 сек. на каждый чанк нужно хотя бы 5 вопросов. И все надо вычитывать, поскольку каждые 4-я пара QA это либо бред, либо LLM уходит от темы. Для ~1Gb исходных данных это очень большие затраты.

Остался вариант "векторная БД + RAG"

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

Для кода был выбран python (v3.12.3)

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

Исходя из объективно/субъективных требований выбрал архитектуру

  1. Ollama с вызовом http+json API ('/api/embed' + '/api/generate') напрямую.

  2. Qdrant в виде docker контейнера (быстро и просто). Обращение через http+json api

  3. Хранение документов и картинок страниц на файловой системы и ссылками на них в payload записей Qdrant.

  4. Одностраничный сайт для поиска без идентификации/аутентификации (домашняя сеть же)

Так оно и оказалось. Написать ПО оказалось не долго. Действительно ничего принципиального сложного.

Хотя потратил кучу времени на создание одно страничного сайта.

Под впечатлением статей типа «как я бросил программировать и занялся Вайб-кодингом и заработал за 1 день несколько миллионов», решил принципиально создать одностраничный примитивный сайт вообще не трогая код и только вымаливая у LLM нужный мне вариант.

Я тряпка.

Я сдался после, наверное, всего 7-8 попыток. Когда объем промта стал близок к объему кода, а изначально сгенерированный LLM более менее приличный шаблон кода стал напоминать клубок переваренной спагетти. Наверное, на колени нужно было встать перед клавиатурой и вымаливать у LLM (не локальной) рабочий вариант. Взял в итоге один из сгенеренных вариантов и доделал руками за 15 минут.

А всего-то нужно было перенаправить в режиме stream поток токенов ответа от ollama через серверную часть сайта напрямую в поле html страницы, что бы была иллюзия интерактивности.

Я не знаю как вайб-кодеры, как они (утверждают "не поправляя код") умудряются создавать что-то более менее сложное, а потом это еще запросами к LLM допиливать. LLM это какой-то генератор банальностей и распространенных ошибок из интернета. Что-то простое - без проблем. Шаг вправо/влево от шаблонных решений и получаешь убедительный бред, который не работает и не может работать.

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

Ошибка выбора модели для эмбединга

Выбрал модель "embeddinggemma:latest".
Зря.
Модель, выложенная месяц/два назад, не обязательно самая хорошая. Что с ней не так – не знаю. Но время выдачи вектора эмбединга в ollama у нее не предсказуемое. От 0.2сек до 30 сек. Обработка чанков для тестового набора документов (300Mb PDF) заняла 4:28:39. В отличие от, например mxbai-embed-large:latest которая выполнила то же самое за 0:04:49. Хотя именно эта ошибка выбора сподвигла на дальнейшие эксперименты.

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

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

Эксперименты в песочнице с разными моделями

В статьях постоянно приводят же примеры прямо в jupyter notebook «вот 10 строк залили в chromadb, а вот сразу нашли нужный текст по запросу». Решил пойти по шагам от этого. Что бы понять почему на реальных данных все так красиво не работает.

Данные для тестов нагенерил конечно не сам (LLM), но вычитал и явного бреда там нет. (https://github.com/mmMikeKn/local-vector-db/tree/main/py_tests/tests_local)
Как оказалось, выбор модели эмбеддинга имеет решающее значение. И не нужно никому доверять в оценках модели. Нужно проводить тесты самому. Графическое отображение результатов тестов по моделям показался более наглядным чем просто обобщенные цифры.

  • Точка на диаграмме – это чанк в списке ответа на поиск. Оси: X - номер вопроса, Y - дистанция между вектором вопроса и вектором найденного чанков в топе выдачи.

    • Зеленый цвет – найденный чанк прямо соответствует вопросу (в тестовых данных). Остальные точки серые – это другие чанки в списке найденных. Просто для наглядности что бы оценить их "дистанцию" до вопроса.

    • Красный цвет – ни один найденный чанк не соответствует вопросу.

Признаки наилучшего поиска на диаграмме

  • Зеленая точка лежит ниже всех и максимально близко к 0 (ось Y). Т.е. "дистанция" между вектором эмбеддинга вопроса и ответа минимальная.

  • Серые точки лежат заметно выше зеленой – хорошая дифференциация ответов в списке найденных ответов.

  • Красный точек нет (ну или очень мало)

Результаты всем моделям можно посмотреть в https://github.com/mmMikeKn/local-vector-db/tree/main/py_tests/tests_local/plt

Все тесты сделаны на одном и том же наборе данных

Модель embeddinggemma:latest

Несмотря на то, что модель свежая, но она отвратительная по качеству. Размер 621 MB (размер привожу в том, сколько фактически займет GPU памяти. Для меня это важней, чем количество параметров), контекст 2048, вектор 768. Время расчета эмбеддинга не предсказуемо в широких пределах. От 300ms до 30 сек (!). Почему? Не знаю. Не вникал и не интересно. Есть из чего выбирать.

Диаграммы тестов
embeddinggemma EN
embeddinggemma EN
embeddinggemma RU
embeddinggemma RU

Модель (русскоязычная) evilfreelancer/FRIDA:latest

Просто не грузится в ollama с ошибкой INFO source=sched.go:438 msg="Load failed" model=/usr/share/ollama/.ollama/models/blobs/sha256-b94dd828625ffee6bd86723e18c5bf8b7a452d71d8d981dde45a974134c8bb03 error="llama runner process has terminated: exit status 2". Хотя находится в списке доступных для ollama и версия ollama, судя по описанию, подходящая. Не стал разбираться. Тем более, что у нее окно контекста всего 512

Модель evilfreelancer/enbeddrus:latest

Позиционируется как до обученная на русском. Размер 336MB, контекст 512.
Да простят меня авторы модели, но, по моим тестам, оказалась хуже мультиязычных схожего размера.

Диаграммы тестов
enbeddrus EN
enbeddrus EN
enbeddrus RU
enbeddrus RU

На этом закончил эксперименты с «русскими» моделями и занялся мультиязычными. Все они, кроме 'qwen3-embedding' оказались приблизительно похожи +/- по результатам.
Но явно видна корреляция между общим размером модели в памяти GPU (оцениваю по ресурсам GPU) и результатами. Чем больше, тем лучше результат. Что ожидаемо.

После всех экспериментов остановился на

Модель qwen3-embedding:latest

Довольно «тяжелая» модель 4.7 GB с огромным контекстным окном и размерностью вектора в 4096. Основной недостаток – высокое потребление ресурсов GPU. Слышно, как включается вентилятор на карте при обработке всех документов. На остальных моделях такого нет. Но время расчета вектора стабильное в рамках одного и того же размера чанка. Для чанка в 2-3Kb байт (условно) это где-то 1000-1200ms. Но полностью использовать огромное контекстное окно данной модели для данной цели не стоит.

  1. Сильно увеличивается потребление ресурсов GPU на больших данных контекста.

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

Диаграммы тестов
qwen3-embedding EN
qwen3-embedding EN
qwen3-embedding RU
qwen3-embedding RU
Нагрузка на GPU
Нагрузка на GPU

В результате модель "qwen3-embedding:latest" и была выбрана мной как рабочая для получения эмбеддинга по всем моим документам.
За ночь (удалил лог не посмотрев время) все обработалось и загрузилось в qdrant.

Мои впечатление и выводы от поиска по векторной БД и RAG

Восторженные статьи замалчивают недостатки как векторного поиска, так и RAG. В области AI вообще тренд "щенячьего восторга". Мне это всегда казалось сомнительным («..и вы то же говорите, что 10 раз за ночь») и собственный опыт это подтверждает. Да <сараказм>AI</сараказм> инструменты. Да удобные. Но вот не прям "ого..ого" с решением вопроса "42".

Контекстный поиск, совсем не заменяет поиск по подстроке

Если вам нужно найти что-либо очень конкретное, например, документ (страницу документа) со строкой вида «Требование 492», то вероятность, того, что страница с этим текстом попадет в топ результатов поиска по вектору контекста близка к 0.

Потому, что поиск по контексту и поиск по подстроке (конкретной) это принципиально разные вещи.

Следующее, что я буду прикручивать – это поиск по подстроке в чанках. К сожалению, qdrant на это плохо ориентирован. Но на 1-2Гб данных в конце концов можно и “grep” поискать. А задача типичная.

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

Нужно постараться, что бы за несколько попыток поиска с разными формулировками найти нужный документ (хотя бы документ, не говоря уж о нужной странице). И никогда точно не знаешь: не нашлось потому что просто нет, или потому что далеко по контексту для данной модели эмбеддинга.

Все что работает «как бы хорошо» в контекстном поиске по товарам Озона (хотя лично меня он бесит. А вас? Недоделка какая-то, а не поиск), очень плохо работает для задачи поиска в технической документации.

Теперь о RAG

Лично мне, он был не особо нужен. Достаточно было png страниц документов на экране для быстрой оценки и ссылок на документы для скачивания. Но прикрутить было не долго и прикрутил (отдельной кнопкой на странице сайта). Но:

  • Скармливать весь документ в контекст запроса – не тянет GPU 16Gb. А для облачных LLM быстро токены кончатся, если в контекст передавать весь документ.

  • Чанк, по реальному документу, это, зачастую, обрывки мыслей и с этим LLM не справляется. Что локальная типа Qwen3-Coder-REAP-25B-A3B-MXFP4_MOE-GGUF:latest, что чатботы «больших» LLM.

    • Несут банальности на которых обучались, игнорируя содержимое чанка.

    • Впадают в откровенный бред.

  • Результаты (ну если не фиксировать параметр options.seed в запросе к LLM) будут каждый раз разные. И никогда не будешь знать: каких то данных в ответе LLM нет потому что их вообще нет, или потому что механизм генерации очередного токена в LLM обошел нужное тебе ветвление предсказаний очередного токена.

В общем то, что "говорит" LLM по вопросу + текст чанка меня сильно разочаровало. Хотя возится и подбирать системные промты и прочее не стал.

Даже большие LLM иногда такой убедительный бред несут, когда их спрашиваешь про узкие/частные случаи, которых не было или было мало в исходных данных обучения. А искать приходится не "почему небо голубое?", а информацию которой просто нет в Интернете (служебные документы и т.п.).

Ссылка на проект [GitHub](https://github.com/mmMikeKn/local-vector-db)

Комментарии (11)


  1. Alex-Freeman
    06.11.2025 09:59

    Первая ступень, аж жабры зачесались)


  1. Mixael-L
    06.11.2025 09:59

    Текст надо вычитать, довольно много ошибок, причём типичных.


  1. Sequd
    06.11.2025 09:59

    У меня были примерно такие же выводы после первых попыток с RAG на "домашних" документах (счета/личные/выписки). В какой то момент мне показалось проще прикрутить elasticsearch. Как решить эти проблемы и дать понимание контекста, я еще не придумал...


  1. Ravius
    06.11.2025 09:59

    Да потому что каждый хочет написать статью и выложить. Раг раг раг раг... эта статья чем лучше "других"? Какую мысль она несёт? Не получилось?

    Ответ супер прост: метрики, метрики и мметрики. Глупость какая эти метрики..циферки которые мешают (сарказм)

    Далее, Смысл возвращаться чанк? Верните документ по чанку.

    А делать раг на эмбедингах одних - это 2023 год, ну 2024.

    Claude code и без рага найдет и ответит. Закинуть в гугл док свою библиотеку и спрашивать chatgpt.

    А вообще начинать нужно с оценки LLM закинули документ и спросили вопрос. Или спросили вопрос которого нет в документе.

    Ps там были какие-то метрики... но толку, как измерение "близости вопрос-ответ" вообще поможет? Есть классика: полнота возвращаемых документов, вернули 100% - значит дальше есть шанс получить правильный ответ. Вернули 10% - нет смысла дальше даже отвечать.


    1. mmMike Автор
      06.11.2025 09:59

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

      Далее, Смысл возвращаться чанк? Верните документ по чанку.

      Вы вообще хотя бы на скриншот в заголовке статьи обратили внимания?
      А обратили внимание, что я сказал, что RAG по результатам был "а просто прикручу попробовать" и вывод "что при таком подходе (просто по чанку) он не имеет смысла."

      Мне кажется, все ваши замечания явно не в тему. Вы точно под этой статьей коммент писали?

      Ps там были какие-то метрики...

      Ну точно.. "я это не читал, но не согласен" :)


      1. Ravius
        06.11.2025 09:59

        А вы статью как назвали?

        Ну точно.. "я это не читал, но не согласен" :)

        Я не согласен с метриками...они абсолютно бессмысленные...впрочем, это даже не метрики.


        1. mmMike Автор
          06.11.2025 09:59

          А вы статью как назвали?

          Статью назвал, потому что прикрутил к векторному поиску еще и обработку результата для реальных документов. И оценил, что для 16Gb GPU и реальных документов это не имеет смысла. К слову, для реальной документации и запихивание всего документа в облачную LLM то же не имеет смысла. Ну по крайней мере для меня и тех документов и задач что мне нужны.

          Считаете, что информация о том, что RAG на не больших чанках в локальных LLM не работает - это "очевидно для всех"?

          А то, в интернете полно примеров, когда вылизанные данные подставляются LLM и получается красивый ответ (для примера, наверняка подобранный за несколько попыток).

          Я не согласен с метриками...они абсолютно бессмысленные...впрочем, это даже не метрики.

          А что конкретно в метриках тестов разных эмбеддинг моделей не устраивает?
          Я столкнулся с тем, что модель (выбранная первая попавшаяся) выдала мне не релевантные ответы.

          Сделал тесты НА ОДНИХ И ТЕХ ЖЕ данных (случайно сгненеренных LLM). Все пары вопрос + "правильный" ответ весьма далеки от друг друга по смыслу. И "правильная" модель для вопроса и ответа из этого набора должна генерить "близкие" вектора.

          Весьма наглядно видно как на одних и тех же данных одна модель генерит эмбеддинг (вектор) вопроса близко к вектору правильного ответа. А другая нет.


  1. Kwentin3
    06.11.2025 09:59

    Open notebook есть на гитхабе, гляньте.


    1. mmMike Автор
      06.11.2025 09:59

      Мне нужна была просто поисковая система по контексту. Ну и еще по явным подстрокам вчера прикрутил.
      В первую очередь off-line (служебные документы).
      Но даже если не были служебными, то загружать около 200-300 pdf (2Gb) в облако для поиска по документам.. Да нет таких халявных сервисов.

      Я ее сделал для себя в виде готового персонального решения. Все.
      И я уже трачу меньше времени на поиски нужно документа и страницы в нем.
      Надо будет еще сделать выдергивание страниц из confluence (лично мне надо) и на этом лично моя задача будет закрыта.

      А прикрутить к результатам поиска RAG. Да просто не сложно было и все. Но результат без облачного LLM весьма сомнительный.
      Он сомнительный и с облачным LLM.
      Я пробовал запихивать техническую документацию и получать ответы на вопросы по ней. Результаты меня не устроили. Для моих целей. Лучше самому прочитать документ, чем целиком полагаться на то что LLM "скажет".


  1. Andreas_Fogel
    06.11.2025 09:59

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


  1. dibu28
    06.11.2025 09:59

    А модель ColbertV2 не пробовали для эмбеддингов? Я получил для себя лучше результаты чем на обычных dense моделях. Её можно подключить через библиотеку fastembed.