Partilhar via


Tutorial: Minimizar o armazenamento e os custos (RAG no Azure AI Search)

O Azure AI Search oferece várias abordagens para reduzir o tamanho dos índices vetoriais. Essas abordagens vão desde a compressão vetorial até ser mais seletiva sobre o que você armazena em seu serviço de pesquisa.

Neste tutorial, você modifica o índice de pesquisa existente para usar:

  • Tipos de dados estreitos
  • Quantização escalar
  • Armazenamento reduzido ao desativar vetores nos resultados de pesquisa

Este tutorial reprisa o índice de pesquisa criado pelo pipeline de indexação. Todas essas atualizações afetam o conteúdo existente, exigindo que você execute novamente o indexador. No entanto, em vez de excluir o índice de pesquisa, crie um segundo para que possa comparar as reduções no tamanho do índice de vetor depois de adicionar os novos recursos.

No conjunto, as técnicas ilustradas neste tutorial podem reduzir o armazenamento vetorial em cerca de metade.

A captura de tela a seguir compara o primeiro índice de um tutorial anterior com o índice criado neste.

Captura de tela do índice vetorial original com o índice criado usando o esquema neste tutorial.

Pré-requisitos

Este tutorial é essencialmente uma repetição do pipeline de indexação. Você precisa de todos os recursos e permissões do Azure descritos nesse tutorial.

Para comparação, você deve ter um índice py-rag-tutorial-idx existente em seu serviço Azure AI Search. Deve ter quase 2 MB de tamanho, e a parte do índice vetorial deve ser de 348 KB.

Você também deve ter os seguintes objetos:

  • py-rag-tutorial-ds (fonte de dados)

  • py-rag-tutorial-ss (conjunto de habilidades)

Transferir o exemplo

Baixe um bloco de anotações Jupyter do GitHub para enviar as solicitações para o Azure AI Search. Para obter mais informações, consulte Baixando arquivos do GitHub.

Atualizar o índice para armazenamento reduzido

O Azure AI Search tem várias abordagens para reduzir o tamanho do vetor, o que reduz o custo das cargas de trabalho vetoriais. Nesta etapa, crie um novo índice que use os seguintes recursos:

  • Índices vetoriais menores compactando os vetores usados durante a execução da consulta. A quantização escalar fornece essa capacidade.

  • Índices vetoriais menores, desativando o armazenamento de vetores para resultados de pesquisa. Se você só precisa de vetores para consultas e não em carga útil de resposta, você pode soltar a cópia de vetor usada para resultados de pesquisa.

  • Campos vetoriais menores através de tipos de dados estreitos. Você pode especificar Collection(Edm.Half) no campo text_vetor para armazenar dimensões float32 de entrada como float16.

Todos esses recursos são especificados em um índice de pesquisa. Depois de carregar o índice, compare a diferença entre o índice original e o novo.

  1. Nomeie o novo índice py-rag-tutorial-small-vectors-idx.

  2. Use a seguinte definição para o novo índice. A diferença entre esse esquema e as atualizações de esquema anteriores em Maximizar relevância são novas classes para quantização escalar e uma nova seção de compressões, um novo tipo de dados (Collection(Edm.Half)) para o campo text_vetor e uma nova propriedade stored definida como false.

    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,
        ScalarQuantizationCompression,
        ScalarQuantizationParameters,
        SearchIndex,
        SemanticConfiguration,
        SemanticPrioritizedFields,
        SemanticField,
        SemanticSearch,
        ScoringProfile,
        TagScoringFunction,
        TagScoringParameters
    )
    
    credential = DefaultAzureCredential()
    
    index_name = "py-rag-tutorial-small-vectors-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="Collection(Edm.Half)", vector_search_dimensions=1024, vector_search_profile_name="myHnswProfile", stored= False)
        ]  
    
    # Configure the vector search configuration  
    vector_search = VectorSearch(  
        algorithms=[  
            HnswAlgorithmConfiguration(name="myHnsw"),
        ],  
        profiles=[  
            VectorSearchProfile(  
                name="myHnswProfile",  
                algorithm_configuration_name="myHnsw",
                compression_name="myScalarQuantization",
                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"
                ),
            ),  
        ],
        compressions=[
            ScalarQuantizationCompression(
                compression_name="myScalarQuantization",
                rerank_with_original_vectors=True,
                default_oversampling=10,
                parameters=ScalarQuantizationParameters(quantized_data_type="int8"),
            )
        ]
    )
    
    semantic_config = SemanticConfiguration(
        name="my-semantic-config",
        prioritized_fields=SemanticPrioritizedFields(
            title_field=SemanticField(field_name="title"),
            keywords_fields=[SemanticField(field_name="locations")],
            content_fields=[SemanticField(field_name="chunk")]
        )
    )
    
    semantic_search = SemanticSearch(configurations=[semantic_config])
    
    scoring_profiles = [  
        ScoringProfile(  
            name="my-scoring-profile",
            functions=[
                TagScoringFunction(  
                    field_name="locations",  
                    boost=5.0,  
                    parameters=TagScoringParameters(  
                        tags_parameter="tags",  
                    ),  
                ) 
            ]
        )
    ]
    
    index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search, semantic_search=semantic_search, scoring_profiles=scoring_profiles)  
    result = index_client.create_or_update_index(index)  
    print(f"{result.name} created")
    

Criar ou reutilizar a fonte de dados

Aqui está a definição da fonte de dados do tutorial anterior. Se já tiver esta fonte de dados no seu serviço de pesquisa, pode ignorar a criação de uma nova.

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

Criar ou reutilizar o conjunto de habilidades

O conjunto de habilidades também permanece inalterado em relação ao tutorial anterior. Aqui está novamente para que você possa revê-lo.

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

Criar um novo indexador e carregar o índice

Embora você possa redefinir e executar novamente o indexador existente usando o novo índice, é igualmente fácil criar um novo indexador. Ter dois índices e indexadores preserva o histórico de execução e permite comparações mais próximas.

Este indexador é idêntico ao indexador anterior, exceto que ele especifica o novo índice deste tutorial.

from azure.search.documents.indexes.models import (
    SearchIndexer
)

# Create an indexer  
indexer_name = "py-rag-tutorial-small-vectors-idxr" 

indexer_parameters = None

indexer = SearchIndexer(  
    name=indexer_name,  
    description="Indexer to index documents and generate embeddings",
    target_index_name="py-rag-tutorial-small-vectors-idx",
    skillset_name="py-rag-tutorial-ss", 
    data_source_name="py-rag-tutorial-ds",
    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.')

Como etapa final, alterne para o portal do Azure para comparar os requisitos de armazenamento de vetor para os dois índices. Você deve ter resultados semelhantes à captura de tela a seguir.

Captura de tela do índice vetorial original com o índice criado usando o esquema neste tutorial.

O índice criado neste tutorial usa números de vírgula flutuante de meia precisão (float16) para os vetores de texto. Isso reduz os requisitos de armazenamento para os vetores pela metade em comparação com o índice anterior que usava números de ponto flutuante de precisão única (float32). A compressão escalar e a omissão de um conjunto de vetores são responsáveis pela economia de armazenamento restante. Para obter mais informações sobre como reduzir o tamanho do vetor, consulte Escolher uma abordagem para otimizar o armazenamento e o processamento de vetores.

Considere revisitar as consultas do tutorial anterior para que você possa comparar a velocidade e o utilitário da consulta. Você deve esperar alguma variação na saída LLM sempre que repetir uma consulta, mas, em geral, as técnicas de economia de armazenamento implementadas não devem degradar a qualidade dos resultados da pesquisa.

Próximo passo

Há exemplos de código em todos os SDKs do Azure que fornecem a programação do Azure AI Search. Você também pode revisar o código de exemplo vetorial para casos de uso específicos e combinações de tecnologia.