Crie um aplicativo de console usando a biblioteca de clientes Azure.Search.Documentspara adicionar classificação semântica a um índice de pesquisa existente.
Como alternativa, é possível baixar o código-fonte para começar com um projeto concluído.
Configure seu 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 a Solução... .
Selecione Procurar.
Pesquise pelo pacote do Azure.Search.Documents e selecione a versão estável mais recente.
Selecione Instalar para adicionar o assembly ao projeto e à solução.
Criar um cliente de pesquisa
Em Program.cs, adicione as diretivas using
a seguir.
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 as clientes precisam do ponto de extremidade de serviço e de uma chave de API de administração 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 propriedade serviceName
. 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);
. . .
}
Crie um índice
Crie ou atualize um esquema de índice para incluir um SemanticConfiguration
. Se você estiver atualizando um índice existente, essa modificação não exigirá uma reindexação porque a estrutura de seus documentos não será alterada.
// 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 em seu serviço de pesquisa:
// Create index
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, adminClient);
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
Carregue os documentos
O Azure AI Search pesquisa o conteúdo armazenado no serviço. O código para carregar documentos é idêntico ao início rápido do C# para pesquisa de texto completo, portanto, 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:
Por outro lado, 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 principal resultado é o hotel com o restaurante, que se alinha melhor às expectativas do usuário.
Executar o programa
Pressione F5 para reconstruir o aplicativo e executar o programa por completo.
A saída inclui mensagens de Console.WriteLine, com a adição de resultados e informações de consulta.
Use um notebook do Jupyter e a biblioteca azure-search-documents no SDK do Azure para Python, para saber mais sobre classificação semântica.
Como alternativa, é possível baixar e executar um notebook concluído.
Configure seu ambiente
Use o Visual Studio Code com a extensão Python, ou um IDE equivalente, com Python 3.10 ou posterior.
É recomendável 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.
Pesquisar Python: criar ambiente.
Selecione Venv.
Selecione um interpretador do Python. Escolha 3.10 ou posterior.
Pode levar um minuto para ser configurado. Se você tiver problemas, consulte os Ambientes do 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 seu ponto de extremidade e chaves de 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"
Crie um índice
Crie ou atualize um esquema de índice para incluir um SemanticConfiguration
. Se você estiver atualizando um índice existente, essa modificação não exigirá uma reindexação porque a estrutura de seus documentos não será alterada.
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 um conteúdo de documentos
Você pode enviar documentos JSON por push 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 no í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)
Executar a 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, em que pontuações mais altas são concedidas a documentos com mais instâncias de termos correspondentes ou termos mais importantes.
Nesta consulta para qual hotel tem um bom restaurante no local, o Sublime Palace Hotel sai por cima porque sua descrição inclui site. Termos que ocorrem raramente 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. Novos parâmetros incluem query_type
e semantic_configuration_name
.
É a mesma consulta, mas observe que o classificador semântico identifica corretamente o Gastronomic Landscape Hotel como um resultado mais relevante, considerando a consulta inicial. Essa consulta também retorna legendas geradas pelos modelos. As entradas são muito mínimas neste exemplo, de modo a criar legendas interessantes, mas o exemplo demonstra a sintaxe com sucesso.
# 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 tem as características de uma pergunta. A resposta gerada é extraída textualmente do seu conteúdo. Para obter uma resposta semântica, a pergunta e a resposta devem estar alinhadas estreitamente, e o modelo deve encontrar conteúdo que responda claramente à pergunta. Se as respostas potenciais não atenderem a um limite de confiança, o modelo não retornará 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")