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

Nota

O Pesquisa de IA do Azure está disponível através do portal Azure, APIs REST e SDKs do Azure. Também sustenta o Foundry IQ, a camada de conhecimento gerida que transforma conteúdos empresariais em bases de conhecimento reutilizáveis e conscientes de permissões para agentes no portal Microsoft Foundry.

Se estiver a fragmentar conteúdo para um padrão RAG ou vetorização, pode especificar uma projeção de índice para controlar a indexação um-para-muitos, onde o conteúdo fonte (um) é projetado para um ou mais índices (muitos). A intenção de uma projeção de índice é determinar se elementos do documento pai, como um nome de ficheiro ou data de criação, devem ser controlados:

  • Repita para cada filho (chunk) num índice único
  • São indexados como documentos de pesquisa independentes no mesmo índice
  • Ou, são armazenados em índices separados

Recomendamos repetir os campos pais num único índice porque ter formas de documentos diferentes ou dividir o conteúdo em dois índices pode ser difícil de consultar, especialmente na pesquisa clássica, onde as junções de índice não são suportadas.

No ambiente do Pesquisa de IA do Azure, o chunking é realizado por competências, e por isso depende dos indexadores. Para definir uma projeção de índice, especifique-a num conjunto de competências.

Pré-requisitos

O conjunto de competências contém a projeção do indexador que molda os dados para a indexação um-para-muitos. Um conjunto de competências pode também incluir outras competências, como uma competência de incorporação AzureOpenAIEmbedding, se o seu cenário incluir vetorização integrada.

Escolha uma abordagem

As projeções de índice geram documentos "filhos" (chunks) para cada documento "pai". Escolha como lidar com o conteúdo dos pais:

Abordagem Descrição Configuração
Índice único, campos pais repetidos (recomendado) Os campos pai repetem-se para cada fragmento. Todos os documentos têm uma forma uniforme. Defina tanto o indexador targetIndexName como a projeção targetIndexName de índice para o mesmo índice. Definir projectionMode para skipIndexingParentDocuments.
Índice único, formas mistas de documentos Documentos principais e documentos de fragmentos coexistem. Os documentos principais têm campos de segmentos nulos. Defina ambos os valores targetIndexName para o mesmo índice. Define projectionMode para includeIndexingParentDocuments (ou omite, pois é o padrão).
Dois ou mais índices separados Índice pai para consultas de metadados, índice filho para pesquisa. Sem junções durante o tempo de consulta. Defina o indexador targetIndexName para o índice principal. Defina a projeção targetIndexName do índice para o índice filho. O selectors array determina a quantidade e composição do índice filho.

Para a maioria dos cenários RAG, usa a primeira abordagem. Veja o exemplo clássico RAG.

  1. Crie um índice concebido para fragmentos, incluindo campos parentais.
  2. Crie um conjunto de competências com uma habilidade de chunking e indexProjections.
  3. Crie um indexador que aponte para a sua fonte de dados suportada.

Se a sua fonte de dados suportar o rastreio de alterações, o indexador sincroniza as alterações automaticamente.

Crie um índice para indexação de um para muitos

Quer você crie um único índice para blocos que repetem valores de pais, ou índices separados para a colocação de campos de relação pai-filho, o índice primário usado para pesquisa é construído em torno dos blocos de dados. O esquema índice deve ter os seguintes campos:

  • Um campo de chave de documento que identifica de forma única cada documento. Deve ser definido como tipo Edm.String com o analisador keyword.

  • Um campo que associa cada bloco ao seu pai. Deve ser do tipo Edm.String. Não pode ser o campo da chave do documento, e deve ter filterable sido definido como verdadeiro. É referido como parent_id nos exemplos e como um valor-chave projetado neste artigo.

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

Deve existir um índice no serviço de pesquisa antes de criar o conjunto de competências ou executar o indexador. O selectors que defines no conjunto de competências deve incluir essas áreas.

Esquema de índice único incluindo campos pai e filho

Um único índice projetado em torno de chunks, no qual o conteúdo pai se repete para cada chunk, é o padrão predominante para cenários de pesquisa RAG e vetorial. A possibilidade de associar o conteúdo pai correto a cada bloco é ativada por intermédio de projeções de indexação.

O esquema seguinte é um exemplo que cumpre os requisitos para projeções de índice. Neste exemplo:

  • Os campos pais são o parent_id e o título, e repetem-se para cada bloco
  • Os campos filhos são os blocos vectoriais e não vetoriais. A chunk_id é o ID do documento deste índice.

Pode usar o portal Azure, APIs REST ou um SDK do Azure para criar um índice.

Use um cliente REST ou o portal Azure Adicionar índice ação e opção 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 competências

As projeções de índice são definidas dentro de uma definição de conjunto de competências e são principalmente definidas como um array de selectors, onde cada seletor corresponde a um índice alvo diferente no serviço de pesquisa. Esta secção começa com sintaxe e exemplos para contexto, seguida de referência de parâmetros.

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

Aqui está um exemplo de payload para uma definição de projeções de índice que pode usar para projetar páginas individuais produzidas pela capacidade Text Split como documentos independentes no índice de pesquisa.

Se o documento pai contiver metadados de permissão para acesso ao nível do documento, como metadata_user_ids, , ou metadata_spo_site_url, inclua esses campos em mappingsmetadata_group_ids. Cada bloco deve herdá-los para que se apliquem os filtros de permissões no momento da consulta. Para mais informações, consulte Escolher onde preencher os campos ACL (pré-visualização).

"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âmetros

Parâmetros de projeção de índice Definição
selectors Um array com parâmetros para o corpus de pesquisa principal, geralmente o índice concebido em torno de blocos. Pode enviar conteúdo para vários índices filhos ao especificar múltiplos seletores. Os esquemas de índice devem existir no serviço de pesquisa antes de executares o indexador.
parameters Um dicionário de parâmetros com propriedades de configuração específicas para a projeção de índices.

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

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

O melhor valor para este parâmetro é skipIndexingParentDocuments. Deves usá-lo quando os documentos em blocos forem o principal alvo da pesquisa.

Se não definires skipIndexingParentDocuments para projectionMode, recebes includeIndexingParentDocuments automaticamente porque é o padrão. Adiciona documentos de pesquisa extra no teu índice que são nulos para blocos, mas preenchidos com conteúdo específico dos pais. Por exemplo, se cinco PDFs contribuírem com 100 blocos para o índice, então o número de documentos no índice é 105. Os cinco documentos criados para campos pais têm nulos para campos de blocos (filhos), tornando-os substancialmente diferentes da maioria dos documentos no índice. Por esta razão, recomendamos projectionMode definir para skipIndexingParentDocuments.

Os selecionadores têm os seguintes elementos como parte da sua definição.

Selecionadores Definição
selectors.targetIndexName O nome do índice para o qual os dados do índice são projetados. Ou é o índice único segmentado com campos pai repetidos, ou é o índice filho se estiver a usar índices separados para conteúdo pai-filho.
selectors.parentKeyFieldName O nome do campo que fornece a chave para o documento pai.
selectors.sourceContext A anotação de enriquecimento que define a granularidade com que os dados devem ser mapeados em documentos de pesquisa individuais. Para mais informações, consulte Contexto de competências e linguagem de anotação de entrada.
selectors.mappings Um conjunto de mapeamentos de dados enriquecidos para campos no índice de pesquisa. Cada mapeamento consiste em:
name: O nome do campo no índice de pesquisa onde os dados devem ser indexados.
source: O caminho de anotação de enriquecimento a partir do qual os dados devem ser obtidos.

Cada mapping pode também definir recursivamente dados com um campo opcional sourceContext e um inputs, semelhante ao knowledge store ou ao Shaper Skill. Dependendo da sua aplicação, estes parâmetros permitem-lhe moldar os dados em campos de tipo Edm.ComplexType no índice de pesquisa. Alguns LLMs não aceitam um tipo complexo nos resultados de pesquisa, por isso o LLM que estás a usar determina se um mapeamento de tipos complexos é útil ou não.

O mappings parâmetro é importante. Deve mapear explicitamente todos os campos no índice filho, exceto os campos ID como a chave do documento e o ID do pai.

Este requisito contrasta com outras convenções de mapeamento de campos no Pesquisa de IA do Azure. Para alguns tipos de fontes de dados, o indexador pode mapear implicitamente campos com base em nomes semelhantes ou características conhecidas (por exemplo, os blob indexers usam o caminho único de armazenamento de metadados como chave padrão do documento). No entanto, para projeções de indexadores, é necessário especificar explicitamente cada mapeamento de campo do lado "muitos" da relação.

Importante

Não crie um mapeamento de campo para o campo da chave pai. Ao fazê-lo, perturba o acompanhamento de alterações e a atualização sincronizada dos dados.

Rever mapeamentos de campos

Os indexadores estão afiliados a três tipos diferentes de mapeamentos de campo. Antes de executares o indexador, verifica os mapeamentos dos campos e sabe quando usar cada tipo.

Os mapeamentos de campos são definidos num indexador e usados para mapear um campo fonte para um campo de índice. Os mapeamentos de campo são usados para caminhos de dados que recolhem dados da fonte e os encaminham para indexação, sem uma etapa intermédia de processamento de competências. Normalmente, um indexador pode mapear automaticamente campos com o mesmo nome e tipo. Mapeamentos explícitos de campos só são necessários quando há discrepâncias. Na indexação um-para-muitos e nos padrões discutidos até agora, poderá não precisar de mapeamentos de campos.

Os mapeamentos de campo de saída são definidos num indexador e usados para mapear conteúdo enriquecido gerado por um conjunto de competências para um campo no índice principal. Os blocos são considerados conteúdo enriquecido devido à criação por uma habilidade (Text Split), mas não necessita de um mapeamento de campo de saída para blocos, nem para projeções de índice definidas por um mapeamento de seletor.

Selectors.mappings são definidos num conjunto de competências e mapeiam para campos no índice filho. Nos casos em que o índice filho também inclui campos dos pais (como na solução de índice consolidado), deve configurar os mapeamentos de campo para todos os campos que contêm conteúdo, incluindo o campo de título dos pais, assumindo que deseja que o título apareça em cada documento segmentado. Se estiveres a usar índices pais e filhos separados, o seletor deve ter mapeamentos de campo apenas para os campos ao nível do filho.

Nota

Tanto os mapeamentos de campo de saída como os mapeamentos de seletores aceitam nós de árvore de documentos enriquecidos como entradas de origem. Saber como especificar um caminho para cada nó é essencial para configurar o caminho dos dados. Para saber mais sobre a sintaxe de caminhos, consulte Referência a caminho para nós enriquecidos e definição de conjunto de competências para exemplos.

Execute o indexador

Depois de criar uma fonte de dados, índices e conjunto de competências, está pronto para criar e executar o indexador. Este passo coloca o pipeline em execução.

Pode consultar o seu índice de pesquisa depois de concluído o processamento para testar a sua solução.

Ciclo de vida do conteúdo

Dependendo da fonte de dados subjacente, um indexador pode normalmente fornecer acompanhamento contínuo de alterações e deteção de eliminações. Esta secção explica o ciclo de vida do conteúdo da indexação um-para-muitos no que diz respeito à atualização de dados.

Para fontes de dados que fornecem monitorização de alterações e deteção de eliminação, um processo indexador pode detetar alterações nos seus dados de origem. Cada vez que executas o indexador e o conjunto de competências, as projeções do índice são atualizadas se o conjunto de competências ou os dados de origem subjacentes tiverem mudado. Quaisquer alterações detetadas pelo indexador são propagadas através do processo de enriquecimento para as projeções no índice, garantindo que os seus dados projetados são uma representação atual do conteúdo na fonte de dados de origem. A atividade de atualização de dados é capturada num valor-chave projetado para cada bloco. Este valor é atualizado quando os dados subjacentes mudam.

Nota

Embora possas editar manualmente os dados nos documentos projetados usando a API Index Push, deves evitar fazê-lo. As atualizações manuais de um índice são sobrescritas na próxima invocação do pipeline, assumindo que o documento nos dados de origem é atualizado e a fonte de dados tem ativado o rastreamento de alterações ou deteção de eliminação.

Conteúdo atualizado

Se adicionar novo conteúdo à sua fonte de dados, novos blocos ou documentos filhos são adicionados ao índice na próxima execução do indexador.

Se modificar o conteúdo existente na fonte de dados, os blocos são atualizados incrementalmente no índice de pesquisa se a fonte de dados que está a usar suportar o acompanhamento de modificações e a detecção de remoção. Por exemplo, se uma palavra ou frase mudar num documento, o bloco no índice alvo que contém essa palavra ou frase é atualizado na próxima execução do indexador. Outros tipos de atualizações, como alterar um tipo de campo e algumas atribuições, não são suportados para campos existentes. Para mais informações sobre atualizações permitidas, consulte Atualizar um esquema de índice.

Algumas fontes de dados como Armazenamento do Azure suportam o rastreio de alterações e eliminações automaticamente, com base no carimbo de data/hora. Outras fontes de dados como Microsoft OneLake, SQL do Azure ou Azure Cosmos DB devem ser configuradas para o acompanhamento de alterações.

Conteúdo eliminado

Se o conteúdo de origem já não existir (por exemplo, se o texto for encurtado para ter menos blocos), o documento filho correspondente no índice de pesquisa é eliminado. Os documentos filhos restantes também terão a sua chave atualizada para incluir um novo valor de hash, ainda que o seu conteúdo não tenha mudado.

Se um documento pai for completamente eliminado da fonte de dados, os documentos filhos correspondentes só são eliminados se a eliminação for detetada por um dataDeletionDetectionPolicy definido na definição da fonte de dados. Se não tiver um dataDeletionDetectionPolicy documento configurado e precisar de eliminar um documento pai da fonte de dados, então deve eliminar manualmente os documentos filhos se já não forem desejados.

Valor chave projetado

Para garantir a integridade dos dados para conteúdos atualizados e eliminados, a atualização de dados na indexação um-para-muitos depende de um valor-chave projetado no lado "muitos". Se estiver a utilizar vetorização integrada ou o assistente de importação de dados, o valor-chave projetado é o parent_id campo no lado fragmentado ou do lado "muitos" do índice.

Um valor-chave projetado é um identificador único que o indexador gera para cada documento. Garante a singularidade e permite que o acompanhamento de alterações e eliminações funcione corretamente. Esta chave contém os seguintes segmentos:

  • Um hash aleatório para garantir a unicidade. Este hash muda se o documento pai for atualizado em execuções posteriores do indexador.
  • A chave do documento principal.
  • O caminho de anotação de enriquecimento que identifica o contexto para o documento gerado.

Por exemplo, se dividir um documento pai com valor-chave "aa1b22c33" em quatro páginas, e depois projetar cada uma dessas páginas como um documento individual através 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, talvez resultando em mais páginas fragmentadas, o hash aleatório muda, mais páginas são adicionadas e o conteúdo de cada bloco é atualizado para corresponder ao que está no documento de origem.

Exemplo de índices separados entre pais e filhos

Esta secção mostra um exemplo para índices separados de pais e filhos. É um padrão pouco comum, mas é possível que tenhas requisitos de candidatura que sejam melhor cumpridos com esta abordagem. Neste cenário, está a projetar conteúdo de pai e filho em dois índices separados.

  1. Crie dois esquemas de índice.

    Cada esquema tem os campos para o seu grão particular, com o campo de ID pai comum a ambos os índices para uso numa consulta de consulta. O corpus principal de pesquisa é o índice secundário, mas pode lançar uma consulta de procura para recuperar os campos principais de cada correspondência no resultado. O Pesquisa de IA do Azure não suporta joins no momento das consultas, por isso o código da sua aplicação ou a camada de orquestração teria de fundir ou compilar resultados que possam ser passados para uma aplicação ou processo.

    O índice principal tem um campo parent_id e um título. A parent_id é a chave do documento. Não precisas de configuração de pesquisa vetorial a menos que queiras vetorizar campos ao 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 fragmentados, mais o campo parent_id. Se estiveres a usar vetorização integrada, perfis de pontuação, ranqueador semântico ou analisadores, deves definir estes 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 indexador para especificar o índice primário como destino.

    A definição do indexador especifica os componentes do pipeline. Na definição do indexador, o nome do índice a fornecer é o índice pai. Se precisar de mapeamentos de campo para os campos de nível superior, defina-os em outputFieldMappings. Para a indexação de um-para-muitos que utiliza índices separados, a definição do indexador pode ser semelhante a este exemplo.

    {
      "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. Acrescenta indexProjections ao teu conjunto de competências.

    Aqui está um exemplo de definição de projeção de índice que especifica o caminho de dados que o indexador deve usar para indexar conteúdo. Especifica o nome do índice filho na definição da projeção de índice e define os mapeamentos de cada campo filho ou campo ao nível de segmento. Este é o único local onde o nome do índice filho é especificado.

    Note que parameters é nulo e está a usar o padrão includeIndexingParentDocuments. O indexador preenche o índice pai. O selectors array é usado para projetar os documentos de blocos para o í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. Executa o indexador. Se já executou o indexador anteriormente, lembre-se de o reiniciar primeiro.

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

Próximo passo

Segmentação de dados e indexação um-para-muitos fazem parte do padrão clássico RAG no Pesquisa de IA do Azure. Continue para o tutorial e exemplo de código seguintes para saber mais sobre o tema.