Självstudie: Utforma ett index för RAG i Azure AI Search
Ett index innehåller sökbar text och vektorinnehåll, plus konfigurationer. I ett RAG-mönster som använder en chattmodell för svar vill du ha ett index som är utformat kring segment av innehåll som kan skickas till en LLM vid frågetillfället.
I den här kursen får du:
- Lär dig egenskaperna för ett indexschema som skapats för RAG
- Skapa ett index som hanterar vektor- och hybridfrågor
- Lägga till vektorprofiler och konfigurationer
- Lägga till strukturerade data
- Lägg till filtrering
Förutsättningar
Visual Studio Code med Python-tillägget och Jupyter-paketet. Mer information finns i Python i Visual Studio Code.
Utdata från den här övningen är en indexdefinition i JSON. I det här läget laddas den inte upp till Azure AI Search, så det finns inga krav på molntjänster eller behörigheter i den här övningen.
Granska schemaöverväganden för RAG
I konversationssökning skriver LLM:er det svar som användaren ser, inte sökmotorn, så du behöver inte tänka på vilka fält som ska visas i sökresultaten och om representationerna av enskilda sökdokument är sammanhängande för användaren. Beroende på frågan kan LLM returnera ordagrant innehåll från ditt index, eller mer troligt, packa om innehållet för ett bättre svar.
Ordnat runt segment
När LLM:er genererar ett svar fungerar de på segment av innehåll för meddelandeindata, och även om de behöver veta var segmentet kom ifrån i källhänvisningssyfte, är det viktigaste kvaliteten på meddelandeindata och dess relevans för användarens fråga. Oavsett om segmenten kommer från ett dokument eller tusen, matar LLM in informationen eller jordningsdata och formulerar svaret med hjälp av instruktionerna i en systemprompt.
Segment är i fokus för schemat och varje segment är det definierande elementet i ett sökdokument i ett RAG-mönster. Du kan se ditt index som en stor samling segment, till skillnad från traditionella sökdokument som förmodligen har mer struktur, till exempel fält som innehåller enhetligt innehåll för ett namn, beskrivningar, kategorier och adresser.
Utökad med genererade data
I den här självstudien består exempeldata av PDF-filer och innehåll från NASA Earth Book. Det här innehållet är beskrivande och informativt, med många referenser till geografiska områden, länder och områden över hela världen. Allt textinnehåll samlas in i segment, men återkommande instanser av platsnamn skapar en möjlighet att lägga till struktur i indexet. Med hjälp av kunskaper är det möjligt att identifiera entiteter i texten och samla in dem i ett index för användning i frågor och filter. I den här självstudien inkluderar vi en entitetsigenkänningsfärdighet som identifierar och extraherar platsentiteter och läser in den i ett sökbart och filterbart locations
fält. Genom att lägga till strukturerat innehåll i ditt index får du fler alternativ för filtrering, förbättrad relevans och mer fokuserade svar.
Överordnat-underordnat fält i ett eller två index?
Segmenterat innehåll härleds vanligtvis från ett större dokument. Och även om schemat är ordnat runt segment, vill du också samla in egenskaper och innehåll på den överordnade nivån. Exempel på dessa egenskaper kan vara den överordnade filsökvägen, rubrik, författare, publiceringsdatum eller en sammanfattning.
En brytpunkt i schemadesignen är om du vill ha två index för överordnat och underordnat/segmenterat innehåll eller ett enda index som upprepar överordnade element för varje segment.
I den här självstudien, eftersom alla textsegment kommer från en enda överordnad (NASA Earth Book), behöver du inte ett separat index som är dedikerat till överordnade fält på samma nivå. Men om du indexerar från flera överordnade PDF-filer kanske du vill att ett överordnat-underordnat indexpar ska samla in nivåspecifika fält och sedan skicka uppslagsfrågor till det överordnade indexet för att hämta de fält som är relevanta för varje segment.
Checklista för schemaöverväganden
I Azure AI Search har ett index som fungerar bäst för RAG-arbetsbelastningar följande egenskaper:
Returnerar segment som är relevanta för frågan och som kan läsas för LLM. LLM:er kan hantera en viss nivå av smutsiga data i segment, till exempel markering, redundans och ofullständiga strängar. Även om segment måste vara läsbara och relevanta för frågan, behöver de inte vara orörda.
Upprätthåller en överordnad-underordnad relation mellan segment i ett dokument och egenskaperna för det överordnade dokumentet, till exempel filnamn, filtyp, rubrik, författare och så vidare. För att besvara en fråga kan segment hämtas var som helst i indexet. Association med det överordnade dokumentet som tillhandahåller segmentet är användbart för kontext, citat och uppföljningsfrågor.
Passar de frågor som du vill skapa. Du bör ha fält för vektor- och hybridinnehåll, och dessa fält bör hänföras till stöd för specifika frågebeteenden, till exempel sökbara eller filterbara. Du kan bara köra frågor mot ett index i taget (inga kopplingar) så fältsamlingen bör definiera allt sökbart innehåll.
Schemat ska antingen vara platt (inga komplexa typer eller strukturer), eller så bör du formatera utdata av komplex typ som JSON innan du skickar det till LLM. Det här kravet är specifikt för RAG-mönstret i Azure AI Search.
Kommentar
Schemadesign påverkar lagring och kostnader. Den här övningen fokuserar på grunderna i schemat. I självstudien Minimera lagring och kostnader går du tillbaka till scheman för att lära dig hur smala datatyper, komprimering och lagringsalternativ avsevärt minskar mängden lagringsutrymme som används av vektorer.
Skapa ett index för RAG-arbetsbelastningar
Ett minimalt index för LLM är utformat för att lagra innehållssegment. Den innehåller vanligtvis vektorfält om du vill ha likhetssökning för mycket relevanta resultat. Den innehåller även icke-huvudfält för läsbara indata till LLM för konversationssökning. Segmenterat innehåll som inte är segmenterat i sökresultaten blir de grunddata som skickas till LLM.
Öppna Visual Studio Code och skapa en ny fil. Det behöver inte vara en Python-filtyp för den här övningen.
Här är en minimal indexdefinition för RAG-lösningar som stöder vektor- och hybridsökning. Granska den för en introduktion till nödvändiga element: indexnamn, fält och ett konfigurationsavsnitt för vektorfält.
{ "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" } ] } }
Fält måste innehålla nyckelfält (
"id"
i det här exemplet) och ska innehålla vektorsegment för likhetssökning och icke-huvudsegment för indata till LLM.Vektorfält är associerade med algoritmer som avgör sökvägarna vid frågetillfället. Indexet har ett vectorSearch-avsnitt för att ange flera algoritmkonfigurationer. Vektorfält har också specifika typer och extra attribut för inbäddning av modelldimensioner.
Edm.Single
är en datatyp som fungerar för vanliga LLM:er. Mer information om vektorfält finns i Skapa ett vektorindex.Metadatafält kan vara den överordnade filsökvägen, skapandedatum eller innehållstyp och är användbara för filter.
Här är indexschemat för självstudiens källkod och earth book-innehållet.
Precis som det grundläggande schemat är det organiserat runt segment. Unikt
chunk_id
identifierar varje segment. Fältettext_vector
är en inbäddning av segmentet. Fältet nonvectorchunk
är en läsbar sträng. Mapparnatitle
till en unik lagringssökväg för metadata för blobarna.parent_id
är det enda fältet på överordnad nivå och det är en base64-kodad version av den överordnade fil-URI:n.I integrerade vektoriseringsarbetsbelastningar som den som används i den här självstudieserien
dimensions
bör egenskapen på dina vektorfält vara identisk med antaletdimensions
som genereras av den inbäddningsfärdighet som används för att vektorisera dina data. I den här serien använder vi inbäddningsfärdigheten för Azure OpenAI, som anropar modellen text-embedding-3-large i Azure OpenAI. Kunskapen anges i nästa självstudie. Vi anger dimensioner till 1024 i både vektorfältet och i kunskapsdefinitionen.Schemat innehåller också ett
locations
fält för lagring av genererat innehåll som skapas av indexeringspipelinen.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")
För ett indexschema som bättre efterliknar strukturerat innehåll skulle du ha separata index för överordnade och underordnade fält (segmenterade). Du skulle behöva indexprognoser för att samordna indexeringen av de två indexen samtidigt. Frågor körs mot det underordnade indexet. Frågelogik innehåller en sökningsfråga med hjälp av parent_idt hämta innehåll från det överordnade indexet.
Fält i det underordnade indexet:
- ID
- segment
- chunkVectcor
- parent_id
Fält i det överordnade indexet (allt som du vill ha "en av"):
- parent_id
- fält på överordnad nivå (namn, rubrik, kategori)