Zelfstudie: Een index ontwerpen voor RAG in Azure AI Search
Een index bevat doorzoekbare tekst- en vectorinhoud, plus configuraties. In een RAG-patroon dat gebruikmaakt van een chatmodel voor antwoorden, wilt u een index die is ontworpen rond segmenten inhoud die tijdens de query kan worden doorgegeven aan een LLM.
In deze zelfstudie hebt u:
- Meer informatie over de kenmerken van een indexschema dat is gebouwd voor RAG
- Een index maken die geschikt is voor vector- en hybride query's
- Vectorprofielen en -configuraties toevoegen
- Gestructureerde gegevens toevoegen
- Filteren toevoegen
Vereisten
Visual Studio Code met de Python-extensie en het Jupyter-pakket. Zie Python in Visual Studio Code voor meer informatie.
De uitvoer van deze oefening is een indexdefinitie in JSON. Op dit moment wordt het niet geüpload naar Azure AI Search, dus er zijn geen vereisten voor cloudservices of machtigingen in deze oefening.
Schemaoverwegingen voor RAG bekijken
In gesprekszoekopdrachten stellen LLM's het antwoord op dat de gebruiker ziet, niet de zoekmachine, dus u hoeft niet na te denken over welke velden in uw zoekresultaten moeten worden weergegeven en of de weergaven van afzonderlijke zoekdocumenten coherent zijn voor de gebruiker. Afhankelijk van de vraag kan de LLM exacte inhoud uit uw index retourneren of de inhoud waarschijnlijk opnieuw verpakken voor een beter antwoord.
Georganiseerd rond segmenten
Wanneer LLM's een antwoord genereren, werken ze op segmenten inhoud voor berichtinvoer en moeten ze weten waar het segment vandaan komt voor bronvermeldingsdoeleinden, wat het belangrijkst is de kwaliteit van berichtinvoer en de relevantie ervan voor de vraag van de gebruiker. Ongeacht of de segmenten afkomstig zijn van één document of duizend, neemt de LLM de informatie of grondgegevens op en formuleert het antwoord met behulp van instructies in een systeemprompt.
Segmenten zijn de focus van het schema en elk segment is het definiërende element van een zoekdocument in een RAG-patroon. U kunt uw index beschouwen als een grote verzameling segmenten, in tegenstelling tot traditionele zoekdocumenten die waarschijnlijk meer structuur hebben, zoals velden met uniforme inhoud voor een naam, beschrijvingen, categorieën en adressen.
Uitgebreid met gegenereerde gegevens
In deze zelfstudie bestaan voorbeeldgegevens uit PDF's en inhoud uit het NASA Earth Book. Deze inhoud is beschrijvend en informatief, met talloze verwijzingen naar geografische gebieden, landen en gebieden over de hele wereld. Alle tekstuele inhoud wordt vastgelegd in segmenten, maar terugkerende instanties van plaatsnamen maken een kans om structuur toe te voegen aan de index. Met behulp van vaardigheden is het mogelijk om entiteiten in de tekst te herkennen en vast te leggen in een index voor gebruik in query's en filters. In deze zelfstudie bevatten we een vaardigheid voor entiteitsherkenning waarmee locatie-entiteiten worden herkend en geëxtraheerd en geladen in een doorzoekbaar en filterbaar locations
veld. Als u gestructureerde inhoud aan uw index toevoegt, krijgt u meer opties voor filteren, verbeterde relevantie en meer gerichte antwoorden.
Bovenliggende en onderliggende velden in een of twee indexen?
Gesegmenteerde inhoud is doorgaans afgeleid van een groter document. En hoewel het schema is geordend rond segmenten, wilt u ook eigenschappen en inhoud vastleggen op het bovenliggende niveau. Voorbeelden van deze eigenschappen zijn het bovenliggende bestandspad, de titel, de auteurs, de publicatiedatum of een samenvatting.
Een afbuigpunt in het schemaontwerp is of er twee indexen moeten zijn voor bovenliggende en onderliggende/gesegmenteerde inhoud, of één index die bovenliggende elementen voor elk segment herhaalt.
In deze zelfstudie, omdat alle stukken tekst afkomstig zijn van één bovenliggend item (NASA Earth Book), hebt u geen afzonderlijke index nodig die is toegewezen aan het niveau van de bovenliggende velden. Als u echter indexeert van meerdere bovenliggende PDF-bestanden, kunt u een bovenliggend/onderliggend indexpaar gebruiken om specifieke velden vast te leggen en vervolgens opzoekquery's naar de bovenliggende index verzenden om deze velden op te halen die relevant zijn voor elk segment.
Controlelijst met schemaoverwegingen
In Azure AI Search heeft een index die het beste werkt voor RAG-workloads de volgende kwaliteiten:
Retourneert segmenten die relevant zijn voor de query en die leesbaar zijn voor de LLM. LLM's kunnen een bepaald niveau van vuile gegevens verwerken in segmenten, zoals markeren, redundantie en onvolledige tekenreeksen. Hoewel segmenten leesbaar en relevant moeten zijn voor de vraag, hoeven ze niet ongerept te zijn.
Onderhoudt een relatie tussen segmenten van een document en de eigenschappen van het bovenliggende document, zoals de bestandsnaam, het bestandstype, de titel, de auteur enzovoort. Als u een query wilt beantwoorden, kunnen segmenten overal in de index worden opgehaald. Koppeling met het bovenliggende document dat het segment aanlevert, is handig voor context, bronvermeldingen en opvolgingsquery's.
Biedt plaats aan de query's die u wilt maken. U moet velden voor vector- en hybride inhoud hebben en deze velden moeten worden toegeschreven aan specifieke querygedragen, zoals doorzoekbaar of filterbaar. U kunt slechts één index tegelijk opvragen (geen joins), zodat uw veldenverzameling al uw doorzoekbare inhoud moet definiëren.
Uw schema moet plat zijn (geen complexe typen of structuren) of u moet de uitvoer van het complexe type opmaken als JSON voordat u het naar de LLM verzendt. Deze vereiste is specifiek voor het RAG-patroon in Azure AI Search.
Notitie
Schemaontwerp is van invloed op opslag en kosten. Deze oefening is gericht op de basisprincipes van schema's. In de zelfstudie Opslag en kosten minimaliseren gaat u terug naar schema's om te leren hoe smalle gegevenstypen, compressie en opslagopties de hoeveelheid opslagruimte die door vectoren wordt gebruikt aanzienlijk verminderen.
Een index maken voor RAG-workloads
Een minimale index voor LLM is ontworpen om segmenten van inhoud op te slaan. Het bevat meestal vectorvelden als u overeenkomsten zoekt naar zeer relevante resultaten. Het bevat ook niet-ctorvelden voor door mensen leesbare invoer aan de LLM voor gesprekszoekopdrachten. Niet-gesegmenteerde inhoud in de zoekresultaten wordt de geaarde gegevens die naar de LLM worden verzonden.
Open Visual Studio Code en maak een nieuw bestand. Het hoeft geen Python-bestandstype voor deze oefening te zijn.
Hier volgt een minimale indexdefinitie voor RAG-oplossingen die vector- en hybride zoekopdrachten ondersteunen. Bekijk deze voor een inleiding tot vereiste elementen: indexnaam, velden en een configuratiesectie voor vectorvelden.
{ "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" } ] } }
Velden moeten sleutelveld (
"id"
in dit voorbeeld) bevatten en vectorsegmenten bevatten voor overeenkomsten zoeken en niet-vector chunks voor invoer aan de LLM.Vectorvelden zijn gekoppeld aan algoritmen die de zoekpaden op het moment van de query bepalen. De index heeft een vectorSearch-sectie voor het opgeven van meerdere algoritmeconfiguraties. Vectorvelden hebben ook specifieke typen en extra kenmerken voor het insluiten van modeldimensies.
Edm.Single
is een gegevenstype dat geschikt is voor veelgebruikte LLM's. Zie Een vectorindex maken voor meer informatie over vectorvelden.Metagegevensvelden zijn mogelijk het bovenliggende bestandspad, de aanmaakdatum of het inhoudstype en zijn handig voor filters.
Dit is het indexschema voor de broncode van de zelfstudie en de inhoud van het Earth Book.
Net als het basisschema is het georganiseerd rond segmenten. Het
chunk_id
unieke identificeert elk segment. Hettext_vector
veld is een insluiting van het segment. Het veld nonvectorchunk
is een leesbare tekenreeks. Detitle
toewijzing wordt toegewezen aan een uniek opslagpad voor metagegevens voor de blobs. Hetparent_id
is het enige veld op bovenliggend niveau en het is een base64-gecodeerde versie van de bovenliggende bestands-URI.In geïntegreerde vectorisatieworkloads, zoals in deze reeks zelfstudies, moet de
dimensions
eigenschap op uw vectorvelden identiek zijn aan het aantaldimensions
gegenereerde door de insluitingsvaardigheid die wordt gebruikt om uw gegevens te vectoriseren. In deze reeks gebruiken we de vaardigheid voor insluiten van Azure OpenAI, die het model text-embedding-3-large aanroept in Azure OpenAI. De vaardigheid wordt opgegeven in de volgende zelfstudie. We stellen dimensies in op 1024 in zowel het vectorveld als in de vaardigheidsdefinitie.Het schema bevat ook een
locations
veld voor het opslaan van gegenereerde inhoud die is gemaakt door de indexeringspijplijn.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")
Voor een indexschema dat gestructureerde inhoud nauwkeuriger nabootst, hebt u afzonderlijke indexen voor bovenliggende en onderliggende (gesegmenteerde) velden. U hebt indexprojecties nodig om de indexering van de twee indexen tegelijk te coördineren. Query's worden uitgevoerd op basis van de onderliggende index. Querylogica bevat een opzoekquery met behulp van de parent_idt inhoud op te halen uit de bovenliggende index.
Velden in de onderliggende index:
- Id
- chunk
- chunkVectcor
- parent_id
Velden in de bovenliggende index (alles wat u wilt 'een van'):
- parent_id
- velden op bovenliggend niveau (naam, titel, categorie)