Compartir a través de


Tutorial: Creación de una canalización de indexación para RAG en Búsqueda de Azure AI

Obtenga información sobre cómo crear una canalización de indexación automatizada para una solución RAG en Búsqueda de Azure AI. La automatización de la indexación se realiza a través de un indexador que controla la indexación y la ejecución de conjuntos de aptitudes, proporcionando fragmentación y vectorización de datos integradas de forma puntual o recurrente para actualizaciones incrementales.

En este tutorial ha:

  • Proporcione el esquema de índice del tutorial anterior
  • Cree una conexión de origen de datos
  • Creación de un indexador
  • Creación de un conjunto de aptitudes que fragmenta, vectoriza y reconoce entidades
  • Ejecute el indexador y compruebe los resultados

Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar.

Sugerencia

Puede usar el Asistente para importar y vectorizar datos para crear la canalización. Pruebe algunas guías de inicio rápido: Búsqueda de imágenes y Vector de búsqueda.

Requisitos previos

Descarga del ejemplo

Descargue un Jupyter Notebook de GitHub para enviar las solicitudes a Búsqueda de Azure AI. Para obtener más información, consulte Descarga de archivos de GitHub.

Proporcione el esquema de índice

Abra o cree un Jupyter Notebook (.ipynb) en Visual Studio Code para contener los scripts que componen la canalización. Los pasos iniciales instalan paquetes y recopilan variables para las conexiones. Después de completar los pasos de configuración, podrá comenzar con los componentes de la canalización de indexación.

Comencemos con el esquema de índice del tutorial anterior. Se organiza en torno a fragmentos vectorizados y no vectorizados. Incluye un campo locations que almacena el contenido generado por IA creado por el conjunto de aptitudes.

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

Cree una conexión de origen de datos

En este paso, configura los datos de ejemplo y una conexión a Azure Blob Storage. El indexador recupera archivos PDF de un contenedor. En este paso, creará el contenedor y cargará los archivos.

El libro electrónico original es grande, más de 100 páginas y 35 MB de tamaño. Lo dividimos en archivos PDF más pequeños, uno por página de texto, para permanecer bajo el límite de documentos para indexadores de 16 MB por llamada API y también los límites de datos de enriquecimiento con IA. Para simplificar, se omite la vectorización de imágenes para este ejercicio.

  1. Inicie sesión en Azure Portal y busque su cuenta de Azure Storage.

  2. Cree un contenedor y cargue los archivos PDF desde earth_book_2019_text_pages.

  3. Asegúrese de que Búsqueda de Azure AI tiene permisos de lector de datos de Blob Storage en el recurso.

  4. A continuación, en Visual Studio Code, defina un origen de datos de indexador que proporcione información de conexión durante la indexación.

    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 configura una identidad administrada para Búsqueda de Azure AI para la conexión, la cadena de conexión incluye un sufijo ResourceId=. Debe tener un aspecto similar al siguiente ejemplo: "ResourceId=/subscriptions/FAKE-SUBCRIPTION=ID/resourceGroups/FAKE-RESOURCE-GROUP/providers/Microsoft.Storage/storageAccounts/FAKE-ACCOUNT;"

Creación de un conjunto de aptitudes

Las competencias son la base de la fragmentación y vectorización integradas de los datos. Como mínimo, necesita una aptitud División de texto para fragmentar el contenido y una aptitud de inserción para crear representaciones vectoriales del contenido fragmentado.

En este conjunto de aptitudes, se usa una aptitud adicional para crear datos estructurados en el índice. La aptitud Reconocimiento de entidades se usa para identificar ubicaciones, que pueden oscilar entre nombres adecuados y referencias genéricas, como "océano" o "montaña". Tener datos estructurados proporciona más opciones para crear consultas interesantes y aumentar la relevancia.

El evento AZURE_AI_MULTISERVICE_KEY es necesario incluso si usa el control de acceso basado en roles. Búsqueda de Azure AI usa la clave para fines de facturación y es necesaria a menos que las cargas de trabajo permanezcan por debajo del límite gratuito. También puede realizar una conexión sin claves si usa la API de versión preliminar o los paquetes beta más recientes. Para más información, consulte Asociación de un recurso de varios servicios de Azure AI a un conjunto de aptitudes.

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

Creación y ejecución del indexador

Los indexadores son el componente que pone en marcha todos los procesos. Puede crear un indexador en estado deshabilitado, pero por defecto se ejecuta inmediatamente. En este tutorial, cree y ejecute el indexador para recuperar los datos de Blob Storage, ejecutar las aptitudes, incluida la fragmentación y la vectorización, y cargar el índice.

El indexador tarda varios minutos en ejecutarse. Cuando haya terminado, puede pasar al paso final: consultar el índice.

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.')    

Ejecución de una consulta para comprobar los resultados

Envíe una consulta para confirmar que el índice está operativo. Esta solicitud convierte la cadena de texto "what's NASA's website?" en un vector para un vector de búsqueda. Los resultados constan de los campos de la instrucción select, algunos de los cuales se imprimen como salida.

No hay ninguna inteligencia artificial generativa ni chat en este momento. Los resultados son contenido literal del índice de búsqueda.

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']}")

Esta consulta devuelve una coincidencia única (top=1) que consta del fragmento determinado por el motor de búsqueda para que sea el más relevante. Los resultados de la consulta deben tener un aspecto similar al del siguiente ejemplo:

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

Pruebe algunas consultas más para obtener una idea de lo que devuelve el motor de búsqueda directamente para poder compararla con una respuesta habilitada para LLM. Vuelva a ejecutar el script anterior con esta consulta: "patagonia geography" y establezca top en 3 para devolver más de una respuesta.

Los resultados de esta segunda consulta deben ser similares a los siguientes, que se editan ligeramente para mayor concisión. La salida se copia del cuaderno, lo que trunca la respuesta a lo que ve en este ejemplo. Puede expandir la salida de celda para revisar la respuesta 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 este ejemplo, es más fácil ver cómo se devuelven fragmentos textuales y cómo la búsqueda por palabra clave y similitud identifica las coincidencias más importantes. Este fragmento específico definitivamente tiene información sobre la Patagonia y geografía, pero no es exactamente relevante para la consulta. El clasificador semántico promovería fragmentos más relevantes para dar una mejor respuesta, pero como paso siguiente, veamos cómo conectar Búsqueda de Azure AI a un LLM para la búsqueda conversacional.

Paso siguiente