Condividi tramite


Definire una proiezione di indice per l'indicizzazione padre-figlio

Per gli indici contenenti documenti in blocchi, una proiezione di indice specifica il mapping del contenuto padre-figlio ai campi in un indice di ricerca per l'indicizzazione uno-a-molti. Tramite una proiezione di indice, è possibile inviare contenuto a:

  • Un singolo indice, in cui i campi padre si ripetono per ogni blocco, ma la granularità dell'indice è a livello di blocco. L'esercitazione rag è un esempio di questo approccio.

  • Due o più indici, in cui l'indice padre contiene campi correlati al documento padre e l'indice figlio è organizzato in blocchi. L'indice figlio è il corpus di ricerca primario, ma l'indice padre può essere usato per le query di ricerca quando si desidera recuperare i campi padre di un determinato blocco o per query indipendenti.

La maggior parte delle implementazioni è un singolo indice organizzato intorno a blocchi con campi padre, ad esempio il nome file del documento, ripetendo per ogni blocco. Tuttavia, il sistema è progettato per supportare indici figlio separati e multipli, se necessario. Ricerca di intelligenza artificiale di Azure non supporta i join di indici, quindi il codice dell'applicazione deve gestire l'indice da usare.

Una proiezione di indice è definita in un set di competenze. È responsabile del coordinamento del processo di indicizzazione che invia blocchi di contenuto a un indice di ricerca, insieme al contenuto padre associato a ogni blocco. Migliora il funzionamento della suddivisione in blocchi di dati nativi offrendo altre opzioni per controllare il modo in cui viene indicizzato il contenuto padre-figlio.

Questo articolo illustra come creare lo schema di indice e i modelli di proiezione dell'indicizzatore per l'indicizzazione uno-a-molti.

Prerequisiti

  • Pipeline di indicizzazione basata su indicizzatore.

  • Indice (uno o più) che accetta l'output della pipeline dell'indicizzatore.

  • Un'origine dati supportata con contenuto che si vuole suddividere in blocchi. Può essere contenuto vettoriale o non operatore.

  • Competenza che suddivide il contenuto in blocchi, ovvero la competenza Suddivisione testo o una competenza personalizzata che fornisce funzionalità equivalenti.

Il set di competenze contiene la proiezione dell'indicizzatore che forma i dati per l'indicizzazione uno-a-molti. Un set di competenze può avere anche altre competenze, ad esempio una competenza di incorporamento come AzureOpenAIEmbedding se lo scenario include la vettorizzazione integrata.

Dipendenza dall'elaborazione dell'indicizzatore

L'indicizzazione uno-a-molti dipende dai set di competenze e dall'indicizzazione basata su indicizzatore che include i quattro componenti seguenti:

  • Origine dati
  • Uno o più indici per il contenuto ricercabile
  • Set di competenze che contiene una proiezione di indice*
  • Indicizzatore

I dati possono provenire da qualsiasi origine dati supportata, ma si presuppone che il contenuto sia sufficientemente grande che si vuole suddividere in blocchi e che il motivo della suddivisione in blocchi sia che si stia implementando un modello RAG che fornisce dati di base a un modello di chat. In alternativa, si sta implementando la ricerca vettoriale ed è necessario soddisfare i requisiti di dimensioni di input inferiori dei modelli di incorporamento.

Gli indicizzatori caricano i dati indicizzati in un indice predefinito. La definizione dello schema e l'uso di uno o più indici è la prima decisione da prendere in uno scenario di indicizzazione uno-a-molti. La sezione successiva illustra la progettazione dell'indice.

Creare un indice per l'indicizzazione uno-a-molti

Indipendentemente dal fatto che si crei un indice per blocchi che ripetano valori padre o indici separati per il posizionamento dei campi padre-figlio, l'indice primario usato per la ricerca è progettato per i blocchi di dati. Deve avere i campi seguenti:

  • Campo chiave documento che identifica in modo univoco ogni documento. Deve essere definito come tipo Edm.String con l'analizzatore keyword .

  • Campo che associa ogni blocco al relativo elemento padre. Deve essere di tipo Edm.String. Non può essere il campo chiave del documento e deve essere filterable impostato su true. Viene definita parent_id negli esempi e come valore chiave proiettato in questo articolo.

  • Altri campi per il contenuto, ad esempio testo o campi di blocchi vettorializzati.

Prima di creare il set di competenze o eseguire l'indicizzatore, è necessario che nel servizio di ricerca esista un indice.

Schema di indice singolo inclusivo dei campi padre e figlio

Un singolo indice progettato per i blocchi con contenuto padre ripetuto per ogni blocco è il modello predominante per gli scenari di ricerca RAG e vettoriali. La possibilità di associare il contenuto padre corretto a ogni blocco è abilitata tramite proiezioni di indice.

Lo schema seguente è un esempio che soddisfa i requisiti per le proiezioni di indici. In questo esempio i campi padre sono i parent_id e il titolo. I campi figlio sono i blocchi vettoriali e non tori. Il chunk_id è l'ID documento di questo indice. Il parent_id e il titolo vengono ripetuti per ogni blocco nell'indice.

È possibile usare l'portale di Azure, le API REST o Azure SDK per creare un indice.

{
    "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": "hsnw", "kind": "hnsw", "hnswParameters": {}}],
        "profiles": [{"name": "hsnw", "algorithm": "hnsw"}]
    }
}

Aggiungere proiezioni di indice a un set di competenze

Le proiezioni di indice vengono definite all'interno di una definizione del set di competenze e sono definite principalmente come matrice di selectors, dove ogni selettore corrisponde a un indice di destinazione diverso nel servizio di ricerca. Questa sezione inizia con la sintassi e gli esempi per il contesto, seguiti dal riferimento ai parametri.

Scegliere una scheda per la sintassi dell'API. Attualmente non è disponibile alcun supporto per la configurazione delle proiezioni, oltre alla modifica della definizione JSON del set di competenze. Vedere l'esempio REST per JSON.

Le proiezioni di indici sono disponibili a livello generale. È consigliabile usare l'API stabile più recente:

Di seguito è riportato un esempio di payload per una definizione di proiezioni di indice che è possibile usare per proiettare singoli output di pagine dalla competenza Divisione testo come documenti personalizzati nell'indice di ricerca.

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

Riferimento ai parametri

Parametri di proiezione dell'indice Definizione
selectors Parametri per il corpus di ricerca principale, in genere quello progettato intorno ai blocchi.
projectionMode Parametro facoltativo che fornisce istruzioni all'indicizzatore. L'unico valore valido per questo parametro è skipIndexingParentDocumentse viene usato quando l'indice di blocco è il corpus di ricerca primario ed è necessario specificare se i campi padre vengono indicizzati come documenti di ricerca aggiuntivi all'interno dell'indice in blocchi. Se non si imposta skipIndexingParentDocuments, si ottengono documenti di ricerca aggiuntivi nell'indice null per blocchi, ma popolati solo con campi padre. Ad esempio, se cinque documenti contribuiscono a 100 blocchi all'indice, il numero di documenti nell'indice è 105. I cinque documenti creati o i campi padre hanno valori Null per i campi blocchi (figlio), rendendoli sostanzialmente diversi dalla maggior parte dei documenti nell'indice. È consigliabile projectionMode impostare su skipIndexingParentDocument.

I selettori hanno i parametri seguenti come parte della relativa definizione.

Parametri dei selettori Definizione
targetIndexName Nome dell'indice in cui vengono proiettati i dati dell'indice. Si tratta del singolo indice in blocchi con campi padre ripetuti oppure dell'indice figlio se si usano indici separati per il contenuto padre-figlio.
parentKeyFieldName Nome del campo che fornisce la chiave per il documento padre.
sourceContext Annotazione di arricchimento che definisce la granularità in corrispondenza della quale eseguire il mapping dei dati in singoli documenti di ricerca. Per altre informazioni, vedere Contesto delle competenze e linguaggio di annotazione di input.
mappings Matrice di mapping di dati arricchiti ai campi nell'indice di ricerca. Ogni mapping è costituito da:
name: nome del campo nell'indice di ricerca in cui devono essere indicizzati i dati.
source: percorso dell’annotazione di arricchimento da cui devono essere estratti i dati.

Ogni mapping può anche definire ricorsivamente i dati con un campo facoltativo sourceContext e inputs, simile all'archivio conoscenze o alla funzionalità Forma. A seconda dell'applicazione, questi parametri consentono di modellare i dati in campi di tipo Edm.ComplexType nell'indice di ricerca. Alcuni llms non accettano un tipo complesso nei risultati della ricerca, quindi l'LLM in uso determina se un mapping di tipi complessi è utile o meno.

Il mappings parametro è importante. È necessario eseguire il mapping esplicito di ogni campo nell'indice figlio, ad eccezione dei campi ID, ad esempio la chiave documento e l'ID padre.

Questo requisito è in contrasto con altre convenzioni di mapping dei campi in Ricerca di intelligenza artificiale di Azure. Per alcuni tipi di origine dati, l'indicizzatore può mappare in modo implicito i campi in base a nomi simili o caratteristiche note, ad esempio gli indicizzatori BLOB usano il percorso di archiviazione dei metadati univoco come chiave documento predefinita. Tuttavia, per le proiezioni dell'indicizzatore, è necessario specificare in modo esplicito ogni mapping dei campi sul lato "molti" della relazione.

Non creare un mapping dei campi per il campo chiave padre. In questo modo, il rilevamento delle modifiche e l'aggiornamento dati sincronizzato vengono interrotti.

Gestione di documenti padre

Ora che sono stati visualizzati diversi modelli per gli indici uno-a-molti, è possibile confrontare le differenze principali su ogni opzione. Le proiezioni di indici generano in modo efficace documenti "figlio" per ogni documento "padre" che viene eseguito attraverso un set di competenze. Sono disponibili diverse opzioni per la gestione dei documenti "padre".

  • Per inviare documenti padre e figlio a indici separati, impostare per la targetIndexName definizione dell'indicizzatore sull'indice padre e impostare targetIndexName nel selettore di proiezione dell'indice sull'indice figlio.

  • Per mantenere i documenti padre e figlio nello stesso indice, impostare l'indicizzatore targetIndexName e la proiezione targetIndexName dell'indice sullo stesso indice.

  • Per evitare di creare documenti di ricerca padre e garantire che l'indice contenga solo documenti figlio di una granularità uniforme, impostare sia per targetIndexName la definizione dell'indicizzatore che per il selettore sullo stesso indice, ma aggiungere un oggetto aggiuntivo parameters dopo selectors, con una projectionMode chiave impostata su skipIndexingParentDocuments, come illustrato di seguito:

    "indexProjections": {
        "selectors": [
            ...
        ],
        "parameters": {
            "projectionMode": "skipIndexingParentDocuments"
        }
    }
    

Esaminare i mapping dei campi

Gli indicizzatori sono associati a tre diversi tipi di mapping dei campi. Prima di eseguire l'indicizzatore, controllare i mapping dei campi e sapere quando usare ogni tipo.

I mapping dei campi vengono definiti in un indicizzatore e usati per eseguire il mapping di un campo di origine a un campo di indice. I mapping dei campi vengono usati per i percorsi di dati che consentono di sollevare i dati dall'origine e di passarli per l'indicizzazione, senza alcun passaggio di elaborazione delle competenze intermedie. In genere, un indicizzatore può eseguire automaticamente il mapping dei campi con lo stesso nome e tipo. I mapping espliciti dei campi sono necessari solo quando si verificano discrepanze. Nell'indicizzazione uno-a-molti e nei modelli illustrati finora, potrebbe non essere necessario eseguire mapping dei campi.

I mapping dei campi di output vengono definiti in un indicizzatore e usati per eseguire il mapping del contenuto arricchito generato da un set di competenze a un campo nell'indice principale. Negli schemi uno-a-molti trattati in questo articolo, si tratta dell'indice padre in una soluzione a due indici. Negli esempi illustrati in questo articolo, l'indice padre è di tipo sparse, con solo un campo del titolo e tale campo non viene popolato con contenuto dall'elaborazione del set di competenze, quindi non viene eseguito un mapping dei campi di output.

I mapping dei campi di proiezione dell'indicizzatore vengono usati per eseguire il mapping del contenuto generato dal set di competenze ai campi nell'indice figlio. Nei casi in cui l'indice figlio include anche campi padre (come nella soluzione di indice consolidata), è necessario configurare i mapping dei campi per ogni campo con contenuto, incluso il campo del titolo a livello padre, presupponendo che il titolo venga visualizzato in ogni documento in blocchi. Se si usano indici padre e figlio separati, le proiezioni dell'indicizzatore devono avere mapping dei campi solo per i campi a livello figlio.

Nota

Sia i mapping dei campi di output che i mapping dei campi di proiezione dell'indicizzatore accettano nodi dell'albero dei documenti arricchiti come input di origine. Sapere come specificare un percorso per ogni nodo è essenziale per configurare il percorso dati. Per altre informazioni sulla sintassi del percorso, vedere Fare riferimento a un percorso per i nodi arricchiti e la definizione del set di competenze per esempi.

Eseguire l'indicizzatore

Dopo aver creato un'origine dati, indici e set di competenze, è possibile creare ed eseguire l'indicizzatore. Questo passaggio inserisce la pipeline in esecuzione.

È possibile eseguire query nell'indice di ricerca dopo la conclusione dell'elaborazione per testare la soluzione.

Ciclo di vita del contenuto

A seconda dell'origine dati sottostante, un indicizzatore può in genere fornire il rilevamento continuo delle modifiche e l'eliminazione. Questa sezione illustra il ciclo di vita del contenuto dell'indicizzazione uno-a-molti in relazione all'aggiornamento dati.

Per le origini dati che forniscono il rilevamento delle modifiche e l'eliminazione, un processo dell'indicizzatore può rilevare le modifiche nei dati di origine. Ogni volta che si eseguono l'indicizzatore e il set di competenze, le proiezioni di indice vengono aggiornate se il set di competenze o i dati di origine sono stati modificati. Tutte le modifiche rilevate dall'indicizzatore vengono propagate tramite il processo di arricchimento alle proiezioni nell'indice, assicurandosi che i dati proiettati siano una rappresentazione attuale del contenuto nell'origine dati di origine. L'attività di aggiornamento dati viene acquisita in un valore di chiave proiettato per ogni blocco. Questo valore viene aggiornato quando i dati sottostanti cambiano.

Nota

Anche se è possibile modificare manualmente i dati nei documenti proiettati usando l'API push dell'indice, è consigliabile evitare di farlo. Gli aggiornamenti manuali a un indice vengono sovrascritti nella chiamata alla pipeline successiva, presupponendo che il documento nei dati di origine venga aggiornato e che l'origine dati disponga del rilevamento delle modifiche o dell'eliminazione abilitato.

Contenuto aggiornato

Se si aggiunge nuovo contenuto all'origine dati, nuovi blocchi o documenti figlio vengono aggiunti all'indice nella successiva esecuzione dell'indicizzatore.

Se si modifica il contenuto esistente nell'origine dati, i blocchi vengono aggiornati in modo incrementale nell'indice di ricerca se l'origine dati in uso supporta il rilevamento delle modifiche e l'eliminazione. Per l'esamemple, se una parola o una frase cambia in un documento, il blocco nell'indice di destinazione che contiene tale parola o frase viene aggiornato alla successiva esecuzione dell'indicizzatore. Altri tipi di aggiornamenti, ad esempio la modifica di un tipo di campo e alcune attribuzioni, non sono supportati per i campi esistenti. Per altre informazioni sugli aggiornamenti consentiti, vedere Modificare uno schema di indice.

Alcune origini dati come Archiviazione di Azure supportano il rilevamento delle modifiche e dell'eliminazione per impostazione predefinita, in base al timestamp. Altre origini dati, ad esempio OneLake, Azure SQL o Azure Cosmos DB , devono essere configurate per il rilevamento delle modifiche.

Contenuto eliminato

Se il contenuto di origine non esiste più (ad esempio, se il testo viene abbreviato per avere meno blocchi), il documento figlio corrispondente nell'indice di ricerca viene eliminato. Anche i documenti figlio rimanenti ottengono la chiave aggiornata in modo da includere un nuovo valore hash, anche se il contenuto non è stato modificato in altro modo.

Se un documento padre viene eliminato completamente dall'origine dati, i documenti figlio corrispondenti vengono eliminati solo se l'eliminazione viene rilevata da dataDeletionDetectionPolicy definito nella definizione dell'origine dati. Se non si dispone di un documento dataDeletionDetectionPolicy configurato e si necessita di eliminare un documento padre dall'origine dati, è necessario eliminare manualmente i documenti figlio se non più necessari.

Valore chiave proiettato

Per garantire l'integrità dei dati per il contenuto aggiornato ed eliminato, l'aggiornamento dei dati nell'indicizzazione uno-a-molti si basa su un valore di chiave proiettato sul lato "molti". Se si usa la vettorializzazione integrata o la procedura guidata Importa e vettorizza dati, il valore della chiave proiettata è il parent_id campo in un lato in blocchi o "molti" dell'indice.

Un valore di chiave proiettato è un identificatore univoco generato dall'indicizzatore per ogni documento. Garantisce l'univocità e consente il corretto funzionamento del rilevamento delle modifiche e dell'eliminazione. Questa chiave contiene i segmenti seguenti:

  • Hash casuale per garantire l'univocità. Questo hash viene modificato se il documento padre viene aggiornato nelle esecuzioni successive dell'indicizzatore.
  • Chiave del documento padre.
  • Percorso dell’annotazione di arricchimento che identifica il contesto da cui è stato generato il documento.

Ad esempio, se si suddivide un documento padre con il valore chiave "aa1b22c33" in quattro pagine e ognuna di queste pagine viene proiettata come documento proprio tramite proiezioni di indice:

  • aa1b22c33
  • aa1b22c33_pages_0
  • aa1b22c33_pages_1
  • aa1b22c33_pages_2

Se il documento padre viene aggiornato nei dati di origine, con conseguente maggior numero di pagine in blocchi, modifiche hash casuali, aggiunta di più pagine e aggiornamento del contenuto di ogni blocco in modo che corrisponda a quello presente nel documento di origine.

Esempio di indici padre-figlio separati

Questa sezione illustra esempi per indici padre e figlio separati. Si tratta di un modello non comune, ma è possibile che si disponga di requisiti dell'applicazione che sono più soddisfatti usando questo approccio. In questo scenario il contenuto padre-figlio viene proiettato in due indici separati.

Ogni schema ha i campi per il relativo granularità particolare, con il campo ID padre comune a entrambi gli indici da usare in una query di ricerca. Il corpus di ricerca primario è l'indice figlio, ma quindi esegue una query di ricerca per recuperare i campi padre per ogni corrispondenza nel risultato. Ricerca di intelligenza artificiale di Azure non supporta i join in fase di query, quindi il codice dell'applicazione o il livello di orchestrazione dovrà unire o collazionare i risultati che possono essere passati a un'app o a un processo.

L'indice padre ha un campo e un titolo parent_id. Il parent_id è la chiave del documento. Non è necessaria la configurazione della ricerca vettoriale, a meno che non si desideri vettorizzare i campi a livello di documento padre.

{
    "name": "my-parent-index",
    "fields": [

        {"name": "parent_id", "type": "Edm.String", "filterable": true},
        {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
    ]
}

L'indice figlio include i campi in blocchi, oltre al campo parent_id. Se si usa la vettorizzazione integrata, i profili di punteggio, il ranker semantico o gli analizzatori, è necessario impostarli nell'indice figlio.

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

Di seguito è riportato un esempio di definizione di proiezione di indice che specifica il percorso dati che l'indicizzatore deve usare per indicizzare il contenuto. Specifica il nome dell'indice figlio nella definizione della proiezione dell'indice e specifica i mapping di ogni campo figlio o a livello di blocco. Si tratta dell'unica posizione in cui viene specificato il nome dell'indice figlio.

"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": []
                }
            ]
        }
    ]
}

La definizione dell'indicizzatore specifica i componenti della pipeline. Nella definizione dell'indicizzatore il nome dell'indice da specificare è l'indice padre. Se sono necessari mapping di campi per i campi a livello padre, definirli in outputFieldMappings. Per l'indicizzazione uno-a-molti che usa indici separati, la definizione dell'indicizzatore potrebbe essere simile all'esempio seguente.

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

Passaggio successivo

La suddivisione in blocchi dei dati e l'indicizzazione uno-a-molti fanno parte del modello RAG in Ricerca di intelligenza artificiale di Azure. Continuare con l'esercitazione e l'esempio di codice seguenti per altre informazioni.