Udostępnij za pośrednictwem


Samouczek: tworzenie potoku indeksowania dla rozwiązania RAG w usłudze Azure AI Search

Dowiedz się, jak utworzyć zautomatyzowany potok indeksowania dla rozwiązania RAG w usłudze Azure AI Search. Automatyzacja indeksowania odbywa się za pomocą indeksatora, który napędza indeksowanie i wykonywanie zestawu umiejętności, zapewniając zintegrowane fragmentowanie i wektoryzacja danych jednorazowo lub cyklicznie na potrzeby aktualizacji przyrostowych.

W tym samouczku zostały wykonane następujące czynności:

  • Podaj schemat indeksu z poprzedniego samouczka
  • Tworzenie połączenia ze źródłem danych
  • Tworzenie indeksatora
  • Tworzenie zestawu umiejętności, który fragmentuje, wektoryzuje i rozpoznaje jednostki
  • Uruchamianie indeksatora i sprawdzanie wyników

Jeśli nie masz subskrypcji platformy Azure, przed rozpoczęciem utwórz bezpłatne konto.

Napiwek

Aby utworzyć potok, możesz użyć kreatora Importowanie i wektoryzowanie danych. Wypróbuj kilka przewodników Szybki start: wyszukiwanie obrazów i wyszukiwanie wektorów.

Wymagania wstępne

Pobierz przykład

Pobierz notes Jupyter z usługi GitHub, aby wysłać żądania do usługi Azure AI Search. Aby uzyskać więcej informacji, zobacz Pobieranie plików z usługi GitHub.

Podaj schemat indeksu

Otwórz lub utwórz notes Jupyter (.ipynb) w programie Visual Studio Code, aby zawierał skrypty składające się na potok. Początkowe kroki instalują pakiety i zbierają zmienne dla połączeń. Po wykonaniu kroków konfiguracji możesz rozpocząć od składników potoku indeksowania.

Zacznijmy od schematu indeksu z poprzedniego samouczka. Jest on zorganizowany wokół wektoryzowanych i niewektorowych fragmentów. Zawiera on pole, które przechowuje zawartość wygenerowaną przez sztuczną locations inteligencję utworzoną przez zestaw umiejętności.

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

Tworzenie połączenia ze źródłem danych

W tym kroku skonfiguruj przykładowe dane i połączenie z usługą Azure Blob Storage. Indeksator pobiera pliki PDF z kontenera. W tym kroku utworzysz kontener i przekażesz pliki.

Oryginalny podręcznik jest duży, ponad 100 stron i 35 MB rozmiaru. Podzieliliśmy go na mniejsze pliki PDF, po jednym na stronę tekstu, aby zachować limit dokumentów dla indeksatorów 16 MB na wywołanie interfejsu API, a także limity danych wzbogacania sztucznej inteligencji. Dla uproszczenia pomijamy wektoryzacja obrazów w tym ćwiczeniu.

  1. Zaloguj się do witryny Azure Portal i znajdź swoje konto usługi Azure Storage.

  2. Utwórz kontener i przekaż pliki PDF z earth_book_2019_text_pages.

  3. Upewnij się, że usługa Azure AI Search ma uprawnienia Czytelnik danych obiektu blob usługi Storage w zasobie.

  4. Następnie w programie Visual Studio Code zdefiniuj źródło danych indeksatora, które udostępnia informacje o połączeniu podczas indeksowania.

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

Jeśli skonfigurujesz tożsamość zarządzaną dla usługi Azure AI Search dla połączenia, parametry połączenia zawiera sufiksResourceId=. Powinien on wyglądać podobnie do poniższego przykładu: "ResourceId=/subscriptions/FAKE-SUBCRIPTION=ID/resourceGroups/FAKE-RESOURCE-GROUP/providers/Microsoft.Storage/storageAccounts/FAKE-ACCOUNT;"

Tworzenie zestawu umiejętności

Umiejętności są podstawą zintegrowanego fragmentowania i wektoryzacji danych. Co najmniej chcesz, aby umiejętność dzielenia tekstu dzieliła zawartość oraz umiejętności osadzania, które tworzą wektorowe reprezentacje fragmentowanej zawartości.

W tym zestawie umiejętności jest używana dodatkowa umiejętność tworzenia danych ustrukturyzowanych w indeksie. Umiejętność rozpoznawania jednostek służy do identyfikowania lokalizacji, które mogą zawierać nazwy od odpowiednich nazw do ogólnych odwołań, takich jak "ocean" lub "góra". Posiadanie danych ustrukturyzowanych zapewnia więcej opcji tworzenia interesujących zapytań i zwiększania istotności.

AZURE_AI_MULTISERVICE_KEY jest potrzebny, nawet jeśli używasz kontroli dostępu opartej na rolach. Usługa Azure AI Search używa klucza do celów rozliczeniowych i jest wymagana, chyba że obciążenia pozostaną w ramach bezpłatnego limitu. Możesz również nawiązać połączenie bez klucza, jeśli używasz najnowszego interfejsu API w wersji zapoznawczej lub pakietów beta. Aby uzyskać więcej informacji, zobacz Attach an Azure AI multi-service resource to a skillset (Dołączanie zasobu wielosłużytowego usługi Azure AI do zestawu umiejętności).

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

Tworzenie i uruchamianie indeksatora

Indeksatory są składnikiem, który ustawia wszystkie procesy w ruchu. Indeksator można utworzyć w stanie wyłączonym, ale ustawieniem domyślnym jest natychmiastowe uruchomienie go. W tym samouczku utwórz i uruchom indeksator, aby pobrać dane z usługi Blob Storage, wykonać umiejętności, w tym fragmentację i wektoryzację oraz załadować indeks.

Uruchomienie indeksatora trwa kilka minut. Gdy wszystko będzie gotowe, możesz przejść do ostatniego kroku: wykonywanie zapytań względem indeksu.

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

Uruchamianie zapytania w celu sprawdzenia wyników

Wyślij zapytanie, aby potwierdzić, że indeks działa. To żądanie konwertuje ciąg tekstowy "what's NASA's website?" na wektor wyszukiwania wektorowego. Wyniki składają się z pól w instrukcji select, z których niektóre są drukowane jako dane wyjściowe.

W tym momencie nie ma czatu ani generowania sztucznej inteligencji. Wyniki są dosłowną zawartością z indeksu wyszukiwania.

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

To zapytanie zwraca pojedyncze dopasowanie (top=1) składające się z jednego fragmentu określonego przez wyszukiwarkę jako najbardziej istotne. Wyniki zapytania powinny wyglądać podobnie do poniższego przykładu:

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

Spróbuj wykonać jeszcze kilka zapytań, aby uzyskać informacje o tym, co zwraca aparat wyszukiwania bezpośrednio, aby można było porównać je z odpowiedzią z włączoną funkcją LLM. Uruchom ponownie poprzedni skrypt za pomocą tego zapytania: "patagonia geography" i ustaw wartość top 3, aby zwrócić więcej niż jedną odpowiedź.

Wyniki z tego drugiego zapytania powinny wyglądać podobnie do poniższych wyników, które są lekko edytowane pod kątem wstrząsu. Dane wyjściowe są kopiowane z notesu, co powoduje obcięcie odpowiedzi na to, co zobaczysz w tym przykładzie. Możesz rozwinąć dane wyjściowe komórki, aby przejrzeć pełną odpowiedź.

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.

W tym przykładzie łatwiej jest dostrzec, jak fragmenty są zwracane dosłownie oraz jak słowo kluczowe i wyszukiwanie podobieństwa identyfikują najlepsze dopasowania. Ten konkretny fragment zdecydowanie zawiera informacje o Patagonii i geografii, ale nie jest to dokładnie istotne dla zapytania. Semantyczny ranga będzie promować bardziej istotne fragmenty, aby uzyskać lepszą odpowiedź, ale w następnym kroku zobaczmy, jak połączyć usługę Azure AI Search z modułem LLM na potrzeby wyszukiwania konwersacyjnego.

Następny krok