Делай RAG: опыт создания векторной базы знаний

Делай RAG: опыт создания векторной базы знаний

Ekaterina Yakunenko, канал @delay_RAG
Изображение создано с помощью нейросети Nano Banana Pro

Эта публикация — это объединённая в один лонгрид серия постов о том, как я работала над RAG-базой для своего бота, проверяющего рекламные креативы на соответствие ФЗ «О рекламе». Пайплайн-воронка, его основные этапы, особенности кода и стоящей за ним логики во всех подробностях.

Этапы пайплайна

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

Я попросила Claude описать функциональность скрапера как-то кратко, но максимально по сути, и он выдал «интеллектуальный веб-краулер с функцией инкрементального обновления данных». Имеется в виду, что скрапер умеет собирать дела за указанный пользователем временной период, причем как смотрит новые (отсутствующие в базе), так и отслеживает появление новых решений в уже известных делах.

Здесь нужно небольшое пояснение. ФАС структурирует базу данных (по крайней мере в части рекламных дел) так: создает карточку дела, которой присваивается UUID (подробнее об этом здесь). Внутри карточки с делом размещаются документы по делу отдельными страничками (определение о возбуждении, решение, предписание и т.д). Соответственно, в разных моментах времени эта карточка может быть пустой или содержать разное количество документов. Меня для целей сборки RAGа интересуют только карточки с решениями. У каждой карточки с документов есть свой UUID (в моих рабочих файлах он обозначен как docID).

Скрапер делает поисковые запросы по 4 фильтрам, затем выкачивает тексты решений в .txt и попутно ведет лог с записями всех caseID и относимых к ним docID. Это если кратко, более подробно расписанные этапы приведены в pdf-аттаче к этому посту в моём канале. 

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

I - Выше упоминала, что поиск ведется по 4 параметрам, а именно

1) Управление Управление контроля рекламы и недобросовестной конкуренции

2) Процедура Реклама

3) Сфера Рынок рекламы

4) Доп. критерии реклама

Подавляющее большинство проходит в параметре «процедура», но далеко не все: рекламный кейс может заваляться и по другим параметрам (например, здесь, в параметре процедура указано «КоАП», а я по нему не проверяю).

Примеры неоднородного ведения базы ФАС, в частности, заполнения поля "процедура"

II - В подавляющем большинстве дел текст решения выкладывается на странице, и скрапер его собирает и создает на моем компьютере txt-файл. Но некоторые УФАСы выкладывают тексты решений в других форматах, которые подвешивают в карточку отдельным аттачментом, оставляя основной текст страницы пустым или заполненным какой-нибудь фразой. 

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

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

1️⃣ Собственно, первый вы уже поняли: нужно изучать, как устроены сайты, с которых вы хотите что-то скрапить и парсить. Нужно и глазами покопаться, и призвать на помощь нейросети: я просто сохраняла в браузере html-файлы с основной страницей поисковых фильтров, со страницей дела (где есть caseID) и страницей решения. Так и нейросети будет легче составлять код скрапера.

2️⃣ Просите закодить небольшую паузу в посылании запросов, чтобы сайт не решил, что на него не совершается DDOS-атака, и не выдал бан вашему IP (особенно если это какой-то реальный IP, с которого вы по работе часто взаимодействуете с сайтом).

3️⃣ Если для каких-то целей вы используете сервисы из трех букв, то на время скрапинга баз решений российских гос.орагнов ковровую активность этих сервисов придется приостанавливать — будьте к этому готовы.

4️⃣ Используйте обязательно логи того, где скрапер побывал и что скачал — если будут какие-то прерывания в соединении, или вы будете скрапить с какой-то регулярностью, это будет совершенно бесценной информацией, экономящей очень много времени.

5️⃣ Помните, что структурированные базы — это вообще-то охраняемый результат интеллектуальной деятельности, и полезно на всякий случай провеять режим допустимого использования. У ФАС об этом вот здесь, например:

Материалы сайта ФАС России являются общедоступными и открытыми для использования в некоммерческих (личных, ознакомительных, образовательных, исследовательских и аналогичных) целях.

Начинается превращение из «гусеницы», сырого текста решения ФАС со всякой не самой важной для целей бота информацией, в идеальную математическую бабочку-эмбеддинг. Начинается превращение с разметки

Следующий компонент кода пайплайна отправляет каждый выскрапленный текст решения УФАС в нейросеть по API. Почти каждый, как упоминала в выше: код проверяет, точно ли там есть какой-то стоящий траты токенов текст (соломоново решение отправлять тексты из txt-файлов более 100 кб).

Если вы знаете, что такое API и зачем это нужно, процитированный блок можно пропустить.

API (Application Programming Interface) — это такой способ организовать взаимодействие созданной вами программы и какого-то другого сервиса. То есть я обращаюсь к нейросети не привычным образом через чат в браузере, а мой скрипт автоматизированно отправляет ей шаблонные запросы, она возвращает определенным образом структурированные ответы. Так и бот работает, к слову. Главная выгода использования API в моём кейсе — скорость, масштаб и сохранение качества (каждый запрос — как новый чат, не замусоренный контекстом). На данный момент я прогнала уже несколько тысяч текстов, и без скрепленного API союза нейросети и скрипта для такого же результата понадобились бы месяцы непрерывной отупляющей работы.

Промпт ставит нейросети несколько задач по анализу кейсов:

Задача 1: Первичная проверка релевантности ФЗ «О рекламе»

Поскольку, как вы помните, базу данных ФАС заполняют люди, а скрапинг идет по 4 фильтрам, в этих фильтрах оседает всякое не относящееся к рекламе, прежде всего, из дел по госзакупкам. Нейросеть первым делом определяет, относится ли дело вообще к ФЗ «О рекламе», и если нет, то модель должна вернуть минималистичный JSON с флагом is_relevant_to_ad_law: false и коротким объяснением причины, не тратя ресурсы на дальнейший анализ.

Если вы знаете, что такое JSON и зачем это нужно — процитированный блок можно пропустить.

JSON (JavaScript Object Notation) — это текстовый формат для структурированного хранения данных, который организует информацию в виде пар «ключ:значение». В приведенном выше тексте ключ — «is_relevant_to_ad_law», а значение — «false». Направляя каждый API-запрос, я запрашиваю одну и ту же информацию, и мне нужно, чтобы нейросеть её располагала не беспорядочно, как токены лягут, а именно структурированно. Если по ключу «is_relevant_to_ad_law» значение становится «true», то будет дальнейший анализ, где ключей уже намного больше. Из таких пар «ключ-значение» легко собираются таблички. Но таблички — это лишь вершина айсберга, в этих массивах легко можно вылавливать другие данные с помощью скриптов без парсинга всего текста.

Задача 2: Оценка ценности дела для RAG-базы

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

I - Мой бот проверяет только субстантивную часть, а всё, что касается каналов размещения и прочих нюансов реального мира (лицензии, соответствие рекламным тезисам реальному положению дел) — не трогает. Поэтому и в базе знаний этих дел мне нужно (забегая вперед скажу, что соотношение «субстантивные»-«технические» в практике делится год от года примерно 60/40 в пользу то одной категории, то второй).

II - У меня есть убеждение, что хорошему RAGу по нишевому юридическому вопросу не требуются десятки тысяч документов. Качество retrieval не станет сильно лучше, если в базе будут сотни написанных под копирку дел о том, что слова «парилки ашкудишки» на окне табачной лавки являются запрещенной рекламной табака. Нужны дела прецедентного характера, где УФАСы на высоком уровне юридической аргументации рассуждали о тонких, интересных, неочевидных вопросах, за которые мы так любим рекламное право. Без таких дел в базе, на мой взгляд, мой проект не имел бы смысла.

Исходя из этих соображений нейросети повелено делать грейдинг (регрессию?) ценности дела для базы знаний от 0 до 10 по обозначенным мной критериям. Подробно о критериях рассказано в pdf-аттаче к этому посту, а если коротко, то:

0 баллов получают все дела по вопросам размещения рекламы в материальном мире (то есть нерелевантные задачам бота);

1-3 балла — банальные прямолинейные нарушения простых требований и запретов (пиво рекламировали, дисклеймеры на БАДы забыли и т.д.);

4-7 баллов — комбинации нарушений, нарушения по определенным видам продукции, достаточно очевидные нарушения, не требующие великой аргументационной мысли, но по важным статьям;

8-10 баллов — все, что задает тон всей практике, где нет очевидного решения, пограничные случаи, слои метаиронии, всякое интересное и дающее смысл профессии.

А зачем мне нужны все эти баллы — будет ниже об этапе ручного отсмотра.

Задача 3: Структурированное извлечение данных

После оценки нейросеть извлекает ключевую информацию и составляет из неё JSON. Моделька вытягивает дату решения, название УФАСа (раньше это делалось скриптом, но при сборке пайплайна кое-что отвалилось и пришлось заставлять модель), информацию о нарушителе и отрасли, в которой он работает, детальное описание содержания рекламы с прямыми цитатами её описания, платформу размещения, суть нарушения, аргументацию ФАС с сильными цитатами из решения, а также список нарушенных норм (пункт-часть-статья) ФЗ О рекламе.

Пример структурированного в JSON анализа нейросети

Этап ручной проверки и сборка таблиц — это в сущности один и тот же этап, но сборка таблиц технически реализована достаточно нетривиально.

Центральная функция этой части кода — собрать таблицу с релевантными для RAG (субстантивными) делами. Каждый ключ в JSONе становится колонкой таблицы.

Затем таблица с релевантными делами дополняется скриптом тремя колонками:

I - колонка с полным текстом решения ФАС из папки, в которую помещаются все txt с решениями на этапе скрапинга;

II - колонка с caseID. Как упоминала ранее, в базе решений на сайте ФАС используются UUID двух типов: для карточки с делом и для отдельных документов. Бот предлагает вам ссылки именно на карточку с делом, она проще формируется, чем ссылка на текст решения (т.к. в ссылке на текст решения есть компонент с названием УФАСа). Но в нейросеть для разметки отправляется файл с ID документа в названии, и JSON сохраняется тоже с docID (см. скриншот выше – в строке с названием окна указан именно docID).

III - колонка с «тематическими тегами». Тематические теги — это описание статей из ФЗ «О рекламе» максимально короткими ключевыми словами по типу «некорректное_сравнение», «БАД_дисклеймер». 

Выдержка из маппинг-справочника тематических меток (тегов)
Пример заполнения csv-таблицы тематическими тегами

Основной функционал этих тегов — они помогают оценивать равномерное наполнение базы знаний кейсами разной тематики (чтобы не было перекоса, например, в рекламу финансовых услуг, и было побольше решений по темам, которые достаточно редко затрагиваются (например, правила о государственном языке)). У тегов есть еще и задел на будущее — они будут основой для reranking-а, который я планирую реализовать для повышения точности выдачи кейсов из базы, связью с кластером эмбеддингов для руководств ФАС, который я тоже хочу до Нового года всё же сделать. 

И затем я перехожу к ручному отсмотру. Делаю я его по нескольким причинам:

  • Нейросеть иногда плохо справляется с частью промпта, которая просит её считать нерелевантными дела, требующими наличия каких-то лицензий и разрешений, где содержание рекламы не совпало с реальностью. В каждом батче обязательно будет 20-30 таких дел, и, на мой взгляд, они не особо нужны для задачи, которую решает бот.
  • Нейросеть получает каждый новый кейс на рассмотрение без знания обо всех предыдущих, и ей невдомек, что база знаний распухнет от однотипных решений по фразе «мы лучше, чем ломбард!».
  • Мне просто это нравится! 🤗 Мне нравится рекламное право в целом, это расширяет мой профессиональный кругозор. Всё, что нейросеть оценивает на 6-9 баллов, как правило, довольно интересно смотреть.
  • Я убеждена в том, что база знаний не должна быть свалкой документов, она требует кураторского подхода, и мои объемы не такие, чтобы всё отдавать на откуп нейросети.

Но нужно сказать, что вся эта система оценок действительно работает, и если лень, то можно и отдать всю задачу нейросети: просто смело брать из каждого батча дела от 5 баллов. Но и при мануальном отсмотре я полагаюсь на эти оценки: примерно с 5 баллов уже просматриваю по диагонали, подмечая только кейсы по редко освещаемым в практике вопросам, даже если там простая правовая позиция, и «поднимаю» им оценку (примеры можно посмотреть в pdf-аттаче здесь). В принципе я читаю только колонку violation_summary, иногда смотрю аргументы ФАС с цитатами. Количество осечек при суммаризации минимальное, и я каждый раз радуюсь тому, как нейросети упростили такой тип задач. 

Итогом этого этапа является то, что я беру и руками копирую строки с оценкой выше 4 или 3 (зависит от батча) и вставляю в файл с основной базой, святая святых, RAG.csv.

Итак, последний этап — сборка корпуса эмбеддингов. Для того, чтобы лучше понять его суть, рекомендую сначала прочитать мой вводный материал о RAG-технологии everybody talks about RAG.

Этап делится на 2 основных сущностных шага — подготовку «чанков», то есть текстов, которые и будут превращаться в эмбеддинги, и само превращение в эмбеддинги, то есть векторизацию. 

Чанки

Обычно под чанками понимаются части документов, имеющие определенную длину (в символах или токенах). Но поскольку я делала RAG для бота по наитию, я не знала, что по классике чанкуются именно тексты. Я пошла по пути изготовления (на этапе разметки) резюме решений нейросетью. И именно резюме отправляются на векторизацию (я схлопываю в один текст содержимое двух колонок из csv-таблицы). 

Я и сейчас думаю, что, учитывая характер исходного материала, это правильный путь: УФАС в решениях пишут много не несущих никакой (для моей задачи) ценности заклинаний, повторяют одно и то же, что просто нарезка текста решения на чанки блага не сделает. 

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

Эмбеддинги

Далее код пайплайна обращается по API к эмбеддинговой модели (сейчас у Gemini она одна — gemini-embedding-001), по очереди векторизует каждый чанк, и все получившиеся координаты собирает в один файл формата NumPy. Как я пишу в своем лонгриде про RAG, RAGи вполне могут быть реализованы не в виде векторной базы данных, а просто вот таким массивом чисел. Учитывая мой объем (менее 2000 решений, в перспективе не более 3000), этого более, чем достаточно. 

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

Я делаю эмбеддинги 768 размерности и использую float64, то есть каждое число в эмбеддинге весит 8 байт: это как бы обеспечивает более высокую точность семантического подбора. Наверное. Я верю на слово всему, что мне говорят нейронки по этому вопросу 🙂

Вот и всё! RAG.csv и корпус эмбеддингов в .npy в паре представляют собой RAG. Коротко напомню, что происходит дальше (или чем занимается код в backend_logic.py): 

1️⃣ пользователь вводит в боте запрос или грузит картинку, скрипт обрабатывает текст или описывает текстом картинку; 

2️⃣ полученный текст тоже становится эмбеддингом с помощью той же самой эмбеддинговой модели (корпус эмбеддингов и входящие эмбеддинги должны делаться одной и той же моделькой из-за используемых ими таблиц токенизации);

3️⃣ для эмбеддинга запроса косинусным сходством высчитываются топ-10 наиболее похожих эмбеддингов;

4️⃣ топ-10 эмбеддингов откатываются обратно в текст и вставляются в шаблон промпта для нейросети;

5️⃣ нейросеть готовит заключение с учетом того, что нашлось эмбеддинговым поиском, цитирует релевантный кейс в заключении.

Полный функционал бота. Выше описаны этапы 2-4.

И здесь уместно рассказать, что я предпринимаю для повышения точности отбора (a.k.a костыли):

  • предобработка креатива — при обработке входящего текста или подготовке описания выкидывается все, что может «зашумить» эмбеддинговый поиск (часы работы, номера телефонов, сайты, всевозможные идентификаторы и т.п.);
  • нейросети дается задача критично относиться к тому, что принесено в промпте из RAG, и если действительно релевантного кейса нет, то не обращать на них внимания.

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

Об организации работы пайплайна

Почему воронка — это воронка? С самого первого парсинга базы ФАС в середине июня до самого последнего в начале ноября мой код сходил в почти 10 тысяч карточек дел, а в базе знаний бота всего 1800+ решений. На каждом этапе воронки что-то отсекается: из-за особенностей ведения базы самой ФАС и моих представление о том, каким должен быть RAG для бота.

Анализ потерь. Итоговая эффективность пайплайна, то есть процент ссылок, ставших знаниями в базе: 19.89%

Технические особенности пайплайна

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

В коде есть переменная CURRENT_SESSION, и при каждом запуске скрапера этой переменной присваивается уникальный ID, который дописывается в JSON, возвращаемый нейросетью после разметки. Таблица, которую я отсматриваю, собирается как раз с учетом этих ID, чтобы в неё не попадало то, что я уже видела. Вся эта история несколько раз ломалась (почему — будет ниже), и мне приходилось дописывать аварийные коды по типу «собери в таблицу всё, что было скачено за последние 4 часа».

Есть и пара толковых механизмов. Например, я упоминала кэширование, чтобы не тратить лишнее время на проходы скрапера и деньги на API-запросы в нейросеть. Кэширование реализовано в виде:

  • большого логгера, записывающего все ссылки, по которым скрапер когда-либо ходил;
  • логики проверки наличия JSONа с нужным docID (если JSON есть, значит решение уже было размечено);
  • специального кэша для корпуса эмбеддингов, чтобы по новой корпус не создавать.

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

Да как вообще этот пайплайн выглядит, это какая-то программа?

Это ноутбук (файл в формате .ipynb) в среде Jupyter Lab. Это не совсем стандартная среда разработки типа VS Code, в которой делаются приложения, то есть не IDE (Integrated Development Environment). У меня достаточно особый вайб-кодерский путь, и я использую именно ноутбуки.

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

Пример отчёта: так выглядит присвоение ID сессии

Ноутбук в варианте последнего прогона ячеек можно послать кому-то, кто откроет его в своём Юпитере и увидит все те же отчёты (или прямо в нейросети, почти все читают .ipynb). Ноутбуки классно подходят для анализа данных, если вам не нужен непременно какой-то интерфейс (хотя и просто код в разных форматах в Юпитере можно править как в редакторе). И в целом этот формат достаточно неплохо подходит для пайплайна — я убеждена, что это сильно проще, чем делать себе какой-то программный интерфейс. Можно быть достаточно гибкой в том, чтобы быстро добавить «аварийные» ячейки (если с сессиями не задалось) или аналитические (анализ эффективности пайплайна я сделала перед тем, как писать этот пост). 

Но! Нюанс использования ноутбука в том, что надо хорошо понимать, какие ячейки с какими у тебя связаны — если не запустишь ячейку с импортами библиотек или активацией API-ключа, то другая нужная ячейка не заработает. Сколько раз я об это спотыкалась… А если Юпитер вылетел посреди скрапинга, то вылетает и вся история с сессиями, потому что переменная CURRENT_SESSION живет только в памяти. Чем больше ячеек — тем более тормозной ноутбук. И всё в таком духе. 

Что еще можно было бы исправить и добавить?

За несколько месяцев работы по наполнению базы у меня несколько раз менялись параметры разметки и скриптованных добавлений в JSONы, которые возвращает нейросеть. Пару раз менялся промпт для разметки. И, собирая датасет с практикой ФАС по рекламным делам, я споткнулась на том, что JSONы получились неоднородные, и их нужно было дополнительно нормализовывать и унифицировать, делая дополнительные API-прогоны через нейросеть.

То есть для текущей версии пайплайна напрашивается какой-то механизм версионирования промпта и унификации всех json-ов. И вообще, кажется, нужно ветвить пайплайн на работу ещё и с датасетом, ведь его тоже нужно будет пополнять и дальше. 

На этом всё! Спасибо всем, кто читал об этой моей работе. Для меня она оказалась одной из самых интересных в рамках всего проекта. И пусть процесс и неидеален, опыт получился совершенно бесценный. 

______________

Описание ячеек кода пайплайна:

Cell 1 (Config): Загружает библиотеки, настраивает пути к папкам и авторизует в Google API. Автоматически ищет API-ключ в файле окружения .env, а если не находит — просит ввести вручную.

Cell 2 (Period): Принимает от пользователя диапазон дат для скрапинга и генерирует уникальный ID сессии, который потом прописывается во все файлы для трекинга.

Cell 3 (Functions): Содержит логику парсинга сайта ФАС и осмотра страниц. Использует список разных поисковых запросов (4 фильтра), чтобы найти максимум документов, которые могут не попасться в одном общем списке.

Cell 4 (Scraping): Запускает скачивание метаданных и текстов решений, обновляя главный лог. Умная проверка обновлений — смотрит, не появились ли решения в уже просмотренных ранее делах.

Cell 5 (Prompt): Хранит огромный системный промпт для LLM с инструкциями по анализу для разметки. Жесткое требование вывода в JSON с флагом value_score, фильтрующим нерелевантные дела.

Cell 6 (Gemini Processing): Отправляет тексты в Gemini API и сохраняет ответы в JSON. Реализована сложная система повторных попыток (retries) и жесткие таймауты через signal, чтобы процесс не зависал.

Cell 6.5 (Session Analysis): Строит воронку потерь для текущего запуска (сколько скачали -> сколько обработали). Показывает процент потерь на каждом этапе, помогая понять, виноват сайт ФАС или API Gemini.

Cell 7 (Results Assembly): Собирает разрозненные JSON-файлы текущей сессии в единую CSV-таблицу. Автоматически превращает номера статей закона (из JSON) в понятные тематические теги (из справочника).

Cell 7C (Rebuild Session): Позволяет пересобрать таблицу RAG для любой прошлой сессии. Сканирует метаданные всех JSON-файлов и предлагает меню выбора доступных прошлых запусков.

Cell 7.5 (Tags Check): Проверяет, для каких статей закона не нашлось описания в справочнике тегов. Выводит список «сирот», помогая дообучать систему тегирования вручную.

Cell 8 (Embeddings): Создает векторные представления (эмбеддинги) для готовой таблицы. Сохраняет векторы сразу в трех форматах (float64, 32, 16) для баланса между точностью и размером памяти.

Cell 8a (Re-embedding): Полностью пересоздает базу векторов с нуля. Фишка: Делает автоматический бэкап старых файлов перед перезаписью.

Cell 9 (Search Test): Тестирует работу RAG, выполняя семантический поиск по запросу. Выводит не только текст, но и коэффициент сходства (cosine similarity), показывая уверенность модели.

Cell 10 (Stats): Выводит итоговый отчет по количеству файлов в системе. Показывает реальные размеры файлов эмбеддингов в мегабайтах.

Cell 11 (Trash Analysis): Собирает все дела, которые нейросеть посчитала нерелввантными для RAG (value_score=0). Позволяет проверить, не выкидывает ли нейросеть важные дела по ошибке.

Cell 12 (Diagnostics): Глубокая отладка проблем с потерей сессий. Использует анализ времени создания файлов (ctime) как запасной вариант, если метаданные ID сессии были утеряны.

Cell 13 (Global Funnel): Считает общую эффективность пайплайна за всё время. Показывает финальный КПД системы (процент ссылок, ставших знаниями).

Report Page