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

Примечание

Поиск с использованием ИИ Azure доступна через портал Azure, REST API и Azure SDKs. Он также служит основой для Foundry IQ — управляемого уровня знаний, который преобразует корпоративный контент в повторно используемые базы знаний с учетом прав доступа для агентов на портале Microsoft Foundry.

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

  • Повторять для каждого дочернего элемента (блок) в одном индексе
  • Индексируются как автономные документы поиска в том же индексе
  • Или загрузка в отдельные индексы

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

В Поиск с использованием ИИ Azure фрагментирование выполняется с помощью навыков и поэтому зависит от индексаторов. Чтобы определить проекцию индекса, укажите ее в наборе навыков.

Необходимые условия

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

Выбор подхода

Проекции индекса создают дочерние документы (блоки) для каждого родительского документа. Выберите способ обработки родительского содержимого:

Подход Описание Конфигурации
Один индекс, повторяющиеся родительские поля (рекомендуется) Родительские поля повторяются для каждого блока. Все документы имеют единую форму. Задайте для индексатора targetIndexName и проекции targetIndexName индекса одинаковый индекс. Установите projectionMode на skipIndexingParentDocuments.
Один индекс, смешанные структуры документа Родительские документы и фрагменты документов сосуществуют. Родительские документы имеют поля с пустыми фрагментами. Задайте для обоих targetIndexName значений одинаковый индекс. projectionMode Задайте значение includeIndexingParentDocuments (или опустить, так как это значение по умолчанию).
Два или более отдельных индексов Родительский индекс для метапоиска, дочерний индекс для поиска. Присоединения не выполняются в момент выполнения запроса. Установите индексатор targetIndexName для родительского индекса. Установите проекцию индекса targetIndexName на дочерний индекс. Массив selectors определяет количество и состав элементов дочернего индекса.

Для большинства сценариев RAG используйте первый подход. См. пример classic RAG.

  1. Создайте индекс, предназначенный для блоков, с включенными родительскими полями.
  2. Создайте набор навыков с навыком разбиения и indexProjections.
  3. Создайте индексатор, указывающий на поддерживаемый источник данных.

Если источник данных поддерживает отслеживание изменений, индексатор автоматически синхронизирует изменения.

Создание индекса для индексирования "один ко многим"

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

  • Поле ключа документа уникально идентифицирует каждый документ. Он должен быть определен как тип Edm.String с помощью анализатора keyword.

  • Поле, связывающее каждый блок с родительским элементом. Он должен быть типом Edm.String. Оно не может быть полем ключа документа и должно filterable иметь значение true. Он называется parent_id в примерах и в качестве проецируемого ключевого значения в этой статье.

  • Другие поля для содержимого, такие как текстовые поля или поля векторизованных фрагментов.

Индекс должен существовать в службе поиска перед созданием набора навыков или запуском индексатора. Определенные selectors в наборе навыков должны включать эти поля.

Единая схема индекса, включающая родительские и дочерние поля

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

Следующая схема — это пример, который соответствует требованиям для проекций индексов. В этом примере:

  • Родительские поля — это parent_id и заголовок, и они повторяются для каждого блока.
  • Дочерние поля — это векторные и невекторные блоки. Chunk_id — это идентификатор документа этого индекса.

Портал Azure, REST API или Azure SDK можно использовать для создания индекса.

Используйте клиент REST или портал Azure Add index действие и параметр JSON для создания индекса.

{
    "name": "my_consolidated_index",
    "fields": [
        {"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
        {"name": "parent_id", "type": "Edm.String", "filterable": true},
        {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
        {"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
        {"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
    ],
    "vectorSearch": {
        "algorithms": [{"name": "hnsw", "kind": "hnsw", "hnswParameters": {}}],
        "profiles": [{"name": "hnsw", "algorithm": "hnsw"}]
    }
}

Добавление проекций индекса в набор навыков

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

Прогнозы индекса обычно доступны. Мы рекомендуем самый последний стабильный API:

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

Если родительский документ содержит метаданные разрешений для доступа на уровне документа, например metadata_user_idsmetadata_group_ids, или metadata_spo_site_urlвключите эти поля в mappings. Каждый фрагмент должен наследовать их, чтобы фильтры прав доступа применялись при выполнении запроса. Дополнительные сведения см. в разделе "Выбор места для заполнения полей ACL (предварительная версия)".

"indexProjections": {
    "selectors": [
        {
            "targetIndexName": "my_consolidated_index",
            "parentKeyFieldName": "parent_id",
            "sourceContext": "/document/pages/*",
            "mappings": [
                {
                    "name": "chunk",
                    "source": "/document/pages/*",
                    "sourceContext": null,
                    "inputs": []
                },
                {
                    "name": "chunk_vector",
                    "source": "/document/pages/*/chunk_vector",
                    "sourceContext": null,
                    "inputs": []
                },
                {
                    "name": "title",
                    "source": "/document/title",
                    "sourceContext": null,
                    "inputs": []
                }
            ]
        }
    ],
    "parameters": {
        "projectionMode": "skipIndexingParentDocuments"
    }
}

Справочник по параметрам

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

Параметры имеют следующие элементы в рамках их определения.

Параметры Определение
parameters.projectionMode Необязательный параметр, предоставляющий инструкции индексатору. Допустимые значения включают includeIndexingParentDocuments и skipIndexingParentDocuments.

Лучшее значение для этого параметра — skipIndexingParentDocuments. Следует использовать его, когда документы с разбиением на части являются основным объектом поиска.

Если вы не задаёте skipIndexingParentDocuments для projectionMode, вы автоматически получите includeIndexingParentDocuments, так как это значение по умолчанию. Он добавляет в ваш индекс дополнительные документы поиска, которые имеют значение NULL для данных блоков, но заполнены содержимым, относящимся к родителю. Например, если пять PDF-файлов вносят 100 блоков в индекс, то количество документов в индексе равно 105. Пять документов, созданных для родительских полей, имеют NULL для дочерних полей (блоков), что делает их значительно отличными от большинства документов в индексе. По этой причине рекомендуется projectionMode установить на skipIndexingParentDocuments.

Селекторы имеют следующие элементы в рамках их определения.

Селекторы Определение
selectors.targetIndexName Имя индекса, в который проецируются данные индекса. Это либо один блоковый индекс с повторяющимися родительскими полями, либо дочерний индекс, если вы используете отдельные индексы для содержимого родительского дочернего элемента.
selectors.parentKeyFieldName Имя поля, предоставляющего ключ родительского документа.
selectors.sourceContext Заметка обогащения, определяющая степень детализации, с помощью которой данные сопоставлялись с отдельными документами поиска. Дополнительные сведения см. в разделе Контекст навыка и язык заметок ввода.
selectors.mappings Массив сопоставлений обогащенных данных с полями в индексе поиска. Каждое сопоставление состоит из следующих элементов:
name: имя поля в индексе поиска, в который должны индексироваться данные.
source: Путь аннотации обогащения, из которого должны извлекаться данные.

Каждый mapping также может рекурсивно определять данные с необязательным sourceContext и inputs полем, аналогичным хранилищу знаний или навыку формообразователя. В зависимости от приложения эти параметры позволяют формировать данные в поля типа Edm.ComplexType в индексе поиска. Некоторые LLM не принимают сложный тип в результатах поиска, поэтому LLM, который вы используете, определяет, является ли сопоставление сложных типов полезным или нет.

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

Это требование отличается от других соглашений о сопоставлении полей в Поиск с использованием ИИ Azure. Для некоторых типов источников данных индексатор может неявно сопоставлять поля на основе аналогичных имен или известных характеристик (например, индексаторы BLOB-объектов используют уникальный путь к хранилищу метаданных в качестве ключа документа по умолчанию). Однако для проекций индексатора необходимо явно указать каждое сопоставление полей на стороне отношения "многие".

Важно

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

Просмотр сопоставлений полей

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

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

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

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

Примечание

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

Запуск индексатора

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

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

Жизненный цикл содержимого

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

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

Примечание

Хотя данные в проецируемых документах можно редактировать вручную с помощью API push-отправки индекса, следует избегать этого. Обновления индекса, сделанные вручную, перезаписываются при следующем вызове конвейера, если документ в исходных данных обновляется, и если источник данных поддерживает отслеживание изменений или обнаружение удаления.

Обновленное содержимое

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

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

Некоторые источники данных, такие как служба хранилища Azure поддерживают отслеживание изменений и удаления по умолчанию на основе метки времени. Другие источники данных, такие как Microsoft OneLake, Azure SQL или Azure Cosmos DB должны быть настроены для отслеживания изменений.

Удаленное содержимое

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

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

Проецируемое ключевое значение

Чтобы обеспечить целостность данных для обновленного и удаленного содержимого, обновление данных в индексировании "один ко многим" использует проецированное значение ключа на стороне "многие". Если вы используете интегрированную векторизацию или мастер импорта данных, то проецируемое значение ключа — это поле на разделяемой на части или "множественной" стороне индекса.

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

  • Случайный хэш для обеспечения уникальности. Этот хэш изменяется, если родительский документ обновляется при последующих запусках индексатора.
  • Ключ родительского документа.
  • Путь аннотации обогащения, определяющий контекст для созданного документа.

Например, если разделить родительский документ со значением ключа "aa1b22c33" на четыре страницы, а затем каждый из этих страниц проецируется в качестве собственного документа с помощью проекций индексов:

  • aa1b22c33
  • aa1b22c33_pages_0
  • aa1b22c33_pages_1
  • aa1b22c33_pages_2

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

Пример отдельных родительско-дочерних индексов

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

  1. Создайте две схемы индекса.

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

    Родительский индекс имеет поле parent_id и заголовок. Parent_id — это ключ документа. Вам не нужна конфигурация векторного поиска, если вы не хотите векторизировать поля на родительском уровне документа.

    {
        "name": "my-parent-index",
        "fields": [
    
            {"name": "parent_id", "type": "Edm.String", "key":true, "filterable": true},
            {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true}
        ]
    }
    

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

    {
        "name": "my-child-index",
        "fields": [
            {"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
            {"name": "parent_id", "type": "Edm.String", "filterable": true},
             {"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
            {"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
        ],
        "vectorSearch": {
            "algorithms": [{"name": "hsnw", "kind": "hnsw", "hnswParameters": {}}],
            "profiles": [{"name": "hsnw", "algorithm": "hnsw"}]
        },
        "scoringProfiles": [],
        "semanticConfiguration": [],
        "analyzers": []
    }
    
  2. Обновите индексатор, чтобы указать родительский индекс в качестве целевого объекта.

    Определение индексатора указывает компоненты конвейера. В определении индексатора имя индекса, указываемого является родительским индексом. Если вам нужны сопоставления полей для полей родительского уровня, определите их в outputFieldMappings. Для индексирования "один ко многим", использующего отдельные индексы, определение индексатора может выглядеть следующим образом.

    {
      "name": "my-indexer",
      "dataSourceName": "my-ds",
      "targetIndexName": "my-parent-index",
      "skillsetName" : "my-skillset",
      "parameters": { },
      "fieldMappings": (optional) Maps fields in the underlying data source to fields in an index,
      "outputFieldMappings" : (required) Maps skill outputs to fields in an index,
    }
    
  3. Добавьте indexProjections в набор навыков.

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

    Обратите внимание, что parameters является NULL и используется значение по умолчанию для includeIndexingParentDocuments. Индексатор заполняет родительский индекс. Массив selectors используется для проецирования фрагментов документов в дочерний индекс.

    "indexProjections": {
        "selectors": [
            {
                "targetIndexName": "my-child-index",
                "parentKeyFieldName": "parent_id",
                "sourceContext": "/document/pages/*",
                "mappings": [
                    {
                        "name": "chunk",
                        "source": "/document/pages/*",
                        "sourceContext": null,
                        "inputs": []
                    },
                    {
                        "name": "chunk_vector",
                        "source": "/document/pages/*/chunk_vector",
                        "sourceContext": null,
                        "inputs": []
                    }
                ]
            }
        ],
        "parameters": {}
    }
    
  4. Запустите индексатор. Если вы ранее запустили индексатор, сначала не забудьте сбросить его.

    У вас должно быть два индекса, заполненных соответствующим содержимым. Запросите индексы в обозревателе поиска, чтобы убедиться, что каждый из них содержит правильное содержимое.

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

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