Esercitazione: creare una pipeline di indicizzazione per RAG in Azure AI Search
Informazioni su come creare una pipeline di indicizzazione automatizzata per una soluzione RAG in Azure AI Search. L'automazione dell'indicizzazione avviene tramite un indicizzatore che supporta l'indicizzazione e l'esecuzione del set di competenze, fornendo la suddivisione in blocchi e la vettorizzazione dei dati integrati su base occasionale o ricorrente per gli aggiornamenti incrementali.
In questa esercitazione:
- Specificare lo schema dell'indice dell'esercitazione precedente
- Crea connessione di origine dati
- Creare un indicizzatore
- Creare un set di competenze che scomponga, vettorizzi e riconosca le entità
- Eseguire l'indicizzatore e verificare i risultati
Se non si ha una sottoscrizione di Azure, creare un account gratuito prima di iniziare.
Suggerimento
È possibile usare la Procedura guidata di importazione e vettorizzazione dei dati per creare la pipeline. Provare alcune guide introduttive: Ricerca immagini e Ricerca vettoriale.
Prerequisiti
Visual Studio Code con l'estensione Python e il pacchetto Jupyter. Per altre informazioni, vedere Python in Visual Studio Code.
Account per utilizzo generico di Archiviazione di Azure. Questo esercizio carica i file PDF nell'archivio BLOB per l'indicizzazione automatica.
Azure AI Search, livello Basic o superiore per l'identità gestita e la classificazione semantica. Scegliere un'area condivisa con Azure OpenAI e Servizi di Azure AI.
Azure OpenAI, con una distribuzione di text-embedding-3-large, nella stessa area di Ricerca di intelligenza artificiale di Azure. Per altre informazioni sull'incorporamento di modelli usati nelle soluzioni RAG, vedere Scegliere modelli di incorporamento per RAG in Azure AI Search.
Account multiservizio del servizio di intelligenza artificiale di Azure, nella stessa area di Ricerca di intelligenza artificiale di Azure. Questa risorsa viene usata per la competenza Riconoscimento entità che rileva le posizioni nei contenuti.
Scaricare l'esempio
Scaricare un Jupyter Notebook da GitHub per inviare le richieste ad Azure AI Search. Per altre informazioni, vedere Download di file da GitHub.
Specificare lo schema dell'indice
Aprire o creare un Jupyter Notebook (.ipynb
) in Visual Studio Code per contenere gli script che costituiscono la pipeline. I passaggi iniziali consentono di installare i pacchetti e di raccogliere le variabili per le connessioni. Dopo aver completato i passaggi di installazione, è possibile iniziare con i componenti della pipeline di indicizzazione.
Si inizia con lo schema dell'indice dell'esercitazione precedente. La struttura si basa su blocchi vettorializzati e nonvettorializzati. Include un campo locations
che archivia i contenuti generati dall'intelligenza artificiale creati dal set di competenze.
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")
Crea connessione di origine dati
In questo passaggio, si configurano i dati di esempio e la connessione ad Archiviazione BLOB di Azure. L'indicizzatore recupera i PDF da un contenitore. È possibile creare il contenitore e caricare i file in questo passaggio.
L'ebook originale è di grandi dimensioni, oltre 100 pagine e 35 MB. È stato suddiviso in PDF più piccoli, uno per pagina di testo, per rimanere al di sotto del limite di documenti per gli indicizzatori di 16 MB per ogni chiamata API e anche i limiti dei dati di arricchimento tramite intelligenza artificiale. Per semplicità, in questo esercizio viene tralasciata la vettorializzazione delle immagini.
Accedere al portale di Azure e trovare l'account Archiviazione di Azure.
Creare un contenitore e caricare i PDF da earth_book_2019_text_pages.
Assicurarsi che Ricerca di intelligenza artificiale di Azure disponga delle autorizzazioni di lettura dei dati dei BLOB di archiviazione per la risorsa.
Successivamente, in Visual Studio Code, definire un'origine dati dell'indicizzatore che fornisca informazioni di connessione durante l'indicizzazione.
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")
Se si configura un'identità gestita per Ricerca di intelligenza artificiale di Azure per la connessione, il stringa di connessione include un ResourceId=
suffisso. Dovrebbe essere simile all'esempio seguente: "ResourceId=/subscriptions/FAKE-SUBCRIPTION=ID/resourceGroups/FAKE-RESOURCE-GROUP/providers/Microsoft.Storage/storageAccounts/FAKE-ACCOUNT;"
Creare un set di competenze
Le competenze sono la base per la suddivisione dei dati integrata e la vettorializzazione. Come minimo, si vuole che una competenza di Suddivisione testo suddivida in blocchi i contenuti e una competenza di incorporamento crei rappresentazioni vettoriali dei contenuti in blocchi.
In questo set di competenze, viene usata una competenza aggiuntiva per creare dati strutturati nell'indice. La competenza Riconoscimento entità viene usata per identificare le località, che possono variare dai nomi appropriati ai riferimenti generici, ad esempio "oceano" o "montagna". La creazione di dati strutturati offre più opzioni per la creazione di query interessanti e per aumentare la pertinenza.
Il AZURE_AI_MULTISERVICE_KEY è necessario anche se si usa il controllo degli accessi in base al ruolo. Ricerca di intelligenza artificiale di Azure usa la chiave a scopo di fatturazione ed è necessaria a meno che i carichi di lavoro non rimangano al di sotto del limite gratuito. È anche possibile stabilire una connessione senza chiave se si usano l'API di anteprima o i pacchetti beta più recenti. Per altre informazioni, vedere Collegare una risorsa multiservizio di Intelligenza artificiale di Azure a un set di competenze.
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=1024,
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")
Creare ed eseguire l'indicizzatore
Gli indicizzatori sono il componente che imposta tutti i processi in movimento. È possibile creare un indicizzatore in uno stato disabilitato, ma l'impostazione predefinita consiste nell'eseguirlo immediatamente. In questa esercitazione si crea e si esegue l'indicizzatore per recuperare i dati dall'archivio BLOB, si eseguono le competenze, tra cui la suddivisione in blocchi e vettorizzazione, e si carica l'indice.
L'esecuzione dell'indicizzatore richiede alcuni minuti. Al termine, è possibile andare al passaggio finale: eseguire query sull'indice.
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.')
Eseguire una query per verificare i risultati
Inviare una query per verificare che l'indice sia operativo. Questa richiesta converte la stringa di testo "what's NASA's website?
" in un vettore per la ricerca vettoriale. I risultati sono costituiti dai campi nell'istruzione di selezione, alcuni dei quali vengono stampati come output.
A questo punto non c'è alcuna chat o intelligenza artificiale generativa. I risultati sono contenuti verbatim dall'indice di ricerca.
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']}")
Questa query restituisce una singola corrispondenza (top=1
) costituita da un blocco determinato dal motore di ricerca come il più rilevante. I risultati della query devono essere simili all'esempio seguente:
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
Provare altre query per ottenere un'idea di ciò che il motore di ricerca restituisce direttamente, in modo da poterlo confrontare con una risposta abilitata per l'LLM. Rieseguire lo script precedente con questa query: "patagonia geography"
e impostare su top
3 per restituire più di una risposta.
I risultati di questa seconda query devono essere simili ai risultati seguenti, che vengono modificati leggermente per brevità. L'output viene copiato dal notebook, che tronca la risposta a ciò che viene visualizzato in questo esempio. È possibile espandere l'output della cella per esaminare la risposta completa.
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.
Con questo esempio, è più facile individuare il modo in cui i blocchi vengono restituiti in modo dettagliato e come la parola chiave e la ricerca di somiglianza identificano le corrispondenze principali. Questo blocco specifico contiene sicuramente informazioni sulla Patagonia e sulla geografia, ma non è esattamente rilevante per la query. Il ranker semantico promuove blocchi più pertinenti per una risposta migliore, ma come passaggio successivo, vediamo come connettere Ricerca di intelligenza artificiale di Azure a un LLM per la ricerca conversazionale.