LlamaIndex: как разбивать документ на ноды
Data Scientist, специализуруюсь на NLP(natural language processing) Автор телеграм-канала @nlp_daily Продолжаем изучать фреймворк для создания AI-ботов. В этой части узнаем про тонкости индексирования собственной базы документов. ← Часть 1 💬🦙 LlamaIndex: создаем чат-бота без боли и страданий. Часть 1 В предыдущей статье мы познакомились с LlamaIndex — мощным инструментом, предназначенным для работы с большими языковыми моделями. Мы рассмотрели основные концепции и принципы работы этого фреймворка, а также увидели его в действии на простом примере поиска ответа в заданном тексте. Это были цветочки: в этой статье погрузимся в его более продвинутые возможности. Современные чат-боты становятся все более интеллектуальными, однако, чтобы действительно раскрыть их потенциал, необходимо обеспечить их доступом к обширным базам данных и документации. Именно здесь и кроется основное преимущество llamaindex. Один из подписчиков моего блога задал справедливый вопрос: «А зачем нужен этот ваш ламаиндекс, если можно напрямую обращаться к языковым моделям?» Да, cам фреймворк всего лишь обертка над множеством апишек – от моделей OpenAI до векторных баз данных типа Pinecone Но именно благодаря этой «простой» обертке разработчики получают уникальную возможность интегрировать разнообразные источники данных и модели в единую систему, что значительно упрощает процесс разработки чат-ботов. В этой части мы рассмотрим, как с помощью llamaIndex правильно проиндексировать собственную базу документов. В статье вы увидите примеры кода. Чтобы у вас тоже всё заработало, настройте окружение по инструкциям из предыдущей статьи. Мы будем строить базу данных для учебного чат-бота на основе договоров в формате Вот как выглядит один из примеров: Образец договора Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека data scientist’а» Интересно, перейти к каналу LlamaIndex предоставляет инструменты для работы с различными форматами данных, включая Например Загружаем файлы На самом деле я подгрузил только 5 договоров, но некоторые разбились на 2 страницы: Уникальные названия Коннекторов данных в ламаиндекс существует огромное количество, можно подгружать данные из Википедии, Jira, даже из YouTube. Все коннекторы можно поcмотреть здесь. После того как мы загрузили наши документы в llamaIndex, следующий шаг — это разбиение их на ноды. Нода — это базовая единица информации в llamaIndex. Каждая нода представляет собой фрагмент текста из документа, который может быть использован для ответа на запрос пользователя. Например, если у нас есть договор, то каждый пункт или подраздел этого договора может быть представлен в виде отдельной ноды. Разбиение документов на ноды позволяет улучшить качество и точность ответов чат-бота. Вместо того чтобы анализировать весь документ целиком, фреймворк может быстро найти и использовать конкретную ноду, которая наиболее релевантна запросу пользователя. Разбиение документов на ноды — это ключевой этап в подготовке данных для LlamaIndex. Правильное разбиение может значительно повысить качество ответов чат-бота. Но как определить, какой фрагмент текста стоит сделать нодой? На мой взгляд это искусство 🙂 И разбиение будет очень сильно зависеть от структуры ваших данных, но, тем не менее можно выделить какие-то общие принципы: Для начала попробуем самое простое деление на ноды: Количество нод Теперь мы можем проиндексировать наши документы Ответ про сумму договора Нам соврали, т. к. в тексте фигурирует другая сумма – 900 тысяч, попробуем задать еще один вопрос: Ответ про контрагента А здесь уже лучше. Так в чем же причина ошибки? Для этого надо посмотреть на ноды, которые были отправлены для получения ответа модели: первая нода была выбрана правильно, но вот вторая взята из другого договора, поэтому и сумма получилась другой. Используемые ноды Статья по теме 🔤 Промпт-инжиниринг: как правильно писать запросы нейросетям В примере выше мы увидели, что поисковый движок выбрал ноды из различных договоров, что не совсем корректно отражает наш запрос. Чтобы улучшить результаты, нам стоит доработать механизм так, чтобы он более точно учитывал контекст конкретного договора. Для решения этой проблемы мы можем задействовать метаинформацию. В этом случае модель сможет более точно фильтровать и выбирать релевантные фрагменты, учитывая контекст и специфику каждого договора. В llamaindex для этой цели есть модуль Для начала попробуем самое простое решение – добавим заголовок документа в ноды: Метаданные ноды Ну а теперь попробуем еще раз задать тот же самый вопрос: Правильный ответ Сработало! Попробуем добавить еще больше метаинформации: Метаданные с ключевыми словами Теперь наш узел содержит ключевые слова, что позволит поисковой модели еще лучше оценить релевантность для заданного запроса. Эту часть можно завершать. В следующей – посмотрим на разные типы ретриверов, а также стратегии их использования. Спасибо за внимание! Пишу про AI и NLP в телеграм. Марк Конаков
Создание синтетических данных
pdf
. Я возьму реальный пример, с которым я столкнулся в моей практике – договоры ПИР (проектные и изыскательские работы). Конечно, настоящие документы я, пожалуй, не буду здесь публиковать, поэтому создам синтетические примеры с помощью ChatGPT. После генерации текста договора я сохраняю его в формате pdf
для большего соответствия реальной ситуации.Работа с PDF в 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')
Разбиение документов на ноды
Что такое нода?
Зачем разбивать документы на ноды?
from llama_index.node_parser import SimpleNodeParser # Cоздаем парсер parser = SimpleNodeParser() # Разбиваем на ноды nodes = parser.get_nodes_from_documents(docs) print(len(nodes))
from llama_index import GPTVectorStoreIndex # Создаем индекс index = GPTVectorStoreIndex([]) # Индексируем ноды index.insert_nodes(nodes) # Создаем движок запросов engine = index.as_query_engine() # Пробуем задать вопрос response = engine.query('Какова сумма договора с ТатарГеоCтрой?') print(response)
Добавляем метаданные в ноды
MetadataExtractor
, в котором реализованы следующие классы:document_title
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])
new_index = GPTVectorStoreIndex(nodes_with_meta) new_engine = new_index.as_query_engine() response = new_engine.query('Какова сумма договора с ТатарГеоCтрой?') print(response)
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)
- 0 views
- 0 Comment