Поделиться через


Руководство. Разработка индекса для RAG в службе "Поиск ИИ Azure"

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

Изучив это руководство, вы:

  • Сведения о характеристиках схемы индекса, созданной для RAG
  • Создание индекса, который содержит векторные и гибридные запросы
  • Добавление профилей и конфигураций векторов
  • Добавление структурированных данных
  • Добавление фильтрации

Необходимые компоненты

Visual Studio Code с расширением Python и пакетом Jupyter. Дополнительные сведения см. в статье Python в Visual Studio Code.

Выходные данные этого упражнения — это определение индекса в ФОРМАТЕ JSON. На этом этапе он не отправляется в поиск ИИ Azure, поэтому в этом упражнении нет требований к облачным службам или разрешениям.

Ознакомьтесь с рекомендациями по схеме для RAG

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

Организовано вокруг блоков

Когда llms создает ответ, они работают с блоками содержимого для входных данных сообщения, и в то время как они должны знать, откуда поступил блок для ссылок, что самое важное — качество входных данных сообщения и его релевантность к вопросу пользователя. Независимо от того, приходят ли блоки из одного документа или тысячи, LLM выполняет прием информации или данных о заземления и сформулирует ответ с помощью инструкций, предоставленных в системном запросе.

Блоки являются фокусом схемы, и каждый блок является определяющим элементом документа поиска в шаблоне RAG. Индекс можно рассматривать как большую коллекцию блоков, а не традиционные документы поиска, которые, вероятно, имеют большую структуру, например поля, содержащие единообразное содержимое для имени, описания, категории и адреса.

Улучшено с помощью созданных данных

В этом руководстве примеры данных состоят из PDF-файлов и содержимого из КНИГи НАСА Земля. Это содержимое является описательным и информативным, с многочисленными ссылками на географические регионы, страны и области по всему миру. Все текстовое содержимое фиксируется в блоках, но повторяющиеся экземпляры имен мест создают возможность добавления структуры в индекс. Используя навыки, можно распознавать сущности в тексте и записывать их в индекс для использования в запросах и фильтрах. В этом руководстве мы добавим навык распознавания сущностей, который распознает и извлекает сущности расположения, загружая его в поле с возможностью locations поиска и фильтрации. Добавление структурированного содержимого в индекс предоставляет дополнительные возможности фильтрации, повышения релевантности и более ориентированных ответов.

Поля родительского дочернего элемента в одном или двух индексах?

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

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

В этом руководстве, так как все фрагменты текста происходят из одного родительского элемента (NASA Earth Book), вам не нужен отдельный индекс, выделенный для уровня родительских полей. Однако при индексировании из нескольких родительских PDF-файлов может потребоваться пара индексов родительского-дочернего элемента для записи полей на уровне, а затем отправлять запросы подстановки родительскому индексу, чтобы получить эти поля, относящиеся к каждому блоку.

Контрольный список рекомендаций по схеме

В поиске ИИ Azure индекс, который лучше всего подходит для рабочих нагрузок RAG, имеет следующие качества:

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

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

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

  • Схема должна быть неструктурированным (без сложных типов или структур), либо следует отформатировать выходные данные сложного типа в формате JSON перед отправкой в LLM. Это требование относится к шаблону RAG в службе "Поиск искусственного интеллекта Azure".

Примечание.

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

Создание индекса для рабочих нагрузок RAG

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

  1. Откройте Visual Studio Code и создайте новый файл. Это не обязательно тип файла Python для этого упражнения.

  2. Ниже приведено минимальное определение индекса для решений RAG, поддерживающих векторный и гибридный поиск. Ознакомьтесь со сведениями о необходимых элементах: имя индекса, поля и раздел конфигурации для векторных полей.

    {
      "name": "example-minimal-index",
      "fields": [
        { "name": "id", "type": "Edm.String", "key": true },
        { "name": "chunked_content", "type": "Edm.String", "searchable": true, "retrievable": true },
        { "name": "chunked_content_vectorized", "type": "Edm.Single", "dimensions": 1536, "vectorSearchProfile": "my-vector-profile", "searchable": true, "retrievable": false, "stored": false },
        { "name": "metadata", "type": "Edm.String", "retrievable": true, "searchable": true, "filterable": true }
      ],
      "vectorSearch": {
          "algorithms": [
              { "name": "my-algo-config", "kind": "hnsw", "hnswParameters": { }  }
          ],
          "profiles": [ 
            { "name": "my-vector-profile", "algorithm": "my-algo-config" }
          ]
      }
    }
    

    Поля должны включать ключевое поле ("id" в этом примере) и должны включать векторные блоки для поиска сходства и невекторные блоки для входных данных в LLM.

    Векторные поля связаны с алгоритмами, определяющими пути поиска во время запроса. Индекс содержит раздел vectorSearch для указания нескольких конфигураций алгоритмов. Поля векторов также имеют определенные типы и дополнительные атрибуты для внедрения измерений модели. Edm.Single — это тип данных, который работает для часто используемых LLM. Дополнительные сведения о полях векторов см. в разделе "Создание векторного индекса".

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

  3. Ниже приведена схема индекса для исходного кода руководства и содержимого Книги Земли.

    Как и базовая схема, она организована вокруг блоков. Уникально chunk_id идентифицирует каждый блок. Поле text_vector представляет собой внедрение блока. Поле невектора chunk — это удобочитаемая строка. Сопоставляется title с уникальным путем к хранилищу метаданных для больших двоичных объектов. Это parent_id единственное поле родительского уровня, и это версия URI родительского файла в кодировке Base64.

    В интегрированных рабочих нагрузках векторизации, таких как в этой серии учебников, dimensions свойство в полях векторов должно совпадать с количеством созданных dimensions навыком внедрения, используемым для векторизации данных. В этой серии мы используем навык внедрения Azure OpenAI, который вызывает модель внедрения текста 3-большого размера в Azure OpenAI. Навык указан в следующем руководстве. Мы задали измерения 1024 в поле вектора и в определении навыка.

    Схема также содержит locations поле для хранения созданного содержимого, созданного конвейером индексирования.

     from azure.identity import DefaultAzureCredential
     from azure.identity import get_bearer_token_provider
     from azure.search.documents.indexes import SearchIndexClient
     from azure.search.documents.indexes.models import (
         SearchField,
         SearchFieldDataType,
         VectorSearch,
         HnswAlgorithmConfiguration,
         VectorSearchProfile,
         AzureOpenAIVectorizer,
         AzureOpenAIVectorizerParameters,
         SearchIndex
     )
    
     credential = DefaultAzureCredential()
    
     # Create a search index  
     index_name = "py-rag-tutorial-idx"
     index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)  
     fields = [
         SearchField(name="parent_id", type=SearchFieldDataType.String),  
         SearchField(name="title", type=SearchFieldDataType.String),
         SearchField(name="locations", type=SearchFieldDataType.Collection(SearchFieldDataType.String), filterable=True),
         SearchField(name="chunk_id", type=SearchFieldDataType.String, key=True, sortable=True, filterable=True, facetable=True, analyzer_name="keyword"),  
         SearchField(name="chunk", type=SearchFieldDataType.String, sortable=False, filterable=False, facetable=False),  
         SearchField(name="text_vector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), vector_search_dimensions=1024, vector_search_profile_name="myHnswProfile")
         ]  
    
     # Configure the vector search configuration  
     vector_search = VectorSearch(  
         algorithms=[  
             HnswAlgorithmConfiguration(name="myHnsw"),
         ],  
         profiles=[  
             VectorSearchProfile(  
                 name="myHnswProfile",  
                 algorithm_configuration_name="myHnsw",  
                 vectorizer_name="myOpenAI",  
             )
         ],  
         vectorizers=[  
             AzureOpenAIVectorizer(  
                 vectorizer_name="myOpenAI",  
                 kind="azureOpenAI",  
                 parameters=AzureOpenAIVectorizerParameters(  
                     resource_url=AZURE_OPENAI_ACCOUNT,  
                     deployment_name="text-embedding-3-large",
                     model_name="text-embedding-3-large"
                 ),
             ),  
         ], 
     )  
    
     # Create the search index
     index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search)  
     result = index_client.create_or_update_index(index)  
     print(f"{result.name} created")  
    
  4. Для схемы индекса, которая более тесно имитирует структурированное содержимое, у вас будут отдельные индексы для родительских и дочерних (фрагментированных) полей. Для координации индексирования двух индексов потребуется проекция индексов одновременно. Запросы выполняются для дочернего индекса. Логика запроса включает запрос подстановки, используя parent_idt получить содержимое из родительского индекса.

    Поля в дочернем индексе:

    • ID
    • chunk
    • chunkVectcor
    • parent_id

    Поля в родительском индексе (все, что требуется "один из"):

    • parent_id
    • Поля родительского уровня (имя, название, категория)

Следующий шаг