Crie um aplicativo de console usando a biblioteca de cliente Azure.Search.Documents para adicionar classificação semântica a um índice de pesquisa existente.
Como alternativa, você pode baixar o código-fonte para começar com um projeto concluído.
Configurar o ambiente
Inicie o Visual Studio e crie um novo projeto para um aplicativo de console.
Em Ferramentas>Gerenciador de Pacotes NuGet, selecione Gerenciar Pacotes NuGet para Solução....
Selecione Procurar.
Procure o pacote Azure.Search.Documents e selecione a versão estável mais recente.
Selecione Instalar para adicionar o assembly ao seu projeto e solução.
Criar um cliente de pesquisa
Em Program.cs, aditar as seguintes using
diretivas.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
Crie dois clientes: SearchIndexClient cria o índice e SearchClient carrega e consulta um índice existente.
Ambos os clientes precisam do ponto de extremidade do serviço e de uma chave de API de administrador para autenticação com direitos de criação/exclusão. No entanto, o código cria o URI para você, portanto, especifique apenas o nome do serviço de pesquisa para a serviceName
propriedade. Não inclua https://
ou .search.windows.net
.
static void Main(string[] args)
{
string serviceName = "<YOUR-SEARCH-SERVICE-NAME>";
string apiKey = "<YOUR-SEARCH-ADMIN-API-KEY>";
string indexName = "hotels-quickstart";
// Create a SearchIndexClient to send create/delete index commands
Uri serviceEndpoint = new Uri($"https://{serviceName}.search.windows.net/");
AzureKeyCredential credential = new AzureKeyCredential(apiKey);
SearchIndexClient adminClient = new SearchIndexClient(serviceEndpoint, credential);
// Create a SearchClient to load and query documents
SearchClient srchclient = new SearchClient(serviceEndpoint, indexName, credential);
. . .
}
Criar um índice
Crie ou atualize um esquema de índice para incluir um SemanticConfiguration
arquivo . Se você estiver atualizando um índice existente, essa modificação não exigirá uma reindexação porque a estrutura dos documentos permanece inalterada.
// Create hotels-quickstart index
private static void CreateIndex(string indexName, SearchIndexClient adminClient)
{
FieldBuilder fieldBuilder = new FieldBuilder();
var searchFields = fieldBuilder.Build(typeof(Hotel));
var definition = new SearchIndex(indexName, searchFields);
var suggester = new SearchSuggester("sg", new[] { "HotelName", "Category", "Address/City", "Address/StateProvince" });
definition.Suggesters.Add(suggester);
definition.SemanticSearch = new SemanticSearch
{
Configurations =
{
new SemanticConfiguration("my-semantic-config", new()
{
TitleField = new SemanticField("HotelName"),
ContentFields =
{
new SemanticField("Description"),
new SemanticField("Description_fr")
},
KeywordsFields =
{
new SemanticField("Tags"),
new SemanticField("Category")
}
})
}
};
adminClient.CreateOrUpdateIndex(definition);
}
O código a seguir cria o índice no seu serviço de pesquisa:
// Create index
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, adminClient);
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
Carregar documentos
O Azure AI Search pesquisa sobre o conteúdo armazenado no serviço. O código para carregar documentos é idêntico ao início rápido em C# para pesquisa de texto completo, por isso não precisamos duplicá-lo aqui. Você deve ter quatro hotéis com nomes, endereços e descrições. Sua solução deve ter tipos para Hotéis e Endereços.
Pesquisar um índice
Aqui está uma consulta que invoca o classificador semântico, com opções de pesquisa para especificar parâmetros:
Console.WriteLine("Example of a semantic query.");
options = new SearchOptions()
{
QueryType = Azure.Search.Documents.Models.SearchQueryType.Semantic,
SemanticSearch = new()
{
SemanticConfigurationName = "my-semantic-config",
QueryCaption = new(QueryCaptionType.Extractive)
}
};
options.Select.Add("HotelName");
options.Select.Add("Category");
options.Select.Add("Description");
// response = srchclient.Search<Hotel>("*", options);
response = srchclient.Search<Hotel>("what hotel has a good restaurant on site", options);
WriteDocuments(response);
Para comparação, aqui estão os resultados de uma consulta que usa a classificação BM25 padrão, com base na frequência e proximidade do termo. Dada a consulta "qual hotel tem um bom restaurante no local", o algoritmo de classificação BM25 retorna correspondências na ordem mostrada nesta captura de tela:
Em contraste, quando a classificação semântica é aplicada à mesma consulta ("qual hotel tem um bom restaurante no local"), os resultados são reclassificados com base na relevância semântica para a consulta. Desta vez, o melhor resultado é o hotel com o restaurante, que se alinha melhor às expectativas dos usuários.
Execute o programa
Pressione F5 para reconstruir o aplicativo e executar o programa em sua totalidade.
A saída inclui mensagens de Console.WriteLine, com a adição de informações de consulta e resultados.
Use um bloco de anotações Jupyter e a biblioteca azure-search-documents no SDK do Azure para Python para saber mais sobre classificação semântica.
Como alternativa, você pode baixar e executar um bloco de anotações concluído.
Configurar o ambiente
Use o Visual Studio Code com a extensão Python, ou IDE equivalente, com Python 3.10 ou posterior.
Recomendamos um ambiente virtual para este início rápido:
Inicie o Visual Studio Code.
Crie um novo arquivo ipynb.
Abra a Paleta de Comandos usando Ctrl+Shift+P.
Procure por Python: Create Environment.
Selecione Venv.
Selecione um interpretador Python. Escolha 3.10 ou posterior.
A configuração pode demorar um minuto. Se você tiver problemas, consulte Ambientes Python no VS Code.
Instalar pacotes e definir variáveis
Instale pacotes, incluindo azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
Forneça suas chaves de endpoint e API:
search_endpoint: str = "PUT-YOUR-SEARCH-SERVICE-ENDPOINT-HERE"
search_api_key: str = "PUT-YOUR-SEARCH-SERVICE-ADMIN-API-KEY-HERE"
index_name: str = "hotels-quickstart"
Criar um índice
Crie ou atualize um esquema de índice para incluir um SemanticConfiguration
arquivo . Se você estiver atualizando um índice existente, essa modificação não exigirá uma reindexação porque a estrutura dos documentos permanece inalterada.
from azure.search.documents.indexes import SearchIndexClient
from azure.search.documents import SearchClient
from azure.search.documents.indexes.models import (
ComplexField,
SimpleField,
SearchFieldDataType,
SearchableField,
SearchIndex,
SemanticConfiguration,
SemanticField,
SemanticPrioritizedFields,
SemanticSearch
)
# Create a search schema
index_client = SearchIndexClient(
endpoint=search_endpoint, credential=credential)
fields = [
SimpleField(name="HotelId", type=SearchFieldDataType.String, key=True),
SearchableField(name="HotelName", type=SearchFieldDataType.String, sortable=True),
SearchableField(name="Description", type=SearchFieldDataType.String, analyzer_name="en.lucene"),
SearchableField(name="Description_fr", type=SearchFieldDataType.String, analyzer_name="fr.lucene"),
SearchableField(name="Category", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="Tags", collection=True, type=SearchFieldDataType.String, facetable=True, filterable=True),
SimpleField(name="ParkingIncluded", type=SearchFieldDataType.Boolean, facetable=True, filterable=True, sortable=True),
SimpleField(name="LastRenovationDate", type=SearchFieldDataType.DateTimeOffset, facetable=True, filterable=True, sortable=True),
SimpleField(name="Rating", type=SearchFieldDataType.Double, facetable=True, filterable=True, sortable=True),
ComplexField(name="Address", fields=[
SearchableField(name="StreetAddress", type=SearchFieldDataType.String),
SearchableField(name="City", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="StateProvince", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="PostalCode", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
SearchableField(name="Country", type=SearchFieldDataType.String, facetable=True, filterable=True, sortable=True),
])
]
semantic_config = SemanticConfiguration(
name="my-semantic-config",
prioritized_fields=SemanticPrioritizedFields(
title_field=SemanticField(field_name="HotelName"),
keywords_fields=[SemanticField(field_name="Category")],
content_fields=[SemanticField(field_name="Description")]
)
)
# Create the semantic settings with the configuration
semantic_search = SemanticSearch(configurations=[semantic_config])
semantic_settings = SemanticSearch(configurations=[semantic_config])
scoring_profiles = []
suggester = [{'name': 'sg', 'source_fields': ['Tags', 'Address/City', 'Address/Country']}]
# Create the search index with the semantic settings
index = SearchIndex(name=index_name, fields=fields, suggesters=suggester, scoring_profiles=scoring_profiles, semantic_search=semantic_search)
result = index_client.create_or_update_index(index)
print(f' {result.name} created')
Criar uma carga útil de documentos
Você pode enviar documentos JSON para um índice de pesquisa. Os documentos devem corresponder ao esquema de índice.
documents = [
{
"@search.action": "upload",
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"Description": "The hotel is ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Time's Square and the historic centre of the city, as well as other places of interest that make New York one of America's most attractive and cosmopolitan cities.",
"Description_fr": "L'hôtel est idéalement situé sur la principale artère commerciale de la ville en plein cœur de New York. A quelques minutes se trouve la place du temps et le centre historique de la ville, ainsi que d'autres lieux d'intérêt qui font de New York l'une des villes les plus attractives et cosmopolites de l'Amérique.",
"Category": "Boutique",
"Tags": [ "pool", "air conditioning", "concierge" ],
"ParkingIncluded": "false",
"LastRenovationDate": "1970-01-18T00:00:00Z",
"Rating": 3.60,
"Address": {
"StreetAddress": "677 5th Ave",
"City": "New York",
"StateProvince": "NY",
"PostalCode": "10022",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "2",
"HotelName": "Old Century Hotel",
"Description": "The hotel is situated in a nineteenth century plaza, which has been expanded and renovated to the highest architectural standards to create a modern, functional and first-class hotel in which art and unique historical elements coexist with the most modern comforts.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Boutique",
"Tags": [ "pool", "free wifi", "concierge" ],
"ParkingIncluded": "false",
"LastRenovationDate": "1979-02-18T00:00:00Z",
"Rating": 3.60,
"Address": {
"StreetAddress": "140 University Town Center Dr",
"City": "Sarasota",
"StateProvince": "FL",
"PostalCode": "34243",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "3",
"HotelName": "Gastronomic Landscape Hotel",
"Description": "The Hotel stands out for its gastronomic excellence under the management of William Dough, who advises on and oversees all of the Hotel's restaurant services.",
"Description_fr": "L'hôtel est situé dans une place du XIXe siècle, qui a été agrandie et rénovée aux plus hautes normes architecturales pour créer un hôtel moderne, fonctionnel et de première classe dans lequel l'art et les éléments historiques uniques coexistent avec le confort le plus moderne.",
"Category": "Resort and Spa",
"Tags": [ "air conditioning", "bar", "continental breakfast" ],
"ParkingIncluded": "true",
"LastRenovationDate": "2015-09-20T00:00:00Z",
"Rating": 4.80,
"Address": {
"StreetAddress": "3393 Peachtree Rd",
"City": "Atlanta",
"StateProvince": "GA",
"PostalCode": "30326",
"Country": "USA"
}
},
{
"@search.action": "upload",
"HotelId": "4",
"HotelName": "Sublime Palace Hotel",
"Description": "Sublime Palace Hotel is located in the heart of the historic center of Sublime in an extremely vibrant and lively area within short walking distance to the sites and landmarks of the city and is surrounded by the extraordinary beauty of churches, buildings, shops and monuments. Sublime Palace is part of a lovingly restored 1800 palace.",
"Description_fr": "Le Sublime Palace Hotel est situé au coeur du centre historique de sublime dans un quartier extrêmement animé et vivant, à courte distance de marche des sites et monuments de la ville et est entouré par l'extraordinaire beauté des églises, des bâtiments, des commerces et Monuments. Sublime Palace fait partie d'un Palace 1800 restauré avec amour.",
"Category": "Boutique",
"Tags": [ "concierge", "view", "24-hour front desk service" ],
"ParkingIncluded": "true",
"LastRenovationDate": "1960-02-06T00:00:00Z",
"Rating": 4.60,
"Address": {
"StreetAddress": "7400 San Pedro Ave",
"City": "San Antonio",
"StateProvince": "TX",
"PostalCode": "78216",
"Country": "USA"
}
}
]
Carregar documentos para o índice
search_client = SearchClient(endpoint=search_endpoint,
index_name=index_name,
credential=credential)
try:
result = search_client.upload_documents(documents=documents)
print("Upload of new document succeeded: {}".format(result[0].succeeded))
except Exception as ex:
print (ex.message)
index_client = SearchIndexClient(
endpoint=search_endpoint, credential=credential)
Execute a sua primeira consulta
Comece com uma consulta vazia como uma etapa de verificação, provando que o índice está operacional. Você deve obter uma lista não ordenada de nomes e descrições de hotéis, com uma contagem de 4 indicando que há quatro documentos no índice.
# Run an empty query (returns selected fields, all documents)
results = search_client.search(query_type='simple',
search_text="*" ,
select='HotelName,Description',
include_total_count=True)
print ('Total Documents Matching Query:', results.get_count())
for result in results:
print(result["@search.score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
Executar uma consulta de texto
Para fins de comparação, execute uma consulta de texto com pontuação de relevância BM25. A pesquisa de texto completo é invocada quando você fornece uma cadeia de caracteres de consulta. A resposta consiste em resultados classificados, onde pontuações mais altas são atribuídas a documentos com mais instâncias de correspondência de termos, ou termos mais importantes.
Nesta consulta para que hotel tem um bom restaurante no local, o Sublime Palace Hotel sai na frente porque a sua descrição inclui site. Termos que ocorrem com pouca frequência aumentam a pontuação de pesquisa do documento.
# Run a text query (returns a BM25-scored result set)
results = search_client.search(query_type='simple',
search_text="what hotel has a good restaurant on site" ,
select='HotelName,HotelId,Description',
include_total_count=True)
for result in results:
print(result["@search.score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
Executar uma consulta semântica
Agora adicione classificação semântica. Os novos parâmetros incluem query_type
e semantic_configuration_name
.
É a mesma consulta, mas repare que o ranker semântico identifica corretamente o Gastronomic Landscape Hotel como um resultado mais relevante dada a consulta inicial. Essa consulta também retorna legendas geradas pelos modelos. As entradas são mínimas demais neste exemplo para criar legendas interessantes, mas o exemplo consegue demonstrar a sintaxe.
# Runs a semantic query (runs a BM25-ranked query and promotes the most relevant matches to the top)
results = search_client.search(query_type='semantic', semantic_configuration_name='my-semantic-config',
search_text="what hotel has a good restaurant on site",
select='HotelName,Description,Category', query_caption='extractive')
for result in results:
print(result["@search.reranker_score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
captions = result["@search.captions"]
if captions:
caption = captions[0]
if caption.highlights:
print(f"Caption: {caption.highlights}\n")
else:
print(f"Caption: {caption.text}\n")
Retornar respostas semânticas
Nesta consulta final, retorne respostas semânticas.
O classificador semântico pode gerar respostas para uma cadeia de caracteres de consulta que tenha as características de uma pergunta. A resposta gerada é extraída literalmente do seu conteúdo. Para obter uma resposta semântica, a pergunta e a resposta devem estar estreitamente alinhadas e o modelo deve encontrar conteúdo que responda claramente à pergunta. Se as respostas potenciais não atingirem um limiar de confiança, o modelo não devolve uma resposta. Para fins de demonstração, a pergunta neste exemplo foi projetada para obter uma resposta para que você possa ver a sintaxe.
# Run a semantic query that returns semantic answers
results = search_client.search(query_type='semantic', semantic_configuration_name='my-semantic-config',
search_text="what hotel is in a historic building",
select='HotelName,Description,Category', query_caption='extractive', query_answer="extractive",)
semantic_answers = results.get_answers()
for answer in semantic_answers:
if answer.highlights:
print(f"Semantic Answer: {answer.highlights}")
else:
print(f"Semantic Answer: {answer.text}")
print(f"Semantic Answer Score: {answer.score}\n")
for result in results:
print(result["@search.reranker_score"])
print(result["HotelName"])
print(f"Description: {result['Description']}")
captions = result["@search.captions"]
if captions:
caption = captions[0]
if caption.highlights:
print(f"Caption: {caption.highlights}\n")
else:
print(f"Caption: {caption.text}\n")