Définir une projection d’index pour une indexation parent-enfant
Pour les index contenant des documents segmentés, une projection d’index spécifie la manière dont le contenu parent-enfant est routé vers les champs dans un index de recherche pour une indexation un-à-plusieurs. Via une injection d’index, vous pouvez envoyer du contenu vers :
Un seul index, où les champs du parent se répètent pour chaque segment, mais le fragment de l’index est au niveau du segment. Le tutoriel RAG constitue un exemple de cette approche.
Au moins deux index, où l’index parent a des champs liés au document parent, et l’index enfant est organisé autour des segments. L’index enfant est le principal corpus de recherche, mais l’index parent peut être utilisé pour des requêtes de recherche lorsque vous souhaitez récupérer les champs parents d’un segment spécifique, ou pour des requêtes indépendantes.
La plupart des implémentations constituent un seul index organisé autour de segments avec des champs parents, comme le nom de fichier du document, se répétant pour chaque segment. Cependant, le système est conçu pour prendre en charge des index enfants distincts et multiples s’il s’agit de votre exigence. Recherche Azure AI ne prend pas en charge les jointures d’index. Votre code d’application doit donc gérer l’index à utiliser.
Une projection d’index est définie dans un ensemble de compétences. Elle doit coordonner le processus d’indexation qui envoie des segments de contenu à un index de recherche, ainsi que du contenu parent lié à chaque segment. Elle améliore le fonctionnement de la segmentation des données natives en vous offrant d’autres options pour contrôler le mode d’indexation du contenu parent-enfant.
Cet article explique la création du schéma d’index et des modèles de projection d’indexeur pour l’indexation un-à-plusieurs.
Prérequis
Un index (ou plusieurs) qui accepte la sortie du pipeline d’indexeur.
Une source de données prise en charge ayant du contenu que vous souhaitez segmenter. Il peut s’agit d’un contenu vectoriel ou non vectoriel.
Une compétence qui fractionne du contenu en segments, la compétence Fractionnement de texte ou une compétence personnalisée offrant une fonctionnalité équivalente.
L’ensemble de compétences contient la projection d’indexeur qui met en forme les données pour l’indexation un-à-plusieurs. Un ensemble de compétences peut également avoir d’autres compétences, comme une compétence d’incorporation telle que AzureOpenAIEmbedding si votre scénario inclut la vectorisation intégrée.
Dépendance sur le traitement d’indexeur
L’indexation un-à-plusieurs prend une dépendance sur les ensembles de compétences et l’indexation basée sur l’indexeur qui inclut les quatre composants suivants :
- Une source de données
- Un ou plusieurs index pour votre contenu pouvant faire l’objet d’une recherche
- Un ensemble de compétences qui contient une projection d’index*
- Un indexeur
Vos données peuvent provenir de n’importe quelle source de données prise en charge. Cependant, l’hypothèse est que le contenu est assez volumineux pour que vous vouliez le segmenter. En outre, la raison de la segmentation est que vous implémentez un modèle RAG qui fournit des données de base à un modèle de conversation. Ou vous implémentez une recherche vectorielle et devez répondre à des exigences de taille d’entrée plus petite des modèles d’incorporation.
Les indexeurs chargent les données indexées dans un index prédéfini. La façon dont vous définissez le schéma et si vous utilisez un ou plusieurs index est la première décision à prendre dans un scénario d’indexation un-à-plusieurs. La section suivante aborde la conception d’index.
Créer un index pour l’indexation un-à-plusieurs
Que vous vouliez créer un index pour des segments qui répètent des valeurs parentes ou des index distincts pour un placement de champ parent-enfant, l’index principal utilisé pour la recherche est conçu autour des segments de données. Il doit avoir les champs suivants :
Un champ clé de document identifiant uniquement chaque document. Il doit être défini comme type
Edm.String
avec l’analyseurkeyword
.Un champ associant chaque segment à son parent. Il doit être de type
Edm.String
. Il ne peut pas s’agir du champ clé de document et la valeur defilterable
doit être définie sur true. Il s’appelle parent_id dans les exemples et valeur de clé projetée dans cet article.D’autres champs pour le contenu, comme les champs de segments vectorisés ou de texte.
Un index doit exister sur le service de recherche avant de créer l’ensemble de compétences ou d’exécuter l’indexeur.
Schéma d’index unique incluant les champs parent et enfant
Un index unique conçu autour de segments avec un contenu parent qui se répète pour chaque segment est le modèle prédominant des scénarios de recherche vectorielle et RAG. La possibilité d’associer le contenu parent correct à chaque segment est activée via les projections d’index.
Le schéma suivant est un exemple qui répond aux exigences pour les projections d’index. Dans cet exemple, les champs parents sont parent_id et le titre. Les champs enfants sont les segments de vecteur non vectoriels et vectoriels. L’ID du document de cet index est chunk_id. Le parent_id et le titre se répètent pour chaque segment dans l’index.
Vous pouvez utiliser le Portail Azure, les API REST ou un Kit de développement logiciel (SDK) Azure pour créer un index.
{
"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"}]
}
}
Ajouter des projections d’index à un ensemble de compétences
Les projections d’index sont définies à l’intérieur d’une définition d’ensemble de compétences et sont principalement définies en tant que tableau de selectors
, où chaque sélecteur correspond à un index cible différent sur le service de recherche. Cette section commence par les syntaxes et exemples pour le contexte, suivis de la référence de paramètre.
Choisissez un onglet pour les diverses syntaxes d’API. Il n’existe actuellement aucune prise en charge du portail pour configurer des projections, autre que la modification de la définition JSON de l’ensemble de compétences. Consultez l’exemple REST pour JSON.
Les projections d’index sont en disponibilité générale. Nous vous recommandons l’API stable la plus récente :
Voici un exemple de charge utile pour une définition des projections d’index que vous pouvez utiliser pour projeter une sortie de pages individuelles par la compétence Fractionnement de texte en tant que vos documents dans l’index de recherche.
"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"
}
}
Référence de paramètre
Paramètres de projection d’index | Définition |
---|---|
selectors |
Paramètres pour le principal corpus de recherche, généralement celui conçu autour des segments. |
projectionMode |
Paramètre facultatif fournissant des instructions à l’indexeur. L’unique valeur valide pour ce paramètre est skipIndexingParentDocuments . Elle est utilisée lorsque l’index de segment est le principal corpus de recherche et vous devez spécifier si les champs parents sont indexés comme documents de recherche supplémentaires dans l’index segmenté. Si vous ne définissez pas les skipIndexingParentDocuments , vous obtenez des documents de recherche supplémentaires dans votre index qui sont nuls pour les segments, mais remplis avec des champs parents uniquement. Par exemple, si cinq documents contribuent à 100 segments dans l’index, le nombre de documents est alors de 105 dans l’index. Les cinq documents créés ou les champs parents ont des valeurs nulles pour les champs (enfants) de segment, ce qui les rend considérablement différents de la majeure partie des documents dans l’index. Nous vous recommandons de définir projectionMode sur skipIndexingParentDocument . |
Les sélecteurs ont les paramètres suivants dans le cadre de leur définition.
Paramètres de sélecteur | Définition |
---|---|
targetIndexName |
Le nom de l’index dans lequel les données d’index sont projetées. Il s’agit de l’index unique segmenté avec des champs parents qui se répètent ou de l’index enfant si vous utilisez des index distincts pour le contenu parent-enfant. |
parentKeyFieldName |
Nom du champ fournissant la clé pour le document parent. |
sourceContext |
Annotation d’enrichissement qui définit la granularité à laquelle mapper des données dans des documents de recherche individuels. Pour plus d’informations, consultez Langage d’annotation du contexte et des entrées de compétences. |
mappings |
Tableau de mappages de données enrichies aux champs de l’index de recherche. Chaque mappage se compose des éléments suivants : name : nom du champ dans l’index de recherche dans lequel les données doivent être indexées. source : chemin d’annotation d’enrichissement à partir duquel les données doivent être extraites. Chaque mapping peut également définir de manière récursive des données avec un champ facultatif sourceContext et inputs , similaire à la base de connaissances ou à la compétence Shaper. Selon votre application, ces paramètres vous permettent de mettre en forme des données dans des champs de type Edm.ComplexType de l’index de recherche. Certains grands modèles de langage n’acceptent pas de type complexe dans les résultats de la recherche. Le grand modèle de langage (LLM) que vous utilisez détermine donc si un mappage de type complexe est utile ou non. |
Le paramètre mappings
est important. Vous devez explicitement mapper chaque champ dans l’index enfant, à l’exception des champs d’ID, tels que la clé de document et l’ID de parent.
Cette exigence diffère des autres conventions de mappage de champs dans Recherche Azure AI. Pour certains types de source de données, l’indexeur peut implicitement mapper des champs en fonction de noms similaires ou de caractéristiques connues (par exemple, des indexeurs de blob utilisent le chemin d’accès unique de stockage de métadonnées comme clé de document par défaut). Toutefois, pour les projections d’indexeur, vous devez explicitement spécifier chaque mappage de champs sur le côté « plusieurs » de la relation.
Ne créez pas de mappage de champ pour le champ clé parent. Cela perturberait le suivi des modifications et l’actualisation des données synchronisées.
Gestion des documents parents
Maintenant que vous avez découvert les différents modèles pour les indexations un-à-plusieurs, comparons les principales différences de chaque option. Les projections d’index génèrent efficacement les documents « enfants » pour chaque document « parent » qui s’exécute via un ensemble de compétences. Vous avez plusieurs options pour gérer les documents « parents ».
Si vous souhaitez envoyer des documents parents et enfants pour distinguer des index, définissez le
targetIndexName
pour la définition de votre indexeur sur l’index parent et définissez letargetIndexName
dans le sélecteur de projection d’index sur l’index enfant.Si vous souhaitez conserver les documents parents et enfants dans le même index, définissez l’indexeur
targetIndexName
et la projection d’indextargetIndexName
sur le même index.Pour éviter de créer des documents de recherche parent et veiller à ce que l’index contiennent uniquement des documents enfants de fragment uniforme, définissez le
targetIndexName
pour la définition d’indexeur et le sélecteur sur le même index, mais ajoutez un objetparameters
supplémentaire aprèsselectors
, avec une cléprojectionMode
définie surskipIndexingParentDocuments
, comme illustré ici :"indexProjections": { "selectors": [ ... ], "parameters": { "projectionMode": "skipIndexingParentDocuments" } }
Vérifier les mappages de champs
Les indexeurs sont affiliés avec trois différents types de mappages de champs. Avant d’exécuter l’indexeur, vérifiez vos mappages de champs et sachez comment utiliser chaque type.
Les mappages de champ sont définis dans un indexeur et utilisés pour mapper un champ source vers un champ d’index. Les mappages de champs sont utilisés pour les chemins d’accès aux données qui déploient les données de la source vers l’indexation, sans aucune étape intermédiaire de traitement des compétences. En général, un indexeur peut automatiquement mapper des champs ayant le nom et type. Les mappages de champs explicites sont uniquement requis lorsqu’il existe des divergences. Dans l’indexation un-à-plusieurs et les modèles évoqués jusqu’à présent, il est possible que vous n’ayez pas besoin de mappages de champs.
Les mappages de champs de sortie sont définis dans un indexeur et utilisés pour mapper du contenu enrichi généré par un ensemble de compétences vers un champ dans l’index principal. Dans les modèles un-à-plusieurs abordés dans cet article, il s’agit de l’index parent dans une solution à deux index. Dans les exemples illustrés de cet article, l’index parent est peu fréquent, avec un simple champ de titre qui n’est pas rempli avec du contenu à partir du traitement de l’ensemble de compétences. Nous n’avons donc pas de mappage de champs de sortie.
Les mappages de champs de projection d’indexeur sont utilisés pour mapper du contenu généré par un ensemble de compétences aux champs dans l’index enfant. Dans les cas où l’index parent inclut également des champs parents (comme dans la solution d’index consolidée), vous devez configurer les mappages de champs pour chaque champ ayant un contenu, notamment le champ de titre au niveau du parent, en supposant que vous souhaitez que le titre s’affiche dans chaque document segmenté. Si vous utilisez des index enfants et parents distincts, les projections d’indexeur doivent avoir des mappages de champs pour les champs au niveau de l’enfant uniquement.
Remarque
Les mappages de champs de sortie et les mappages de champs de projection d’indexeur acceptent les nœuds enrichis d’arborescence de document comme entrées sources. Savoir comment spécifier un chemin d’accès à chaque nœud est essentiel pour configurer le chemin d’accès aux données. Pour découvrir plus d’informations sur la syntaxe de chemin d’accès, consultez Référencer un chemin d’accès aux nœuds et la définition d’ensemble de compétences pour découvrir des exemples.
Exécuter l’indexeur
Une fois la source de données, les index et l’ensemble de compétences créés, vous êtes prêt à créer et exécuter l’indexeur. Cette étape met le pipeline en exécution.
Vous pouvez interroger votre index de recherche après la fin du traitement permettant de tester votre solution.
Cycle de vie du contenu
Selon la source de données sous-jacente, un indexeur peut généralement fournir un suivi des modifications et une détection des suppressions en cours. Cette section explique le cycle de vie du contenu d’une indexation un-à-plusieurs, car il se rapporte à l’actualisation des données.
Pour les sources de données offrant le suivi des modifications et la détection des suppressions, un processus d’indexeur peut tenir compte des modifications dans vos données sources. Chaque fois que vous exécutez l’indexeur et l’ensemble de compétences, les projections d’index mises à jour si l’ensemble de compétences ou les données sources sous-jacentes ont changé. Les changements détectés par l’indexeur sont propagés via le processus d’enrichissement aux projections d’index, ce qui permet de garantir que les données projetées sont une représentation actualisée du contenu de la source de données d’origine. L’activité d’actualisation des données est capturée dans une valeur de clé projetée pour chaque segment. Cette valeur est mise à jour quand les données sous-jacentes changent.
Remarque
Bien que vous puissiez modifier manuellement les données dans les documents projetés en utilisant l’API d’envoi (push) de données, nous vous déconseillons de le faire. Les mises à jour manuelles d’un index sont remplacées sur l’invocation de pipeline suivante, en supposant que le document dans la source de données soient mises à jour et que vous ayez activé le suivi des modifications ou la détection des suppressions dans la source de données.
Mise à jour du contenu
Si vous ajoutez du contenu à votre source de données, de nouveaux segments ou documents enfants sont ajoutés à l’index lors de l’exécution suivante de l’indexeur.
Si vous modifiez un contenu existant dans la source de données, les segments sont mis à jour de manière incrémentielle dans l’index de recherche si la source de données utilisée prend en charge le suivi des modifications et la détection des suppressions. Par exemple, si un mot ou une phrase change dans un document, le segment dans l’index cible contenant ce mot ou cette phrase est mis à jour lors de l’exécution suivante de l’indexeur. D’autres types de mises à jour, telles que le changement d’un type de champ et d’attributions, ne sont pas pris en charge pour des champs existants. Pour découvrir plus d’informations sur les mises à jour autorisées, consultez Modifier un schéma d’index.
Certaines sources de données comme Stockage Azure prennent en charge le suivi des modifications et des suppressions par défaut en fonction du timestamp. D’autres sources de données, comme OneLake, Azure SQL ou Cosmos DB, doivent être configurées pour le suivi des modifications.
Contenu supprimé
Si le contenu source n’existe plus (par exemple, si le texte est raccourci pour avoir moins de segments), le document enfant correspondant est supprimé dans l’index de recherche. La clé des documents enfants restants est également mise à jour pour inclure une nouvelle valeur de hachage, même si leur contenu n’a pas changé autrement.
Si un document parent est complètement supprimé de la source de données, les documents enfants correspondants sont supprimés uniquement si la suppression est détectée par un dataDeletionDetectionPolicy
défini sur la définition de source de données. Si vous n’avez pas configuré de dataDeletionDetectionPolicy
et que vous devez supprimer un document parent de la source de données, vous devez supprimer manuellement les documents enfants s’ils ne sont plus souhaités.
Valeur de clé projetée
Pour veiller à l’intégrité des données du contenu mis à jour et supprimé, l’actualisation des données dans une indexation un-à-plusieurs s’appuie sur une valeur de clé projetée sur le côté « plusieurs ». Si vous utilisez la vectorisation intégrée ou l’Assistant Importation et vectorisation des données, la valeur de clé projetée est le champ parent_id
dans un côté « plusieurs » ou segmenté de l’index.
Une valeur de clé projetée est un identificateur unique que l’indexeur génère pour chaque document. Il veille à une unicité et permet au suivi des modifications et des suppressions de fonctionner correctement. Cette clé contient les segments suivants :
- Hachage aléatoire pour garantir l’unicité. Ce code de hachage change si le document parent est mis à jour lors des exécutions suivantes de l’indexeur.
- Clé du document parent.
- Chemin d’annotation d’enrichissement qui identifie le contexte à partir duquel ce document a été généré.
Par exemple, si vous fractionnez un document parent avec la valeur de clé « aa1b22c33 » en quatre pages, puis que chacune de ces pages est projetée comme son propre document via des projections d’index :
- aa1b22c33
- aa1b22c33_pages_0
- aa1b22c33_pages_1
- aa1b22c33_pages_2
Si le document parent est mis à jour dans les données sources, pouvant entraîner la segmentation d’autres pages, le code de hachage aléatoire change, d’autres pages sont ajoutées et le contenu de chaque segment est mis à jour pour correspondre à ce qui se trouve dans le document source.
Exemples d’index parent-enfant distincts
Cette section montre des exemples pour des index parent et enfant distincts. Il s’agit d’un modèle inhabituel, mais il est possible que vous ayez des exigences d’application qui sont mieux utilisées en utilisant cette approche. Dans ce scénario, vous projetez du contenu parent-enfant dans deux index distincts.
Chaque schéma dispose des champs pour son fragment particulier, avec le champ d’ID parent commun aux deux index pour une utilisation dans une requête de recherche. Le principal corpus de recherche est l’index enfant, qui émet ensuite une requête de recherche pour récupérer les champs parents pour chaque correspondance dans le résultat. Recherche Azure AI ne prend pas en charge les jointures au moment de la requête. Par conséquent, le code de votre application ou couche d’orchestration devra fusionner ou rassembler les résultats qui peuvent être transmis à une application ou un processus.
L’index parent a un titre et un champ parent_id. Le parent_id est la clé de document. Vous n’avez pas besoin de la configuration de recherche vectorielle, sauf si vous souhaitez vectoriser des champs au niveau du document parent.
{
"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’index enfant a des champs segmentés, plus le champ parent_id. Si vous utilisez la vectorisation intégrée, les profils de scoring, le classeur sémantique ou les analyseurs, vous devrez les définir dans l’index enfant.
{
"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": []
}
Voici l’exemple d’une définition de projection d’index qui spécifie le chemin d’accès aux données que l’indexeur doit utiliser pour indexer le contenu. Elle spécifie le nom d’index enfant dans la définition de projection d’index et les mappages de chaque enfant ou champ au niveau du segment. Il s’agit du seul emplacement dans lequel le nom d’index enfant est spécifié.
"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 définition d’indexeur spécifie les composants du pipeline. Dans la définition d’indexeur, le nom d’index à fournir est l’index parent. Si vous avez besoin de mappages de champs pour les champs au niveau du parent, définissez-les dans outputFieldMappings. Pour l’indexation un-à-plusieurs qui utilise des index distincts, il est possible que la définition d’indexeur ressemble à l’exemple suivant.
{
"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,
}
Étape suivante
La segmentation de données et l’indexation un-à-plusieurs font partie du modèle RAG dans Recherche Azure AI. Passez au tutoriel suivant et à l’exemple de code pour découvrir plus d’information à ce sujet.