Definir uma projeção de índice para indexação pai-filho

Nota

Pesquisa de IA do Azure  está disponível por meio do portal Azure, APIs REST e SDKs do Azure. Ele também sustenta o IQ do Foundry, a camada de conhecimento gerenciado que transforma o conteúdo da empresa em bases de conhecimento reutilizáveis e com reconhecimento de permissão para agentes no portal do Microsoft Foundry.

Se você estiver agrupando conteúdo para um padrão RAG ou vetorização, poderá especificar uma projeção de índice para controlar a indexação um para muitos, em que o conteúdo de origem (um) é projetado para um ou mais índices (muitos). A intenção de uma projeção de índice é controlar se os elementos do documento pai, como um nome de arquivo ou a data de criação:

  • Repetir para cada filho (ou bloco) em um único índice
  • São indexados como documentos de pesquisa autônomos no mesmo índice
  • Ou são ingeridos em índices separados

Recomendamos que se repita os campos pai em um único índice, porque usar formatos diferentes de documento ou dividir o conteúdo em dois índices pode dificultar a consulta, especialmente na pesquisa clássica, na qual não há suporte para as associações de índices.

Em Pesquisa de IA do Azure , o agrupamento é executado por habilidades e, portanto, depende de indexadores. Para definir uma projeção de índice, especifique-a em um conjunto de habilidades.

Pré-requisitos

O conjunto de habilidades contém a projeção do indexador que adapta os dados para a indexação um para muitos. Um conjunto de habilidades também pode ter outras habilidades, como uma habilidade de inserção, como AzureOpenAIEmbedding , se o cenário incluir vetorização integrada.

Escolher uma abordagem

As projeções de índice geram os documentos "filho" (blocos) para cada documento "pai". Escolha como lidar com o conteúdo principal:

Abordagem Descrição Configuração
Índice único, repetição de campos de pai (recomendado) Os campos pai são repetidos para cada bloco. Todos os documentos têm uma forma uniforme. Defina o indexador targetIndexName e a projeção targetIndexName de índice para o mesmo índice. Definir projectionMode como skipIndexingParentDocuments.
Índice único, formas de documento mistas Os documentos pai e documentos em blocos coexistem. Os documentos pai apresentam campos de bloco nulos. Defina ambos os targetIndexName valores para o mesmo índice. Defina projectionMode como includeIndexingParentDocuments (ou omita, pois é o padrão).
Dois ou mais índices separados Índice pai para consulta de metadados, índice filho para pesquisa. Nenhuma junção de tempo de consulta. Defina o indexador targetIndexName como índice pai. Defina a projeção de índice targetIndexName como índice filho. A matriz selectors determina a quantidade e a composição do índice filho.

Para a maioria dos cenários de RAG, use a primeira abordagem. Consulte o exemplo clássico de RAG.

  1. Crie um índice projetado para blocos, com campos principais incluídos.
  2. Crie um conjunto de habilidades com uma habilidade de blocos e indexProjections.
  3. Crie um indexador apontando para a fonte de dados com suporte.

Se a fonte de dados der suporte ao controle de alterações, o indexador sincronizará as alterações automaticamente.

Criar um índice para indexação um para muitos

Se você criar um índice para segmentos que repetem valores do pai ou índices separados para posicionamento de campo pai-filho, o índice primário usado para pesquisa é projetado em torno de segmentos de dados. O esquema de índice deve ter os seguintes campos:

  • Um campo de chave de documento que identifica exclusivamente cada documento. Ele deve ser definido como tipo Edm.String com o analisador keyword.

  • Um campo que associa cada bloco com seu elemento pai. Deve ser do tipo Edm.String. Ele não pode ser o campo de chave do documento e deve ter filterable sido definido como true. Ele é conhecido como parent_id nos exemplos e como um valor de chave projetado neste artigo.

  • Outros campos para conteúdo, como texto ou campos de partes vetorizados.

Um índice deve existir no serviço de pesquisa antes de criar o conjunto de habilidades ou executar o indexador. O selectors que você definir no conjunto de habilidades deve incluir esses campos.

Esquema de índice único inclusive de campos pai e filho

Um único índice criado em torno de segmentos, com conteúdo pai que se repete para cada segmento, é o padrão predominante para cenários de pesquisa de RAG e busca em vetores. A capacidade de associar o conteúdo pai correto a cada segmento é ativado por projeções de índice.

O esquema a seguir é um exemplo que atende aos requisitos para projeções de índice. Neste exemplo:

  • Os campos pai são parent_id e título, e eles se repetem para cada segmento
  • Os campos filho são os segmentos vetoriais e não vetoriais. O chunk_id é a ID do documento desse índice.

Você pode usar o portal Azure, AS APIs REST ou um SDK do Azure para criar um índice.

Use um cliente REST ou a opção Azure portal Add index e JSON para criar o índice.

{
    "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"}]
    }
}

Adicionar projeções de índice a um conjunto de habilidades

As projeções de índice são definidas dentro de uma definição de conjunto de habilidades e são definidas principalmente como uma matriz de selectors, em que cada seletor corresponde a um índice de destino diferente no serviço de pesquisa. Esta seção começa com sintaxe e exemplos de contexto, seguidos de referência de parâmetro.

As projeções de índice geralmente estão disponíveis. Recomendamos a API estável mais recente:

Aqui está um exemplo de conteúdo para uma definição de projeções de índice que você pode usar para projetar a saída de páginas individuais pela habilidade de Divisão de Texto como seus próprios documentos no índice de pesquisa.

Se o documento pai tiver metadados de permissão para acesso no nível do documento, como metadata_user_ids, metadata_group_idsou metadata_spo_site_url, inclua esses campos em mappings. Cada grupo deve herdá-los para que os filtros de permissão no momento da consulta sejam aplicados. Para obter mais informações, consulte Escolher onde preencher os campos de ACL (versão prévia).

"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"
    }
}

Referência de parâmetro

Parâmetros de projeção de índice Definição
selectors Uma matriz com parâmetros para o corpus de pesquisa principal, geralmente o índice projetado em torno dos blocos. Para enviar o conteúdo a vários índices filhos, especifique múltiplos seletores. Os esquemas de índice devem existir no serviço de pesquisa antes de executar o indexador.
parameters Um dicionário de parâmetros de propriedades de configuração específicas da projeção de índice.

Os parâmetros têm os seguintes elementos como parte de sua definição.

Parâmetros Definição
parameters.projectionMode Um parâmetro opcional que fornece instruções ao indexador. Os valores válidos incluem includeIndexingParentDocuments e skipIndexingParentDocuments.

O melhor valor para esse parâmetro é skipIndexingParentDocuments. Você deve usá-lo quando documentos em partes são o destino de pesquisa primário.

Se você não definir skipIndexingParentDocuments para projectionMode, você obterá includeIndexingParentDocuments automaticamente porque ele é o padrão. Ela adiciona documentos de pesquisa extras em seu índice que são nulos para os blocos, mas preenchidos com conteúdo específico do pai. Por exemplo, se cinco PDFs contribuem com 100 partes para o índice, o número de documentos no índice será 105. Os cinco documentos criados para os campos pai têm nulos para os campos de bloco (filho), tornando-os substancialmente diferentes da maioria dos documentos presentes no índice. Por esse motivo, recomendamos projectionMode definir como skipIndexingParentDocuments.

Os seletores têm os seguintes elementos como parte de sua definição.

Seletores Definição
selectors.targetIndexName O nome do índice no qual os dados de índice são projetados. Ele será o índice único segmentado com campos pai repetitivos, ou será o índice filho se você estiver usando índices separados para o conteúdo pai-filho.
selectors.parentKeyFieldName O nome do campo que fornece a chave do documento pai.
selectors.sourceContext A anotação de enriquecimento que define o nível de granularidade para mapear dados em documentos de pesquisa individuais. Para obter mais informações, consulte o contexto de habilidade e a linguagem de anotação de entrada.
selectors.mappings Uma matriz de mapeamentos de dados enriquecidos para campos no índice de pesquisa. Cada mapeamento consiste em:
name: o nome do campo no índice de pesquisa no qual os dados devem ser indexados.
source: o caminho da anotação de enriquecimento de onde dados devem ser extraídos.

Cada mapping também pode definir dados recursivamente com campos sourceContext e inputs opcionais, semelhante ao repositório de conhecimento ou ao Shaper Skill. Dependendo do aplicativo, esses parâmetros permitem que você modele dados em campos de tipo Edm.ComplexType no índice de pesquisa. Algumas LLMs não aceitam um tipo complexo nos resultados da pesquisa, portanto, a LLM que você está usando determina se um mapeamento de tipo complexo é útil ou não.

O mappings parâmetro é importante. Você deve mapear de forma explícita cada campo no índice filho, exceto para os campos de ID, como a chave de documento e a ID pai.

Esse requisito contrasta com outras convenções de mapeamento de campo em Pesquisa de IA do Azure . Para alguns tipos de fonte de dados, o indexador pode mapear implicitamente campos com base em nomes semelhantes ou características conhecidas (por exemplo, indexadores de blob usam o caminho de armazenamento de metadados exclusivo como a chave de documento padrão). Mas, para projeções de indexador, você deve especificar de forma explicita cada mapeamento de campo no lado "muitos" do relacionamento.

Importante

Não crie um mapeamento de campo para o campo de chave pai. Isso interrompe o controle de alterações e a atualização de dados sincronizada.

Examinar mapeamentos de campo

Os indexadores são afiliados a três tipos diferentes de mapeamentos de campo. Antes de executar o indexador, verifique os mapeamentos de campo e saiba quando usar cada tipo.

Os mapeamentos de campo são definidos em um indexador e usados para mapear um campo de origem para um campo de índice. Os mapeamentos de campo são usados para caminhos de dados que levantam dados da origem e os transmitem para indexação, sem nenhuma etapa de processamento de habilidades intermediárias. Normalmente, um indexador pode mapear automaticamente campos com o mesmo nome e tipo. Mapeamentos de campo explícitos só são necessários quando há discrepâncias. Na indexação um para muitos e nos padrões discutidos até agora, talvez você não precise de mapeamentos de campo.

Os mapeamentos de campo de saída são definidos em um indexador e usados para mapear o conteúdo enriquecido gerado por um conjunto de habilidades para um campo no índice principal. Os fragmentos são considerados conteúdo enriquecido devido à criação por uma habilidade (Divisão de Texto), mas você não precisa de um mapeamento de campo de saída para fragmentos ou para projeções de índice, que são definidas por mapeamento de seletor.

Selectors.mappings é definido em um conjunto de habilidades e é mapeado para os campos no índice filho. Quando o índice filho também inclui campos pai (como na solução de índice consolidado), você deve definir mapeamentos de campo para cada campo com conteúdo, incluindo o campo de título de nível pai, supondo que o título deva aparecer em cada documento segmentado. Se você estiver usando índices pai e filho separados, o seletor deverá ter mapeamentos de campo apenas para os campos de nível filho.

Nota

Mapeamentos de campo de saída e de seletor aceitam nós de árvore de documentos enriquecidos como entradas de origem. É essencial saber como especificar um caminho para cada nó para configurar o caminho de dados. Para saber mais sobre a sintaxe do caminho, confira Referenciar um caminho para nós enriquecidos e definição do conjunto de habilidades para obter exemplos.

Executar o indexador

Depois de criar uma fonte de dados, índices e conjunto de habilidades, você estará pronto para criar e executar o indexador. Esta etapa ativa a execução do pipeline.

Você pode consultar o índice de pesquisa após a conclusão do processamento para testar sua solução.

Ciclo de vida do conteúdo

Dependendo da fonte de dados subjacente, um indexador geralmente pode fornecer controle de alterações e detecção de exclusão contínuas. Esta seção explica o ciclo de vida de conteúdo da indexação um para muitos em relação à atualização de dados.

Para fontes de dados que fornecem controle de alterações e detecção de exclusão, um processo de indexador pode detectar alterações em seus dados de origem. Sempre que você executar o indexador e o conjunto de habilidades, as projeções de índice serão atualizadas se o conjunto de habilidades ou os dados de origem subjacentes forem alterados. Todas as alterações captadas pelo indexador são propagadas por meio do processo de enriquecimento para as projeções no índice, garantindo que os dados projetados sejam uma representação atual do conteúdo na fonte de dados de origem. A atividade de atualização de dados é capturada em um valor chave projetado para cada bloco. Esse valor é atualizado quando os dados subjacentes são alterados.

Nota

Embora você possa editar manualmente os dados nos documentos projetados usando a API de push de índice, evite fazer isso. As atualizações manuais em um índice são sobrescritas na próxima invocação do pipeline, pressupondo que o documento em dados de origem seja atualizado e que a fonte de dados tenha o controle de alterações ou a detecção de exclusões ativada.

Conteúdo atualizado

Se você adicionar um novo conteúdo à sua fonte de dados, novos fragmentos ou documentos filhos serão adicionados ao índice na próxima execução do processo de indexação.

Se você modificar o conteúdo existente na fonte de dados, as partes serão atualizadas incrementalmente no índice de pesquisa se a fonte de dados que você está usando oferecer suporte à detecção de controle de alterações e exclusão. Por exemplo, se uma palavra ou frase for alterada em um documento, a parte no índice de destino que contém essa palavra ou frase será atualizada na próxima execução do indexador. Outros tipos de atualizações, como alterar um tipo de campo e algumas atribuições, não têm suporte para campos existentes. Para obter mais informações sobre atualizações permitidas, consulte Atualizar um esquema de índice.

Algumas fontes de dados, como Armazenamento do Azure, dão suporte ao rastreamento de alterações e exclusões por padrão, com base no timestamp. Outras fontes de dados, como Microsoft OneLake, SQL do Azure ou Azure Cosmos DB devem ser configuradas para controle de alterações.

Conteúdo excluído

Se o conteúdo de origem não existir mais (por exemplo, se o texto for reduzido para ter menos partes), o documento filho correspondente no índice de pesquisa será excluído. Os documentos filho restantes também recebem a chave atualizada para incluir um novo valor de hash, mesmo que o conteúdo não tenha sido alterado de outra forma.

Se um documento pai for completamente excluído da fonte de dados, os documentos filho correspondentes serão excluídos somente se a exclusão for detectada por uma dataDeletionDetectionPolicy definida na definição da fonte de dados. Se não tiver dataDeletionDetectionPolicy configurado e precisar excluir um documento pai da fonte de dados, você deve excluir manualmente os documentos filho caso não sejam mais necessários.

Valor de chave projetado

Para garantir a integridade dos dados para conteúdo atualizado e excluído, a atualização de dados na indexação um para muitos depende de um valor de chave projetado no lado "muitos". Se você estiver usando a vetorização integrada ou o assistente de importação de dados, o valor da chave projetada é o campo parent_id no lado em partes ou "muitos" do índice.

Um valor de chave projetado é um identificador exclusivo que o indexador gera para cada documento. Ele garante a exclusividade e permite que o controle de alteração e exclusão funcione corretamente. Essa chave contém os seguintes segmentos:

  • Um hash aleatório para garantir a exclusividade. Esse hash mudará se o documento pai for atualizado em execuções do indexador subsequentes.
  • A chave do documento pai.
  • O caminho de anotações de enriquecimento que identifica o contexto do documento gerado.

Por exemplo, se você dividir um documento pai com o valor de chave "aa1b22c33" em quatro páginas e cada uma dessas páginas for projetada como seu próprio documento por meio de projeções de índice:

  • aa1b22c33
  • aa1b22c33_pages_0
  • aa1b22c33_pages_1
  • aa1b22c33_pages_2

Se o documento pai for atualizado nos dados de origem, possivelmente resultando em páginas mais divididas, as alterações no hash aleatório fazem com que mais páginas sejam adicionadas e o conteúdo de cada parte seja atualizado para corresponder ao que está no documento de origem.

Exemplo de índices pai-filho separados

Esta seção mostra um exemplo para índices pai e filho separados. É um padrão incomum, mas é possível que você tenha requisitos de aplicativo que sejam melhor atendidos usando essa abordagem. Nesse cenário, você está projetando conteúdo pai-filho em dois índices separados.

  1. Crie dois esquemas de índice.

    Cada esquema tem os campos para a granularidade específica, com o campo ID pai comum aos dois índices para uso em uma consulta de pesquisa. O corpus de pesquisa principal é o índice filho, mas você pode fazer uma consulta de pesquisa a fim de recuperar os campos pai para cada correspondência no resultado. Pesquisa de IA do Azure  não dá suporte a junções no momento da consulta, portanto, o código do aplicativo ou a camada de orquestração precisariam mesclar ou agrupar resultados que possam ser passados para um aplicativo ou processo.

    O índice pai possui um campo chamado parent_id e um título. O parent_id é a chave do documento. Você não precisa de configuração de pesquisa de vetor, a menos que deseje vetorizar campos no nível do documento pai.

    {
        "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}
        ]
    }
    

    O índice filho tem os campos segmentados, além do campo parent_id. Se você estiver usando a vetorização integrada, perfis de pontuação, o classificador semântico ou analisadores, você os definirá no índice filho.

    {
        "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. Atualize o indexador para especificar o parent-index como o alvo.

    A definição do indexador especifica os componentes do pipeline. Na definição do indexador, o nome do índice a ser fornecido é o índice pai. Se você precisar de mapeamentos de campo para os campos de nível pai, defina-os em outputFieldMappings. Para a indexação um para muitos que utiliza índices distintos, a definição do indexador terá a aparência do exemplo a seguir.

    {
      "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. Adicione indexProjections ao conjunto de habilidades.

    Aqui está um exemplo de uma definição de projeção de índice que especifica o caminho de dados que o indexador deve usar para indexar o conteúdo. Ele especifica o nome do índice filho na definição de projeção de índice, e também especifica os mapeamentos de cada campo filho ou em nível de segmento. Esse é o único lugar em que o nome do índice filho é especificado.

    Observe que parameters é nulo e está usando o padrão includeIndexingParentDocuments. O indexador preenche o índice pai. A matriz selectors é usada para projetar os documentos de bloco no índice filho.

    "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. Execute o indexador. Se você executou anteriormente o indexador, lembre-se de redefini-lo primeiro.

    Você deve ter dois índices preenchidos com o conteúdo apropriado. Consulte os índices no Gerenciador de Pesquisa para verificar se cada um tem o conteúdo correto.

Próxima etapa

O agrupamento de dados e a indexação um-para-muitos fazem parte do padrão RAG clássico na Pesquisa de IA do Azure. Continue no tutorial a seguir e no exemplo de código para saber mais sobre ele.