Cree una aplicación de consola utilizando la biblioteca cliente de Azure.Search.Documents para agregar clasificación semántica a un índice de búsqueda existente.
Como alternativa, puede descargar el código fuente para empezar con un proyecto terminado.
Configurar el entorno
Inicie Visual Studio y cree un nuevo proyecto para una aplicación de consola.
En Herramientas>Administrador de paquetes NuGet seleccione Administrar paquetes NuGet para la solución.... .
Seleccione Examinar.
Busque el paquete de Azure.Search.Documents y seleccione la versión estable más reciente.
Seleccione Instalar para agregar el ensamblado al proyecto y la solución.
Creación de un cliente de búsqueda
En Program.cs, agregue las siguientes directivas using
.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
Cree dos clientes: SearchIndexClient crea el índice y SearchClient carga y consulta uno existente.
Ambos clientes necesitan el punto de conexión de servicio y una clave de API de administrador para la autenticación con derechos de creación y eliminación. Sin embargo, el código crea el URI automáticamente, por lo que especifica solo el nombre del servicio de búsqueda para la propiedad serviceName
. No incluya https://
ni .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);
. . .
}
Creación de un índice
Cree o actualice un esquema de índice para incluir una SemanticConfiguration
. Si va a actualizar un índice existente, esta modificación no requiere una reindexación porque la estructura de los documentos no ha cambiado.
// 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);
}
Con el código siguiente, se crea el índice en el servicio de búsqueda:
// Create index
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, adminClient);
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
Carga de documentos
Azure AI Search busca en el contenido almacenado en el servicio. El código para cargar documentos es idéntico al del inicio rápido de C# para la búsqueda de texto completo, por lo que no es necesario duplicarlo aquí. Debe tener cuatro hoteles con nombres, direcciones y descripciones. La solución debe tener tipos para hoteles y direcciones.
Búsqueda de un índice
La siguiente es una consulta que invoca al clasificador semántico, con opciones de búsqueda 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 fines de comparación, estos son los resultados de una consulta que usa la clasificación BM25 predeterminada, basada en la frecuencia del término y la proximidad. Dada la consulta "qué hotel tiene un buen restaurante en el sitio", el algoritmo de clasificación BM25 devuelve coincidencias en el orden que se muestra en esta captura de pantalla:
Por el contrario, cuando se aplica la clasificación semántica a la misma consulta ("qué hotel tiene un buen restaurante en el sitio"), los resultados se vuelven a clasificar en función de la relevancia semántica de la consulta. Esta vez, el resultado principal es el hotel con el restaurante, que se alinea mejor con las expectativas del usuario.
Ejecución del programa
Presione F5 para recompilar la aplicación y ejecutar el programa en su totalidad.
La salida incluye mensajes de Console.WriteLIne, con la incorporación de la información de la consulta y los resultados.
Use un cuaderno de Jupyter Notebook y la biblioteca azure-search-documents del SDK de Azure para Python para obtener más información sobre clasificación semántica.
Como alternativa, puede descargar y ejecutar un cuaderno terminado.
Configurar el entorno
Use Visual Studio Code con la extensión de Python, o un IDE equivalente, con Python 3.10 o posterior.
Se recomienda un entorno virtual para esta guía de inicio rápido:
Inicie Visual Studio Code.
Crear un nuevo archivo ipynb.
Abra la paleta de comandos mediante Ctrl+Mayús+P.
Busque Python: Crear entorno.
Seleccione Venv.
Seleccione un intérprete de Python. Elija 3.10 o posterior.
Puede tardar un minuto en configurarse. Si tiene problemas, consulte Entornos de Python en VS Code.
Instalación de paquetes y establecimiento de variables
Instale paquetes, incluidos azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
Proporcione el punto de conexión y las claves 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"
Creación de un índice
Cree o actualice un esquema de índice para incluir una SemanticConfiguration
. Si va a actualizar un índice existente, esta modificación no requiere una reindexación porque la estructura de los documentos no ha cambiado.
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')
Creación de una carga de documentos
Puede insertar documentos JSON en un índice de búsqueda. Los documentos deben coincidir con el 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"
}
}
]
Cargar documentos en el í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)
Ejecución de la primera consulta
Comience con una consulta vacía como paso de comprobación, para verificar que el índice esté operativo. Debe obtener una lista desordenada de nombres y descripciones de hoteles, con un recuento de 4, lo que indica que hay cuatro documentos en el í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']}")
Ejecutar una consulta de texto
Con fines de comparación, ejecute una consulta de texto con puntuación de relevancia BM25. La búsqueda de texto completo se invoca cuando se proporciona una cadena de consulta. La respuesta consta de resultados clasificados, en la que se asignan puntuaciones más altas a los documentos que tienen más instancias de términos coincidentes o términos más importantes.
En esta consulta para qué hotel tiene un buen restaurante en el sitio, Sublime Palace Hotel se encuentra en la parte superior porque su descripción incluye sitio. Los términos que se producen con poca frecuencia suben la puntuación de búsqueda del 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']}")
Ejecutar una consulta semántica
Ahora agregue la clasificación semántica. Los nuevos parámetros incluyen query_type
y semantic_configuration_name
.
Es la misma consulta, pero tenga en cuenta que el clasificador semántico identifica correctamente El Gastronomic Landscape Hotel como un resultado más relevante dada la consulta inicial. Esta consulta también devuelve descripciones generadas por los modelos. Las entradas son insuficientes en este ejemplo para crear descripciones interesantes, pero el ejemplo realiza correctamente la demostración de la sintaxis.
# 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")
Devolver respuestas semánticas
En esta consulta final, se devuelven respuestas semánticas.
El clasificador semántico puede generar respuestas a una cadena de consulta que tenga las características de una pregunta. La respuesta generada se extrae textualmente del contenido. Para obtener una respuesta semántica, la pregunta y la respuesta deben estar estrechamente alineadas y el modelo debe encontrar contenido que responda claramente a la pregunta. Si las posibles respuestas no alcanzan el umbral de confianza, el modelo no devuelve una respuesta. Para fines de demostración, la pregunta de este ejemplo está diseñada para obtener una respuesta que le permita ver la sintaxis.
# 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")