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

Если вы разбиваете содержимое на части для шаблона 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:

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

"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. Пять документов, созданных для родительских полей, имеют пустые значения для полей "chunk" (дочерних), из-за чего они существенно отличаются от большинства документов в индексе. По этой причине рекомендуется projectionMode задать значение skipIndexingParentDocuments.

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

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

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

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

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

Важно

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

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

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

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

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

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

Примечание

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

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

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

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

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

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

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

Примечание

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

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

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

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

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

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

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

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

Предполагаемое значение ключа

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

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

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

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

  • aa1b22c33
  • aa1b22c33_pages_0
  • aa1b22c33_pages_1
  • aa1b22c33_pages_2

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

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

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

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

    Каждая схема имеет поля для своей определенной единицы (grain), причем поле родительского идентификатора общее для обоих индексов для использования в поисковом запросе. Основным корпусом поиска является дочерний индекс, но вы можете выполнить запрос поиска, чтобы получить родительские поля для каждого совпадения в результате. Поиск с использованием ИИ 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. Перейдите к следующему руководству и примеру кода, чтобы узнать больше об этом.