Mapper la sortie enrichie aux champs d’un index de recherche dans la Recherche Azure AI

Diagramme des étapes de l’indexeur avec mappages de champs de sortie mis en évidence.

Cet article explique comment configurer des mappages de champs de sortie, en définissant un chemin d’accès de données entre les données en mémoire créées pendant le traitement d’un ensemble de compétences et les champs cibles dans un index de recherche. Durant l’exécution de l’indexeur, les informations générées par les compétences sont uniquement stockées en mémoire. Pour rendre ces informations persistantes dans un index de recherche, vous devez indiquer à l’indexeur où envoyer les données.

Un mappage de champs de sortie se définit dans un indexeur et comporte les éléments suivants :

"outputFieldMappings": [
  {
    "sourceFieldName": "document/path-to-a-node-in-an-enriched-document",
    "targetFieldName": "some-search-field-in-an-index",
    "mappingFunction": null
  }
],

Contrairement à une définition fieldMappings qui mappe un chemin d’accès entre des champs source et d’index verbatim, une définition outputFieldMappings mappe les enrichissements en mémoire aux champs d’un index de recherche.

Prérequis

  • Indexeur, index, source de données et ensemble de compétences.

  • Les champs d’index doivent être des champs simples ou de niveau supérieur. Vous ne pouvez pas générer de sortie pour un type complexe. Toutefois, si vous avez un type complexe, vous pouvez utiliser une définition de champ de sortie pour aplatir des parties du type complexe et les envoyer à une collection dans un index de recherche.

Quand utiliser un mappage de champs de sortie

Les mappages de champs de sortie sont requis si votre indexeur a un ensemble de compétences attaché qui crée des informations que vous voulez avoir dans votre index. Voici quelques exemples :

  • Vecteurs de compétences d’incorporation
  • Compétences de reconnaissance optique de caractères (OCR) de texte d’image
  • Emplacements, organisations ou personnes à partir des compétences de reconnaissance d’entité

Les mappages de champs de sortie peuvent également être utilisés pour :

  • Créer plusieurs copies de votre contenu généré (mappages de champs de sortie un-à-plusieurs).

  • Aplatir le type complexe d’un document source. Par exemple, supposons que les documents sources ont un type complexe, tel qu’une adresse en multiples parties, et que vous désirez simplement la ville. Vous pouvez utiliser un mappage de champ de sortie pour aplatir une structure de données imbriquée, puis utiliser un mappage de champs de sortie pour envoyer la sortie vers une collection de chaînes dans l’index de recherche.

Les mappages de champs de sortie s’appliquent uniquement aux index de recherche. Si vous remplissez une base de connaissances, utilisez des projections pour la configuration du chemin des données.

Définir un mappage de champs de sortie

Les mappages de champs de sortie sont ajoutés au tableau outputFieldMappings dans une définition d’indexeur, généralement placée après le tableau fieldMappings. Un mappage de champs de sortie se compose de trois parties.

Vous pouvez utiliser l’API REST ou un SDK Azure pour définir les mappages de champs de sortie.

Conseil

Les indexeurs créés par l’Assistant Importation de données incluent les mappages de champs de sortie qui sont générés par l’Assistant. Si vous avez besoin d’exemples, lancez l’Assistant sur votre source de données pour voir les mappages de champs de sortie dans l’indexeur.

  1. Utilisez Créer un indexeur, Créer ou mettre à jour un indexeur ou une méthode équivalente dans un Kit de développement logiciel (SDK) Azure. Voici un exemple de définition d’un indexeur.

    {
       "name": "myindexer",
       "description": null,
       "dataSourceName": "mydatasource",
       "targetIndexName": "myindex",
       "schedule": { },
       "parameters": { },
       "fieldMappings": [],
       "outputFieldMappings": [],
       "disabled": false,
       "encryptionKey": { }
     }
    
  2. Renseignez le tableau outputFieldMappings pour spécifier les mappages. Un mappage de champs se compose de trois parties.

    "outputFieldMappings": [
      {
        "sourceFieldName": "/document/path-to-a-node-in-an-enriched-document",
        "targetFieldName": "some-search-field-in-an-index",
        "mappingFunction": null
      }
    ]
    
    Propriété Description
    sourceFieldName Obligatoire. Spécifie le chemin du contenu enrichi. Un exemple pourrait être /document/content. Pour obtenir la syntaxe du chemin d’accès et des exemples, consultez Référencer des enrichissements dans un ensemble de compétences Recherche Azure AI.
    targetFieldName facultatif. Spécifie le champ de recherche qui reçoit le contenu enrichi. Les champs cibles doivent être des champs ou collections simples de premier niveau. Il ne doit pas s’agir d’un chemin vers un sous-champ dans un type complexe. Si vous souhaitez récupérer des nœuds spécifiques dans une structure complexe, vous pouvez aplatir des nœuds individuels en mémoire, puis envoyer la sortie vers une collection de chaînes dans l’index.
    mappingFunction facultatif. Ajoute un traitement supplémentaire fourni par les fonctions de mappage prises en charge par les indexeurs. Pour les nœuds d’enrichissement, l’encodage et le décodage sont les fonctions les plus couramment utilisées.
  3. Le targetFieldName est toujours le nom du champ dans l’index de recherche.

  4. La sourceFieldName est un chemin d’accès à un nœud dans le document enrichi. C’est la sortie d’une compétence. Le chemin commence toujours par /document, et si vous indexez à partir d’un blob, le deuxième élément du chemin est /content. Le troisième élément est la valeur produite par la compétence. Pour plus d’informations et d’exemples, consultez Référencer des enrichissements dans un ensemble de compétences Recherche Azure AI.

    Cet exemple ajoute des entités et des étiquettes de sentiment extraites de la propriété de contenu d’un objet blob aux champs d’un index de recherche.

    {
        "name": "myIndexer",
        "dataSourceName": "myDataSource",
        "targetIndexName": "myIndex",
        "skillsetName": "myFirstSkillSet",
        "fieldMappings": [],
        "outputFieldMappings": [
            {
                "sourceFieldName": "/document/content/organizations/*/description",
                "targetFieldName": "descriptions",
                "mappingFunction": {
                    "name": "base64Decode"
                }
            },
            {
                "sourceFieldName": "/document/content/organizations",
                "targetFieldName": "orgNames"
            },
            {
                "sourceFieldName": "/document/content/sentiment",
                "targetFieldName": "sentiment"
            }
        ]
    }
    
  5. Affectez les fonctions de mappage nécessaires pour transformer le contenu d’un champ avant qu’il ne soit stocké dans l’index. Pour les nœuds d’enrichissement, l’encodage et le décodage sont les fonctions les plus couramment utilisées.

Mappage de champs de sortie un-à-plusieurs

Vous pouvez utiliser un mappage de champ de sortie pour router un champ source unique vers plusieurs champs d’un index de recherche. Vous pouvez le faire pour les tests de comparaison ou si vous souhaitez des champs avec différents attributs.

Supposons un ensemble de compétences qui génère des incorporations pour un champ vectoriel et un index qui a plusieurs champs vectoriels qui varient selon les paramètres d’algorithme et de compression. Dans l’indexeur, mappez la sortie de la compétence d’incorporation à chacun des champs vectoriels multiples d’un index de recherche.

"outputFieldMappings": [
    { "sourceFieldName" : "/document/content/text_vector", "targetFieldName" : "vector_hnsw" }, 
    { "sourceFieldName" : "/document/content/text_vector", "targetFieldName" : "vector_eknn" },
    { "sourceFieldName" : "/document/content/text_vector", "targetFieldName" : "vector_narrow" }, 
    { "sourceFieldName" : "/document/content/text_vector", "targetFieldName" : "vector_no_stored" },
    { "sourceFieldName" : "/document/content/text_vector", "targetFieldName" : "vector_scalar" }       
  ]

Le chemin du champ source est la sortie de compétence. Dans cet exemple, la sortie est text_vector. Le nom cible est une propriété facultative. Si vous ne donnez pas un nom cible au mappage de sortie, le chemin d’accès sera embedding ou plus précisément, /document/content/embedding.

{
  "name": "test-vector-size-ss",  
  "description": "Generate embeddings using AOAI",
  "skills": [
    {
      "@odata.type": "#Microsoft.Skills.Text.AzureOpenAIEmbeddingSkill",
      "name": "#1",
      "description": null,
      "context": "/document/content",
      "resourceUri": "https://my-demo-eastus.openai.azure.com",
      "apiKey": null,
      "deploymentId": "text-embedding-ada-002",
      "dimensions": 1536,
      "modelName": "text-embedding-ada-002",
      "inputs": [
        {
          "name": "text",
          "source": "/document/content"
        }
      ],
      "outputs": [
        {
          "name": "embedding",
          "targetName": "text_vector"
        }
      ],
      "authIdentity": null
    }
  ]
}

Aplatir des structures complexes dans une collection de chaînes

Si vos données sources sont composées de données JSON imbriquées ou hiérarchiques, vous ne pouvez pas utiliser de mappages de champs pour configurer les chemins des données. Au lieu de cela, votre index de recherche doit refléter exactement la structure des données source à chaque niveau pour une importation complète.

Cette section vous guide pas à pas tout au long d’un processus d’importation qui reproduit fidèlement un document complexe côté source et côté cible. Ensuite, elle reprend le même document source pour illustrer la récupération et l’aplatissement de nœuds individuels dans des collections de chaînes.

Voici un exemple de document dans Azure Cosmos DB avec des données JSON imbriquées :

{
   "palette":"primary colors",
   "colors":[
      {
         "name":"blue",
         "medium":[
            "acrylic",
            "oil",
            "pastel"
         ]
      },
      {
         "name":"red",
         "medium":[
            "acrylic",
            "pastel",
            "watercolor"
         ]
      },
      {
         "name":"yellow",
         "medium":[
            "acrylic",
            "watercolor"
         ]
      }
   ]
}

Si vous souhaitez indexer entièrement ce document source, vous devez créer une définition d’index où les noms, niveaux et types de champ sont reflétés comme un type complexe. Étant donné que les mappages de champs ne sont pas pris en charge pour les types complexes dans l’index de recherche, votre définition d’index doit refléter exactement le document source.

{
  "name": "my-test-index",
  "defaultScoringProfile": "",
  "fields": [
    { "name": "id", "type": "Edm.String", "searchable": false, "retrievable": true, "key": true},
    { "name": "palette", "type": "Edm.String", "searchable": true, "retrievable": true },
    { "name": "colors", "type": "Collection(Edm.ComplexType)",
      "fields": [
        {
          "name": "name",
          "type": "Edm.String",
          "searchable": true,
          "retrievable": true
        },
        {
          "name": "medium",
          "type": "Collection(Edm.String)",
          "searchable": true,
          "retrievable": true,
        }
      ]
    }
  ]
}

Voici un exemple de définition d’indexeur qui exécute l’importation. Notez qu’il n’existe aucun mappage de champ ni aucun ensemble de compétences.

{
  "name": "my-test-indexer",
  "dataSourceName": "my-test-ds",
  "skillsetName": null,
  "targetIndexName": "my-test-index",

  "fieldMappings": [],
  "outputFieldMappings": []
}

Le résultat est l’exemple de document de recherche suivant, qui est similaire au document d’origine dans Azure Cosmos DB.

{
  "value": [
    {
      "@search.score": 1,
      "id": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
      "palette": "primary colors",
      "colors": [
        {
          "name": "blue",
          "medium": [
            "acrylic",
            "oil",
            "pastel"
          ]
        },
        {
          "name": "red",
          "medium": [
            "acrylic",
            "pastel",
            "watercolor"
          ]
        },
        {
          "name": "yellow",
          "medium": [
            "acrylic",
            "watercolor"
          ]
        }
      ]
    }
  ]
}

Un autre résultat possible dans un index de recherche consiste à aplatir des nœuds individuels dans la structure imbriquée de la source afin d’obtenir une collection de chaînes dans un index de recherche.

Pour accomplir cette tâche, vous devez utiliser un outputFieldMappings qui mappe un nœud en mémoire à une collection de chaînes dans l’index. Les mappages de champs de sortie s’appliquent principalement aux sorties de compétences, mais vous pouvez aussi les utiliser pour traiter les nœuds après le craquage de document où l’indexeur ouvre un document source et le lit en mémoire.

Voici un exemple de définition d’index qui utilise des collections de chaînes pour recevoir une sortie aplatie :

{
  "name": "my-new-flattened-index",
  "defaultScoringProfile": "",
  "fields": [
    { "name": "id", "type": "Edm.String", "searchable": false, "retrievable": true, "key": true },
    { "name": "palette", "type": "Edm.String", "searchable": true, "retrievable": true },
    { "name": "color_names", "type": "Collection(Edm.String)", "searchable": true, "retrievable": true },
    { "name": "color_mediums", "type": "Collection(Edm.String)", "searchable": true, "retrievable": true}
  ]
}

Voici un exemple de définition d’indexeur, qui utilise outputFieldMappings pour associer les données JSON imbriquées aux champs des collections de chaînes. Notez que le champ source utilise la syntaxe de chemin des nœuds d’enrichissement, même s’il n’y a pas d’ensemble de compétences. Les documents enrichis sont créés dans le système pendant le craquage de document. Vous pouvez donc accéder aux nœuds dans chaque arborescence de documents tant que ces nœuds existent au moment du craquage de document.

{
  "name": "my-test-indexer",
  "dataSourceName": "my-test-ds",
  "skillsetName": null,
  "targetIndexName": "my-new-flattened-index",
  "parameters": {  },
  "fieldMappings": [   ],
  "outputFieldMappings": [
    {
       "sourceFieldName": "/document/colors/*/name",
       "targetFieldName": "color_names"
    },
    {
       "sourceFieldName": "/document/colors/*/medium",
       "targetFieldName": "color_mediums"
    }
  ]
}

Les résultats de la définition sont les suivants. La simplification de la structure amenuise le contexte dans ce cas. Il n’y a plus d’associations entre une couleur donnée et les supports qui la proposent. Toutefois, en fonction de votre scénario, un résultat similaire à l’exemple suivant pourrait être exactement ce dont vous avez besoin.

{
  "value": [
    {
      "@search.score": 1,
      "id": "11bb11bb-cc22-dd33-ee44-55ff55ff55ff",
      "palette": "primary colors",
      "color_names": [
        "blue",
        "red",
        "yellow"
      ],
      "color_mediums": [
        "[\"acrylic\",\"oil\",\"pastel\"]",
        "[\"acrylic\",\"pastel\",\"watercolor\"]",
        "[\"acrylic\",\"watercolor\"]"
      ]
    }
  ]
}