Utwórz aplikację konsolową przy użyciu biblioteki klienta Azure.Search.Documents , aby dodać semantyczną klasyfikację do istniejącego indeksu wyszukiwania.
Alternatywnie możesz pobrać kod źródłowy, aby rozpocząć od gotowego projektu.
Konfigurowanie środowiska
Uruchom program Visual Studio i utwórz nowy projekt dla aplikacji konsolowej.
W obszarze Narzędzia>Menedżer pakietów NuGet wybierz pozycję Zarządzaj pakietami NuGet dla rozwiązania....
Wybierz przycisk Przeglądaj.
Wyszukaj pakiet Azure.Search.Documents i wybierz najnowszą stabilną wersję.
Wybierz pozycję Zainstaluj , aby dodać zestaw do projektu i rozwiązania.
Tworzenie klienta wyszukiwania
W Program.cs dodaj następujące using
dyrektywy.
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
Utwórz dwóch klientów: Element SearchIndexClient tworzy indeks, a element SearchClient ładuje i wykonuje zapytania dotyczące istniejącego indeksu.
Oba klienci potrzebują punktu końcowego usługi i klucza interfejsu API administratora do uwierzytelniania z uprawnieniami tworzenia/usuwania. Jednak kod tworzy identyfikator URI dla Ciebie, dlatego określ tylko nazwę usługi wyszukiwania dla serviceName
właściwości. Nie dołączaj https://
ani .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);
. . .
}
Tworzenie indeksu
Utwórz lub zaktualizuj schemat indeksu, aby uwzględnić SemanticConfiguration
element . Jeśli aktualizujesz istniejący indeks, ta modyfikacja nie wymaga ponownego indeksowania, ponieważ struktura dokumentów pozostaje niezmieniona.
// 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);
}
Poniższy kod tworzy indeks w usłudze wyszukiwania:
// Create index
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, adminClient);
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
Ładowanie dokumentów
Usługa Azure AI Search wyszukuje zawartość przechowywaną w usłudze. Kod do przekazywania dokumentów jest identyczny z przewodnikiem Szybki start języka C# na potrzeby wyszukiwania pełnotekstowego, więc nie musimy go duplikować tutaj. Powinny istnieć cztery hotele z nazwami, adresami i opisami. Twoje rozwiązanie powinno mieć typy hoteli i adresów.
Przeszukiwanie indeksu
Oto zapytanie, które wywołuje semantyczny ranger z opcjami wyszukiwania określającymi parametry:
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);
Dla porównania poniżej przedstawiono wyniki zapytania korzystającego z domyślnego rankingu BM25 na podstawie częstotliwości terminów i zbliżenia. Biorąc pod uwagę zapytanie "co hotel ma dobrą restaurację na miejscu", algorytm klasyfikacji BM25 zwraca dopasowania w kolejności pokazanej na tym zrzucie ekranu:
Natomiast gdy klasyfikacja semantyczna jest stosowana do tego samego zapytania ("co hotel ma dobrą restaurację na miejscu"), wyniki są ponownie klasyfikowane na podstawie semantycznego istotności zapytania. Tym razem najlepszym wynikiem jest hotel z restauracją, która lepiej odpowiada oczekiwaniom użytkowników.
Uruchamianie programu
Naciśnij F5, aby ponownie skompilować aplikację i uruchomić program w całości.
Dane wyjściowe obejmują komunikaty z elementu Console.WriteLine z dodawaniem informacji o zapytaniu i wynikach.
Użyj notesu Jupyter i biblioteki azure-search-documents w zestawie Azure SDK dla języka Python, aby dowiedzieć się więcej o klasyfikacji semantycznej.
Alternatywnie możesz pobrać i uruchomić gotowy notes.
Konfigurowanie środowiska
Użyj programu Visual Studio Code z rozszerzeniem języka Python lub równoważnym środowiskiem IDE z językiem Python w wersji 3.10 lub nowszej.
W tym przewodniku Szybki start zalecamy środowisko wirtualne:
Uruchom program Visual Studio Code.
Utwórz nowy plik ipynb.
Otwórz paletę poleceń przy użyciu Ctrl+Shift+P.
Wyszukaj frazę Python: Create Environment (Tworzenie środowiska).
Wybierz pozycję Venv.
Wybierz interpreter języka Python. Wybierz 3.10 lub nowszą.
Skonfigurowanie może potrwać minutę. Jeśli wystąpią problemy, zobacz Środowiska języka Python w programie VS Code.
Instalowanie pakietów i ustawianie zmiennych
Zainstaluj pakiety, w tym azure-search-documents.
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
Podaj punkt końcowy i klucze interfejsu 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"
Tworzenie indeksu
Utwórz lub zaktualizuj schemat indeksu, aby uwzględnić SemanticConfiguration
element . Jeśli aktualizujesz istniejący indeks, ta modyfikacja nie wymaga ponownego indeksowania, ponieważ struktura dokumentów pozostaje niezmieniona.
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')
Tworzenie ładunku dokumentów
Możesz wypchnąć dokumenty JSON do indeksu wyszukiwania. Dokumenty muszą być zgodne ze schematem indeksu.
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"
}
}
]
Przekazywanie dokumentów do indeksu
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)
Uruchamianie pierwszego zapytania
Zacznij od pustego zapytania jako kroku weryfikacji, udowadniając, że indeks działa. Należy uzyskać nieurządkowaną listę nazw i opisów hotelowych z liczbą 4 wskazującą, że w indeksie znajdują się cztery dokumenty.
# 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']}")
Uruchamianie zapytania tekstowego
Na potrzeby porównania uruchom zapytanie tekstowe z oceniania istotnością BM25. Wyszukiwanie pełnotekstowe jest wywoływane po podaniu ciągu zapytania. Odpowiedź składa się z sklasyfikowanych wyników, gdzie wyższe wyniki są przyznawane dokumentom o większej liczbie wystąpień pasujących terminów lub ważniejszych terminów.
W tym zapytaniu o to, jaki hotel ma dobrą restaurację na miejscu, hotel Sublime Palace wychodzi na górę, ponieważ jego opis obejmuje witrynę. Terminy, które występują rzadko, zgłaszają wynik wyszukiwania dokumentu.
# 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']}")
Uruchamianie zapytania semantycznego
Teraz dodaj klasyfikację semantyczną. Nowe parametry obejmują query_type
i semantic_configuration_name
.
Jest to to to samo zapytanie, ale zwróć uwagę, że semantyczny rangą poprawnie identyfikuje hotel Gastronomic Landscape Hotel jako bardziej odpowiedni wynik, biorąc pod uwagę początkowe zapytanie. To zapytanie zwraca również podpisy wygenerowane przez modele. Dane wejściowe są zbyt minimalne w tym przykładzie, aby utworzyć interesujące podpisy, ale w przykładzie pokazano składnię.
# 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")
Zwracanie odpowiedzi semantycznych
W tym ostatnim zapytaniu zwróć semantyczne odpowiedzi.
Semantyczny ranger może generować odpowiedzi na ciąg zapytania, który ma cechy pytania. Wygenerowana odpowiedź jest wyodrębniona z zawartości. Aby uzyskać semantyczną odpowiedź, pytanie i odpowiedź muszą być ściśle dopasowane, a model musi znaleźć zawartość, która wyraźnie odpowiada na pytanie. Jeśli potencjalne odpowiedzi nie spełniają progu ufności, model nie zwraca odpowiedzi. W celach demonstracyjnych pytanie w tym przykładzie zostało zaprojektowane tak, aby uzyskać odpowiedź, aby zobaczyć składnię.
# 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")