Share This
Связаться со мной
Крути в низ
Categories
//LlamaIndex: как разбивать документ на ноды

LlamaIndex: как разбивать документ на ноды

llamaindex kak razbivat dokument na nody e69d1b8 - LlamaIndex: как разбивать документ на ноды

Data Scientist, специализуруюсь на NLP(natural language processing) Автор телеграм-канала @nlp_daily Продолжаем изучать фреймворк для создания AI-ботов. В этой части узнаем про тонкости индексирования собственной базы документов.

llamaindex kak razbivat dokument na nody 64b8851 - LlamaIndex: как разбивать документ на ноды

← Часть 1 💬🦙 LlamaIndex: создаем чат-бота без боли и страданий. Часть 1

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

Современные чат-боты становятся все более интеллектуальными, однако, чтобы действительно раскрыть их потенциал, необходимо обеспечить их доступом к обширным базам данных и документации. Именно здесь и кроется основное преимущество llamaindex.

Один из подписчиков моего блога задал справедливый вопрос: «А зачем нужен этот ваш ламаиндекс, если можно напрямую обращаться к языковым моделям?»

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

В этой части мы рассмотрим, как с помощью llamaIndex правильно проиндексировать собственную базу документов. В статье вы увидите примеры кода. Чтобы у вас тоже всё заработало, настройте окружение по инструкциям из предыдущей статьи.

Создание синтетических данных

Мы будем строить базу данных для учебного чат-бота на основе договоров в формате pdf. Я возьму реальный пример, с которым я столкнулся в моей практике – договоры ПИР (проектные и изыскательские работы). Конечно, настоящие документы я, пожалуй, не буду здесь публиковать, поэтому создам синтетические примеры с помощью ChatGPT. После генерации текста договора я сохраняю его в формате pdf для большего соответствия реальной ситуации.

Вот как выглядит один из примеров:

llamaindex kak razbivat dokument na nody bdeab53 - LlamaIndex: как разбивать документ на ноды

Образец договора Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а» Интересно, перейти к каналу

Работа с PDF в LlamaIndex

LlamaIndex предоставляет инструменты для работы с различными форматами данных, включая pdf. Для этого используется специальный коннектор. Коннектор в контексте llamaIndex — это инструмент, который позволяет интегрировать и обрабатывать данные из различных источников или форматов. Это своего рода адаптер, который обеспечивает совместимость между llamaIndex и внешними данными.

Например SimpleDirectoryReader, позволяет загружать данные в форматах: .pdf, .jpg, .png, .docx. Для чтения pdf надо будет еще дополнительно поставить пакет pypdf

         import os from llama_index import SimpleDirectoryReader  # Не забываем указать ключ к апи os.environ['OPENAI_API_KEY'] = 'sk-L0xrKrmzb2KufE*'  # Создаем объект для работы с PDF reader = SimpleDirectoryReader(input_dir='./pir_samples/')  # Загружаем наши документы docs = reader.load_data()  print(f'Loaded {len(docs)} docs')       

llamaindex kak razbivat dokument na nody bec1cd3 - LlamaIndex: как разбивать документ на ноды

Загружаем файлы

На самом деле я подгрузил только 5 договоров, но некоторые разбились на 2 страницы:

llamaindex kak razbivat dokument na nody 62c0bba - LlamaIndex: как разбивать документ на ноды

Уникальные названия

Коннекторов данных в ламаиндекс существует огромное количество, можно подгружать данные из Википедии, Jira, даже из YouTube. Все коннекторы можно поcмотреть здесь.

Разбиение документов на ноды

После того как мы загрузили наши документы в llamaIndex, следующий шаг — это разбиение их на ноды.

Что такое нода?

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

Зачем разбивать документы на ноды?

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

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

  1. Определить ключевые разделы документа. Обычно в документах есть четко выделенные разделы или подразделы. В контексте договора ПИР, это могут быть пункты, например, такие как «Обязанности Сторон», «Стоимость работ» и т. д.
  2. Размер ноды имеет значение. Если нода слишком велика, модель может утонуть в избыточной информации и не выделить наиболее релевантный фрагмент в ответ на запрос пользователя, в то время как слишком маленькие ноды могут не содержать достаточно информации для формирования полноценного ответа. Идеальный размер ноды — это фрагмент текста, который полностью и ясно отвечает на конкретный запрос.
  3. Использовать метаданные. Метаинформация — это дополнительные данные, которые могут быть прикреплены к ноде. Она может включать в себя дату создания документа, автора, заголовок, ключевые слова и многое другое.

Для начала попробуем самое простое деление на ноды:

         from llama_index.node_parser import SimpleNodeParser  # Cоздаем парсер parser = SimpleNodeParser()  # Разбиваем на ноды nodes = parser.get_nodes_from_documents(docs)  print(len(nodes))      

llamaindex kak razbivat dokument na nody 2aabc66 - LlamaIndex: как разбивать документ на ноды

Количество нод

Теперь мы можем проиндексировать наши документы

         from llama_index import GPTVectorStoreIndex  # Создаем индекс index = GPTVectorStoreIndex([])  # Индексируем ноды index.insert_nodes(nodes)  # Создаем движок запросов engine = index.as_query_engine()  # Пробуем задать вопрос response = engine.query('Какова сумма договора с ТатарГеоCтрой?')  print(response)      

llamaindex kak razbivat dokument na nody 673aa2d - LlamaIndex: как разбивать документ на ноды

Ответ про сумму договора

Нам соврали, т. к. в тексте фигурирует другая сумма – 900 тысяч, попробуем задать еще один вопрос:

llamaindex kak razbivat dokument na nody 73f230b - LlamaIndex: как разбивать документ на ноды

Ответ про контрагента

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

llamaindex kak razbivat dokument na nody 6fdf4ec - LlamaIndex: как разбивать документ на ноды

Используемые ноды Статья по теме 🔤 Промпт-инжиниринг: как правильно писать запросы нейросетям

Добавляем метаданные в ноды

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

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

  1. TitleExtractor: этот класс предназначен для извлечения заголовков, особенно полезен для длинных документов. Он извлекает поле метаданных document_title
  2. KeywordExtractor: извлекает ключевые слова на уровне ноды.
  3. QuestionsAnsweredExtractor: генерирует вопросы, на которые может ответить данный узел.
  4. SummaryExtractor: создает резюме ноды, в том числе с возможностью совместного использования соседних узлов.

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

         from llama_index.node_parser.extractors import (     MetadataExtractor,     TitleExtractor, )  # Создаем тип сборщика метаинформации metadata_extractor = MetadataExtractor(     extractors=[         TitleExtractor(nodes=5) # указываем количество нод с одним title     ] )  # Создаем парсер для нод с нужным свойством node_parser = SimpleNodeParser(     metadata_extractor=metadata_extractor ) # Получаем ноды nodes_with_meta = node_parser.get_nodes_from_documents(docs)  print(nodes_with_meta[0])      

llamaindex kak razbivat dokument na nody 92de0c4 - LlamaIndex: как разбивать документ на ноды

Метаданные ноды

Ну а теперь попробуем еще раз задать тот же самый вопрос:

Правильный ответ

         new_index = GPTVectorStoreIndex(nodes_with_meta) new_engine = new_index.as_query_engine()  response = new_engine.query('Какова сумма договора с ТатарГеоCтрой?')  print(response)      

llamaindex kak razbivat dokument na nody ffbf35c - LlamaIndex: как разбивать документ на ноды

Сработало! Попробуем добавить еще больше метаинформации:

         from llama_index.node_parser.extractors import KeywordExtractor  metadata_extractor = MetadataExtractor(     extractors=[         TitleExtractor(nodes=5),         KeywordExtractor(keywords=10) # задаем количество ключевых слов     ] )  node_parser = SimpleNodeParser(     metadata_extractor=metadata_extractor, )  nodes_with_meta = node_parser.get_nodes_from_documents(docs) print(nodes_with_meta[0].metadata)      

llamaindex kak razbivat dokument na nody 7a09968 - LlamaIndex: как разбивать документ на ноды

Метаданные с ключевыми словами

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

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

Спасибо за внимание!

Пишу про AI и NLP в телеграм.

  • 2 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close