Tutorial: Diseño de un índice para RAG en Azure AI Search
Un índice contiene contenido vectorial y texto que se puede buscar, además de configuraciones. En un patrón RAG que usa un modelo de chat para las respuestas, necesita un índice diseñado en torno a fragmentos de contenido que se puedan pasar a un LLM en el momento de la consulta.
En este tutorial ha:
- Conozca las características de un esquema de índice creado para RAG
- Crear un índice que admita consultas vectoriales e híbridas
- Adición de perfiles y configuraciones de vectores
- Adición de datos estructurados
- Adición de filtrado
Requisitos previos
Visual Studio Code con la extensión de Python y el paquete Jupyter. Para obtener más información, consulte Python en Visual Studio Code.
La salida de este ejercicio es una definición de índice en JSON. En este momento, no se carga en Azure AI Search, por lo que no hay ningún requisito para los servicios en la nube ni los permisos de este ejercicio.
Revisión de las consideraciones de esquema para RAG
En la búsqueda conversacional, los LLM componen la respuesta que ve el usuario, no el motor de búsqueda, por lo que no es necesario pensar en qué campos mostrar en los resultados de la búsqueda y si las representaciones de documentos de búsqueda individuales son coherentes con el usuario. Dependiendo de la pregunta, LLM podría devolver contenido textual del índice, o más probable, volver a empaquetar el contenido para obtener una mejor respuesta.
Organizado en torno a fragmentos
Cuando las LLM generan una respuesta, operan en fragmentos de contenido para las entradas de mensajes y, aunque necesitan saber de dónde procede el fragmento con fines de cita, lo que más importa es la calidad de las entradas de mensaje y su relevancia para la pregunta del usuario. Tanto si los fragmentos proceden de un documento como de mil, LLM ingiere la información o los datos de puesta a tierra, y formula la respuesta mediante instrucciones proporcionadas en un mensaje del sistema.
Los fragmentos son el foco del esquema y cada fragmento es el elemento que define un documento de búsqueda en un patrón RAG. Puede considerar el índice como una gran colección de fragmentos, en lugar de documentos de búsqueda tradicionales que probablemente tengan más estructura, como campos que contienen contenido uniforme para un nombre, descripciones, categorías y direcciones.
Mejorado con datos generados
En este tutorial, los datos de ejemplo constan de archivos PDF y contenido del Libro de la Tierra de la NASA. Este contenido es descriptivo e informativo, con numerosas referencias a geografías, países y áreas en todo el mundo. Todo el contenido textual se captura en fragmentos, pero las instancias periódicas de nombres de lugar crean una oportunidad para agregar estructura al índice. Gracias a las capacidades, es posible reconocer entidades en el texto y capturarlas en un índice para utilizarlas en consultas y filtros. En este tutorial, se incluye una competencia de reconocimiento de entidades que reconoce y extrae entidades de ubicación y las carga en un campo de locations
en el que se puede buscar y que permite filtrados. Agregar contenido estructurado al índice proporciona más opciones para filtrar, optimizar la relevancia y obtener respuestas más específicas.
¿Campos primarios y secundarios en uno o dos índices?
Normalmente, el contenido fragmentado se deriva de un documento mayor. Y aunque el esquema se organiza en torno a fragmentos, también quiere capturar propiedades y contenido en el nivel primario. Algunos ejemplos de estas propiedades pueden incluir la ruta de acceso del archivo principal, el título, los autores, la fecha de publicación o un resumen.
Un punto de inflexión en el diseño del esquema es si se deben tener dos índices para el contenido primario y secundario o fragmentado, o un único índice que repite los elementos primarios para cada fragmento.
En este tutorial, debido a que todos los fragmentos de texto se originan en un único elemento primario (NASA Earth Book), no necesita un índice independiente dedicado a nivel superior de los campos primarios. Sin embargo, si va a indexar desde varios archivos PDF primarios, es posible que desee que un par de índices primarios y secundarios capture campos específicos de nivel y a continuación, envíe consultas de búsqueda al índice primario para recuperar esos campos relevantes para cada fragmento.
Lista de comprobación de consideraciones de esquema
En la Búsqueda de Azure AI, un índice que funciona mejor para las cargas de trabajo RAG tiene estas cualidades:
Devuelve fragmentos relevantes para la consulta y legibles para LLM. Las LLM pueden controlar un determinado nivel de datos sucios en fragmentos, como marcar, redundancia y cadenas incompletas. Aunque los fragmentos deben ser legibles y relevantes para la pregunta, no necesitan ser prístinos.
Mantiene una relación de elementos primarios y secundarios entre fragmentos de un documento y las propiedades del documento primario, como el nombre de archivo, el tipo de archivo, el título, el autor, etc. Para responder a una consulta, los fragmentos se pueden extraer de cualquier parte del índice. La asociación con el documento primario que proporciona el fragmento es útil para el contexto, las citas y las consultas de seguimiento.
Admite las consultas que desea crear. Debe tener campos para el contenido vectorial e híbrido, y esos campos deben atribuirse para admitir comportamientos de consulta específicos, como los que se pueden buscar o filtrar. Solo puede consultar un índice a la vez (sin combinaciones), por lo que la colección de campos debe definir todo el contenido que se puede buscar.
El esquema debe ser plano (sin tipos o estructuras complejos) o debe Dar formato a la salida del tipo complejo como JSON antes de enviarlo al LLM. Este requisito es específico del patrón RAG en Búsqueda de Azure AI.
Nota:
El diseño del esquema afecta tanto al almacenamiento como a los costos. Este ejercicio se centra en los aspectos básicos del esquema. En el tutorial Minimización del almacenamiento y los costos, volverá a consultar los esquemas para obtener información sobre la forma en que los tipos de datos estrechos, la compresión y las opciones de almacenamiento reducen considerablemente la cantidad de almacenamiento que usan los vectores.
Creación de un índice para cargas de trabajo RAG
Un índice mínimo para LLM está diseñado para almacenar fragmentos de contenido. Normalmente incluye campos vectoriales si desea buscar resultados altamente relevantes. También incluye campos no vectores para entradas legibles para el LLM para la búsqueda conversacional. El contenido fragmentado no vectores en los resultados de la búsqueda se convierte en los datos de puesta en tierra enviados al LLM.
Abra Visual Studio Code y cree un archivo. No tiene que ser un tipo de archivo de Python para este ejercicio.
Esta es una definición de índice mínima para las soluciones RAG que admiten vectores y búsqueda híbrida. Repase para obtener una introducción a los elementos necesarios: nombre de índice, campos y una sección de configuración para campos vectoriales.
{ "name": "example-minimal-index", "fields": [ { "name": "id", "type": "Edm.String", "key": true }, { "name": "chunked_content", "type": "Edm.String", "searchable": true, "retrievable": true }, { "name": "chunked_content_vectorized", "type": "Edm.Single", "dimensions": 1536, "vectorSearchProfile": "my-vector-profile", "searchable": true, "retrievable": false, "stored": false }, { "name": "metadata", "type": "Edm.String", "retrievable": true, "searchable": true, "filterable": true } ], "vectorSearch": { "algorithms": [ { "name": "my-algo-config", "kind": "hnsw", "hnswParameters": { } } ], "profiles": [ { "name": "my-vector-profile", "algorithm": "my-algo-config" } ] } }
Los campos deben incluir el campo de clave (
"id"
en este ejemplo) y deben incluir fragmentos vectoriales para la búsqueda de similitud y fragmentos no vectores para las entradas del LLM.Los campos vectoriales están asociados a algoritmos que determinan las rutas de búsqueda en el momento de la consulta. El índice tiene una sección vectorSearch para especificar varias configuraciones de algoritmo. Los campos vectoriales tienen también tipos específicos y atributos adicionales para insertar dimensiones del modelo.
Edm.Single
es un tipo de datos que funciona para las LLMs que se usan habitualmente. Para obtener más información acerca de los campos vectoriales, consulte Crear un índice vectorial.Los campos de metadatos pueden ser ruta de acceso de archivo principal, fecha de creación o tipo de contenido y son útiles para filtros.
Este es el esquema de índice del código fuente del tutorial y el contenido de Earth Book.
Al igual que el esquema básico, se organiza en torno a fragmentos. El
chunk_id
identifica de forma única cada fragmento. Eltext_vector
campo es una inserción del fragmento. El campo no vectorchunk
es una cadena legible.title
asigna a una ruta de acceso de almacenamiento de metadatos única para los blobs.parent_id
es el único campo de nivel primario y es una versión codificada en base64 del URI del archivo primario.El esquema también incluye un campo
locations
para almacenar el contenido generado creado por la canalización de indexación.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")
Para un esquema de índice que imita más detenidamente el contenido estructurado, tendría índices independientes para los campos primarios y secundarios (fragmentados). Necesitaría proyecciones de índice para coordinar la indexación de los dos índices simultáneamente. Las consultas se ejecutan en el índice secundario. La lógica de consulta incluye una consulta de búsqueda mediante el parent_idt para recuperar el contenido del índice primario.
Campos del índice secundario:
- ID
- chunk
- chunkVectcor
- parent_id
Campos del índice primario (todo lo que desea "uno de"):
- parent_id
- campos de nivel primario (nombre, título, categoría)