Utilice la biblioteca cliente Azure.Search.Documents para crear una aplicación de consola y así poder agregar la 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 o seguir los pasos de este artículo para crear su propio proyecto.
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 necesitan el punto de conexión de servicio y una clave de API de administración para la autenticación con derechos de creación y eliminación.
Dado que el código compila el URI automáticamente, especifique solo el nombre del servicio de búsqueda en la propiedad "serviceName".
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 la clasificación semántica, 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, descargue y ejecute 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 (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": "Secret Point Motel",
"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": "Twin Dome Motel",
"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": "Triple 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 Cliff Hotel",
"Description": "Sublime Cliff 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 Cliff is part of a lovingly restored 1800 palace.",
"Description_fr": "Le sublime Cliff 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 Cliff 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 la 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 Cliff Hotel se encuentra al principio 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 observe que el clasificador semántico identifica correctamente Triple Landscape Hotel como resultado más relevante con 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.
La clasificación semántica puede generar respuestas a una cadena de consulta que tiene 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")