Tutorial: minimizar o armazenamento e os custos (RAG na Pesquisa de IA do Azure)
A Pesquisa de IA do Azure oferece várias abordagens para reduzir o tamanho dos índices de vetor. Essas abordagens vão desde compactação de vetor até ser mais seletiva sobre o que você armazena no seu serviço de pesquisa.
Neste tutorial, você modificará o índice de pesquisa existente para usar:
- Tipos de dados estreitos
- Compartimentalização escalar
- Redução do armazenamento ao optar por não usar 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, você cria um segundo para que possa comparar reduções no tamanho do índice de vetor depois de adicionar os novos recursos.
Ao todo, as técnicas ilustradas neste tutorial podem reduzir o armazenamento de vetores em cerca de metade.
A captura de tela a seguir compara o primeiro índice de um tutorial anterior com o índice compilado neste.
Pré-requisitos
Este tutorial é essencialmente uma nova execuçã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 no seu serviço de Pesquisa de IA do Azure. Deve ter quase 2 MB de tamanho e a parte do índice de vetor deve ter 348 KB.
Você também deve ter os seguintes objetos:
py-rag-tutorial-ds (fonte de dados)
py-rag-tutorial-ss (conjunto de habilidades)
Baixar o exemplo
No GitHub, baixe um notebook do Jupyter para enviar as solicitações para a Pesquisa de IA do Azure. Para obter mais informações, consulte Baixando arquivos do GitHub.
Atualizar o índice para armazenamento reduzido
A Pesquisa de IA do Azure tem várias abordagens para reduzir o tamanho do vetor, o que reduz o custo das cargas de trabalho de vetor. Nesta etapa, crie um novo índice que use os seguintes recursos:
Índices de vetor menores compactando os vetores usados durante a execução da consulta. A quantização escalar fornece essa funcionalidade.
Índices de vetor menores, optando por não usar armazenamento de vetor para resultados da pesquisa. Se você precisar apenas de vetores para consultas e não em conteúdo de resposta, poderá remover a cópia do vetor usada para os resultados da pesquisa.
Campos de vetor menores por meio de tipos de dados estreitos. Você pode especificar
Collection(Edm.Half)
no campo text_vector 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.
Nomeie o novo índice
py-rag-tutorial-small-vectors-idx
.Use a definição a seguir para o novo índice. A diferença entre esse esquema e as atualizações de esquema anteriores em Maximizar a relevância são novas classes para quantização escalar e uma nova seção de compactações, um novo tipo de dados (
Collection(Edm.Half)
) para o campo text_vector e uma nova propriedadestored
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 você já tiver essa fonte de dados no seu serviço de pesquisa, poderá 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 é inalterado em relação ao tutorial anterior. Veja-o novamente para que você possa analisá-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, é 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.
Esse indexador é idêntico ao indexador anterior, exceto pelo fato de especificar 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ê deverá ver resultados semelhantes às seguintes capturas de tela.
O índice criado neste tutorial usa números de ponto 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). Compactação escalar e a omissão de um conjunto da conta de vetores para as economias de armazenamento restantes. 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 de LLM sempre que repetir uma consulta, mas, em geral, as técnicas de salvamento de armazenamento implementadas não devem prejudicar a qualidade dos resultados da pesquisa.
Próxima etapa
Há exemplos de código em todos os SDKs do Azure que fornecem programação da Pesquisa de IA do Azure. Você também pode examinar o código de exemplo de vetor para casos de uso específicos e combinações de tecnologia.