Tutoriel : Créer un pipeline d’indexation pour RAG sur Recherche Azure AI
Découvrez comment créer un pipeline d’indexation automatisé pour une solution RAG sur Recherche Azure AI. L’automatisation de l’indexation consiste à utiliser un indexeur qui pilote l’indexation et l’exécution de l’ensemble de compétences, fournissant une segmentation et une vectorisation des données intégrées de façon ponctuelle ou récurrente pour les mises à jour incrémentielles.
Dans ce tutoriel, vous allez :
- Fournir le schéma d’index provenant du tutoriel précédent
- Créer une connexion à la source de données
- Créer un indexeur
- Créer un ensemble de compétences qui segmente, vectorise et reconnaît les entités
- Exécuter l’indexeur et vérifier les résultats
Si vous n’avez pas d’abonnement Azure, créez un compte gratuit avant de commencer.
Conseil
Vous pouvez utiliser l’Assistant Importation et vectorisation des données pour créer votre pipeline. Essayez des guides de démarrage rapide : Recherche d’images et Recherche vectorielle.
Prérequis
Visual Studio Code avec l’extension Python et le package Jupyter. Pour plus d’informations, consultez Python dans Visual Studio Code.
Compte Stockage Azure à usage général. Cet exercice charge des fichiers PDF dans Stockage Blob pour l’indexation automatisée.
Recherche Azure AI, niveau De base ou supérieur pour l’identité managée et le classement sémantique. Choisissez une région partagée avec Azure OpenAI et Azure AI Services.
Azure OpenAI avec un déploiement de text-embedding-3-large, dans la même région que Recherche Azure AI. Pour plus d’informations sur l’incorporation de modèles utilisés dans des solutions RAG, consultez Choisir des modèles d’incorporation pour RAG dans Recherche Azure AI.
Compte multiservice Azure AI Service, dans la même région que Recherche Azure AI. Cette ressource est utilisée pour la compétence Reconnaissance d’entités qui détecte des emplacements dans votre contenu.
Télécharger l’exemple
Téléchargez un notebook Jupyter depuis GitHub pour envoyer les requêtes à Recherche Azure AI. Si vous souhaitez obtenir plus d’informations, consultez Téléchargement de fichiers à partir de GitHub.
Fournir le schéma d’index
Ouvrez ou créez un notebook Jupyter (.ipynb
) dans Visual Studio Code, destiné à contenir les scripts qui composent le pipeline. Les étapes initiales installent les packages et collectent des variables pour les connexions. Une fois les étapes d’installation terminées, vous êtes prêt à commencer par les composants du pipeline d’indexation.
Commençons par le schéma d’index provenant du tutoriel précédent. Il est organisé autour de blocs vectorisés et non vectorisés. Il inclut un champ locations
qui stocke le contenu généré par l’IA et créé par l’ensemble de compétences.
from azure.identity import DefaultAzureCredential
from azure.identity import get_bearer_token_provider
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents.indexes.models import (
SearchField,
SearchFieldDataType,
VectorSearch,
HnswAlgorithmConfiguration,
VectorSearchProfile,
AzureOpenAIVectorizer,
AzureOpenAIVectorizerParameters,
SearchIndex
)
credential = DefaultAzureCredential()
# Create a search index
index_name = "py-rag-tutorial-idx"
index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
fields = [
SearchField(name="parent_id", type=SearchFieldDataType.String),
SearchField(name="title", type=SearchFieldDataType.String),
SearchField(name="locations", type=SearchFieldDataType.Collection(SearchFieldDataType.String), filterable=True),
SearchField(name="chunk_id", type=SearchFieldDataType.String, key=True, sortable=True, filterable=True, facetable=True, analyzer_name="keyword"),
SearchField(name="chunk", type=SearchFieldDataType.String, sortable=False, filterable=False, facetable=False),
SearchField(name="text_vector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), vector_search_dimensions=1024, vector_search_profile_name="myHnswProfile")
]
# Configure the vector search configuration
vector_search = VectorSearch(
algorithms=[
HnswAlgorithmConfiguration(name="myHnsw"),
],
profiles=[
VectorSearchProfile(
name="myHnswProfile",
algorithm_configuration_name="myHnsw",
vectorizer_name="myOpenAI",
)
],
vectorizers=[
AzureOpenAIVectorizer(
vectorizer_name="myOpenAI",
kind="azureOpenAI",
parameters=AzureOpenAIVectorizerParameters(
resource_url=AZURE_OPENAI_ACCOUNT,
deployment_name="text-embedding-3-large",
model_name="text-embedding-3-large"
),
),
],
)
# Create the search index
index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search)
result = index_client.create_or_update_index(index)
print(f"{result.name} created")
Créer une connexion à la source de données
Dans cette étape, configurez les exemples de données et une connexion à Stockage Blob Azure. L’indexeur récupère les fichiers PDF auprès d’un conteneur. Dans cette étape, vous créez le conteneur et vous chargez des fichiers.
Le livre électronique d’origine est volumineux, plus de 100 pages, et sa taille s’élève à 35 Mo. Nous l’avons divisé en fichiers PDF plus petits, un par page de texte, pour rester sous la limite de documents pour les indexeurs de 16 Mo par appel d’API, et en dessous des limites de données d’enrichissement par IA. Pour rester simples, nous omettons la vectorisation d’image pour cet exercice.
Connectez-vous au portail Azure et recherchez votre compte de stockage Azure.
Créez un conteneur et chargez les fichiers PDF depuis earth_book_2019_text_pages.
Vérifiez que Recherche Azure AI dispose des autorisations Lecteur des données Blob du stockage sur la ressource.
Ensuite, dans Visual Studio Code, définissez une source de données d’indexeur qui fournit des informations de connexion lors de l’indexation.
from azure.search.documents.indexes import SearchIndexerClient from azure.search.documents.indexes.models import ( SearchIndexerDataContainer, SearchIndexerDataSourceConnection ) # Create a data source indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential) container = SearchIndexerDataContainer(name="nasa-ebooks-pdfs-all") data_source_connection = SearchIndexerDataSourceConnection( name="py-rag-tutorial-ds", type="azureblob", connection_string=AZURE_STORAGE_CONNECTION, container=container ) data_source = indexer_client.create_or_update_data_source_connection(data_source_connection) print(f"Data source '{data_source.name}' created or updated")
Si vous configurez une identité managée pour Recherche Azure AI pour la connexion, la chaîne de connexion inclut un suffixe ResourceId=
. Le résultat doit ressembler à l’exemple suivant : "ResourceId=/subscriptions/FAKE-SUBCRIPTION=ID/resourceGroups/FAKE-RESOURCE-GROUP/providers/Microsoft.Storage/storageAccounts/FAKE-ACCOUNT;"
Créer un ensemble de compétences
Les compétences constituent la base de la segmentation et de la vectorisation des données intégrées. Au minimum, vous voulez qu’une compétence Fractionnement de texte segmente votre contenu, et une compétence d’incorporation qui crée des représentations vectorielles de votre contenu segmenté.
Dans cet ensemble de compétences, une compétence supplémentaire est utilisée pour créer des données structurées dans l’index. La compétence Reconnaissance d’entités est utilisée pour identifier les emplacements, qui peuvent aller des noms propres à des références génériques, comme « océan » ou « montagne ». Le fait d’avoir des données structurées vous offre davantage d’options pour créer des requêtes intéressantes et améliorer la pertinence.
AZURE_AI_MULTISERVICE_KEY est nécessaire même si vous utilisez le contrôle d’accès en fonction du rôle. Recherche Azure AI utilise la clé à des fins de facturation et elle est requise, sauf si vos charges de travail restent sous la limite gratuite. Vous pouvez également établir une connexion sans clé si vous utilisez l’API ou les packages bêta les plus récents. Pour plus d’informations, consultez Attacher une ressource multiservices Azure AI à un ensemble de compétences.
from azure.search.documents.indexes.models import (
SplitSkill,
InputFieldMappingEntry,
OutputFieldMappingEntry,
AzureOpenAIEmbeddingSkill,
EntityRecognitionSkill,
SearchIndexerIndexProjection,
SearchIndexerIndexProjectionSelector,
SearchIndexerIndexProjectionsParameters,
IndexProjectionMode,
SearchIndexerSkillset,
CognitiveServicesAccountKey
)
# Create a skillset
skillset_name = "py-rag-tutorial-ss"
split_skill = SplitSkill(
description="Split skill to chunk documents",
text_split_mode="pages",
context="/document",
maximum_page_length=2000,
page_overlap_length=500,
inputs=[
InputFieldMappingEntry(name="text", source="/document/content"),
],
outputs=[
OutputFieldMappingEntry(name="textItems", target_name="pages")
],
)
embedding_skill = AzureOpenAIEmbeddingSkill(
description="Skill to generate embeddings via Azure OpenAI",
context="/document/pages/*",
resource_url=AZURE_OPENAI_ACCOUNT,
deployment_name="text-embedding-3-large",
model_name="text-embedding-3-large",
dimensions=1536,
inputs=[
InputFieldMappingEntry(name="text", source="/document/pages/*"),
],
outputs=[
OutputFieldMappingEntry(name="embedding", target_name="text_vector")
],
)
entity_skill = EntityRecognitionSkill(
description="Skill to recognize entities in text",
context="/document/pages/*",
categories=["Location"],
default_language_code="en",
inputs=[
InputFieldMappingEntry(name="text", source="/document/pages/*")
],
outputs=[
OutputFieldMappingEntry(name="locations", target_name="locations")
]
)
index_projections = SearchIndexerIndexProjection(
selectors=[
SearchIndexerIndexProjectionSelector(
target_index_name=index_name,
parent_key_field_name="parent_id",
source_context="/document/pages/*",
mappings=[
InputFieldMappingEntry(name="chunk", source="/document/pages/*"),
InputFieldMappingEntry(name="text_vector", source="/document/pages/*/text_vector"),
InputFieldMappingEntry(name="locations", source="/document/pages/*/locations"),
InputFieldMappingEntry(name="title", source="/document/metadata_storage_name"),
],
),
],
parameters=SearchIndexerIndexProjectionsParameters(
projection_mode=IndexProjectionMode.SKIP_INDEXING_PARENT_DOCUMENTS
),
)
cognitive_services_account = CognitiveServicesAccountKey(key=AZURE_AI_MULTISERVICE_KEY)
skills = [split_skill, embedding_skill, entity_skill]
skillset = SearchIndexerSkillset(
name=skillset_name,
description="Skillset to chunk documents and generating embeddings",
skills=skills,
index_projection=index_projections,
cognitive_services_account=cognitive_services_account
)
client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
client.create_or_update_skillset(skillset)
print(f"{skillset.name} created")
Créer et exécuter l’indexeur
Les indexeurs sont les composants qui définissent tous les processus en mouvement. Vous pouvez créer un indexeur dans un état désactivé, mais l’action par défaut est de l’exécuter immédiatement. Dans ce tutoriel, vous créez et vous exécutez l’indexeur pour récupérer les données auprès de Stockage Blob, vous exécutez les compétences, notamment la segmentation et la vectorisation, et vous chargez l’index.
L’exécution de l’indexeur prend plusieurs minutes. Quand elle est terminée, vous pouvez passer à l’étape finale : interroger votre index.
from azure.search.documents.indexes.models import (
SearchIndexer,
FieldMapping
)
# Create an indexer
indexer_name = "py-rag-tutorial-idxr"
indexer_parameters = None
indexer = SearchIndexer(
name=indexer_name,
description="Indexer to index documents and generate embeddings",
skillset_name=skillset_name,
target_index_name=index_name,
data_source_name=data_source.name,
# Map the metadata_storage_name field to the title field in the index to display the PDF title in the search results
field_mappings=[FieldMapping(source_field_name="metadata_storage_name", target_field_name="title")],
parameters=indexer_parameters
)
# Create and run the indexer
indexer_client = SearchIndexerClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential)
indexer_result = indexer_client.create_or_update_indexer(indexer)
print(f' {indexer_name} is created and running. Give the indexer a few minutes before running a query.')
Exécuter une requête pour vérifier les résultats
Envoyez une requête pour vérifier que votre index est opérationnel. Cette requête convertit la chaîne de texte « what's NASA's website?
» en vecteur pour une recherche vectorielle. Les résultats sont constitués des champs de l’instruction select, certains étant affichés en sortie.
Il n’y a pas de conversation ni d’IA générative à ce stade. Les résultats sont du contenu détaillé de votre index de recherche.
from azure.search.documents import SearchClient
from azure.search.documents.models import VectorizableTextQuery
# Vector Search using text-to-vector conversion of the querystring
query = "what's NASA's website?"
search_client = SearchClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential, index_name=index_name)
vector_query = VectorizableTextQuery(text=query, k_nearest_neighbors=50, fields="text_vector")
results = search_client.search(
search_text=query,
vector_queries= [vector_query],
select=["chunk"],
top=1
)
for result in results:
print(f"Score: {result['@search.score']}")
print(f"Chunk: {result['chunk']}")
Cette requête retourne une seule correspondance (top=1
) composée d’un bloc déterminé par le moteur de recherche comme étant le plus pertinent. Les résultats de la première requête doivent être similaires à l’exemple suivant :
Score: 0.01666666753590107
Chunk: national Aeronautics and Space Administration
earth Science
NASA Headquarters
300 E Street SW
Washington, DC 20546
www.nasa.gov
np-2018-05-2546-hQ
Essayez quelques autres requêtes pour avoir une idée de ce que le moteur de recherche retourne directement et pouvoir le comparer à une réponse produite par un LLM. Réexécutez le script précédent avec cette requête : "patagonia geography"
et définissez top
sur 3 pour renvoyer plusieurs réponses.
Les résultats de cette deuxième requête doivent être similaires aux résultats suivants, qui sont légèrement modifiés pour des raisons de concision. La sortie est copiée à partir du notebook, qui tronque la réponse à ce que vous voyez dans cet exemple. Vous pouvez développer la sortie de la cellule pour passer en revue la réponse complète.
Score: 0.03306011110544205
Chunk:
Swirling Bloom off Patagonia
Argentina
Interesting art often springs out of the convergence of different ideas and influences.
And so it is with nature.
Off the coast of Argentina, two strong ocean currents converge and often stir up a colorful
brew, as shown in this Aqua image from
December 2010.
This milky green and blue bloom formed on the continental shelf off of Patagonia, where warmer,
saltier waters from the subtropics
meet colder, fresher waters flowing from the south. Where these currents collide, turbulent
eddies and swirls form, pulling nutrients
up from the deep ocean. The nearby Rio de la Plata also deposits nitrogen- and iron-laden
sediment into the sea. Add in some
...
while others terminate in water. The San Rafael and San Quintín glaciers (shown at the right)
are the icefield’s largest. Both have
been receding rapidly in the past 30 years.
Avec cet exemple, il est plus facile de repérer la façon dont les blocs sont retournés mot pour mot, et comment la recherche de mots clés et de similarité identifient les correspondances principales. Ce bloc spécifique a certainement des informations sur la Patagonie et la géographie, mais il n’est pas exactement pertinent pour la requête. Le classeur sémantique favoriserait des segments plus pertinents pour une réponse de meilleure qualité, mais nous allons voir dans l’étape suivante comment connecter Recherche Azure AI à un LLM pour la recherche conversationnelle.