Azure.Search.Documents クライアント ライブラリを使用してコンソール アプリケーションをビルドし、既存の検索インデックスにセマンティック ランク付けを追加します。
または、ソース コードをダウンロードして、完了したプロジェクトを開始することもできます。
環境を設定する
Visual Studio を起動し、コンソール アプリ用の新しいプロジェクトを作成します。
[ツール]>[NuGet パッケージ マネージャー] で、 [ソリューションの NuGet パッケージの管理] を選択します。
[参照] を選択します。
Azure.Search.Documents パッケージを検索し、最新の安定バージョンを選択します。
[インストール] を選択して、プロジェクトとソリューションにアセンブリを追加します。
検索クライアントを作成する
Program.cs で次の using
ディレクティブを追加します。
using Azure;
using Azure.Search.Documents;
using Azure.Search.Documents.Indexes;
using Azure.Search.Documents.Indexes.Models;
using Azure.Search.Documents.Models;
2 つのクライアントを作成します。SearchIndexClient はインデックスを作成するクライアントで、SearchClient は既存のインデックスを読み込んで照会するクライアントです。
どちらのクライアントにも、作成と削除の権限による認証のためのサービス エンドポイントと管理者 API キーが必要です。 ただし、コードによって URI が構築されるため、serviceName
プロパティには検索サービス名のみを指定します。 https://
または .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);
. . .
}
インデックスを作成する
SemanticConfiguration
を含むようにインデックス スキーマを作成または更新します。 既存のインデックスを更新する場合、ドキュメントの構造は変更されないため、この変更ではインデックスの再作成は必要ありません。
// 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("semantic-config", new()
{
TitleField = new SemanticField("HotelName"),
ContentFields =
{
new SemanticField("Description"),
},
KeywordsFields =
{
new SemanticField("Tags"),
new SemanticField("Category")
}
})
}
};
adminClient.CreateOrUpdateIndex(definition);
}
次のコードで、検索サービスにインデックスを作成します。
// Create index
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, adminClient);
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
ドキュメントを読み込む
Azure AI Search は、サービスに保存されているコンテンツを検索します。 ドキュメントをアップロードするコードは C# 用のフルテキスト検索のクイック スタートと同じであるため、ここで複製する必要はありません。 名前、住所、説明を含む 4 つのホテルが必要です。 ソリューションには、ホテルと住所の種類が必要です。
インデックスを検索する
パラメーターを指定するための検索オプションを使用してセマンティック ランカーを呼び出すクエリを次に示します。
Console.WriteLine("Example of a semantic query.");
options = new SearchOptions()
{
QueryType = Azure.Search.Documents.Models.SearchQueryType.Semantic,
SemanticSearch = new()
{
SemanticConfigurationName = "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>("restaurant on site", options);
WriteDocuments(response);
比較のために、用語の頻度と近接度に基づいて、既定の BM25 ランク付けを使用するクエリの結果を次に示します。 クエリ "restaurant on site" を指定すると、BM25 ランク付けアルゴリズムは、このスクリーンショットに示されている順序で一致を返します。ここで、"site" の一致はデータセット全体でまれであるため、より関連性が高いと見なされます。
これに対し、セマンティック ランク付けが同じクエリ ("restaurant on site") に適用されると、結果はクエリに対するセマンティックの関連性に基づいて再ランク付けされます。 今回の上位の結果は、ユーザーの期待により合致したレストランのあるホテルです。
プログラムを実行する
F5 キーを押して、アプリをリビルドし、プログラム全体を実行します。
出力には、Console.WriteLine からのメッセージに加え、クエリの情報と結果が表示されます。
セマンティック ランク付けについて学習するには、Azure SDK for Python で azure-search-documents ライブラリと Jupyter ノートブックを使用してください。
あるいは、完成したノートブックをダウンロードして実行することもできます。
環境を設定する
Visual Studio Code と Python 拡張機能 (または同等の IDE)、および Python 3.10 以降。
このクイックスタートでは仮想環境をお勧めします。
Visual Studio Code を起動します。
新しい .ipynb ファイルを作成します。
Ctrl + Shift + P キーを使用してコマンド パレットを開きます。
"Python: 環境の作成" を検索します。
Venv.
を選択
Python インタープリターを選択します。 3.10 以降を選択します。
短時間で設定されます。 問題が発生した場合は、「VS Code での Python 環境」を参照してください。
パッケージをインストールし、変数を設定する
azure-search-documents などのパッケージをインストールします。
! pip install azure-search-documents==11.6.0b1 --quiet
! pip install azure-identity --quiet
! pip install python-dotenv --quiet
エンドポイントと 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"
インデックスを作成する
SemanticConfiguration
を含むようにインデックス スキーマを作成または更新します。 既存のインデックスを更新する場合、ドキュメントの構造は変更されないため、この変更ではインデックスの再作成は必要ありません。
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="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="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])
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')
ドキュメント ペイロードを作成する
JSON ドキュメントを検索インデックスにプッシュできます。 ドキュメントはインデックス スキーマと一致する必要があります。
documents = [
{
"@search.action": "upload",
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"Description": "This classic hotel is fully-refurbished and ideally located on the main commercial artery of the city in the heart of New York. A few minutes away is Times 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.",
"Category": "Boutique",
"Tags": [ "view", "air conditioning", "concierge" ],
"ParkingIncluded": "false",
"LastRenovationDate": "2022-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. The hotel also regularly hosts events like wine tastings, beer dinners, and live music.",
"Category": "Boutique",
"Tags": [ "pool", "free wifi", "concierge" ],
"ParkingIncluded": "false",
"LastRenovationDate": "2019-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 Gastronomic Hotel stands out for its culinary excellence under the management of William Dough, who advises on and oversees all of the Hotel’s restaurant services.",
"Category": "Suite",
"Tags": [ "restaurant", "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 Cliff is part of a lovingly restored 19th century resort, updated for every modern convenience.",
"Category": "Boutique",
"Tags": [ "concierge", "view", "air conditioning" ],
"ParkingIncluded": "true",
"LastRenovationDate": "2020-02-06T00:00:00Z",
"Rating": 4.60,
"Address": {
"StreetAddress": "7400 San Pedro Ave",
"City": "San Antonio",
"StateProvince": "TX",
"PostalCode": "78216",
"Country": "USA"
}
}
]
インデックスにドキュメントをアップロードする
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)
最初のクエリを実行する
検証手順として空のクエリから開始し、インデックスが動作可能であることを証明します。 ホテル名と説明の順序付けられていないリストを取得し、項目数は 4 (インデックスに 4 つのドキュメントがあることを示す) である必要があります。
# 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']}")
テキスト クエリを実行する
比較する目的で、BM25 関連性スコアリングを使用してテキスト クエリを実行します。 クエリ文字列を指定すると、フルテキスト検索が呼び出されます。 応答はランク付けされた結果で構成され、一致する用語のインスタンスがより多いドキュメント、またはより重要な用語があるドキュメントに高いスコアが付与されます。
このクエリでは、サイトの説明が含まれているので、サイト上のレストランのためのこのクエリでは、サブライムパレスホテルが上に出てくる。 出現頻度の低い用語の場合、ドキュメントの検索スコアが上がります。
# Run a text query (returns a BM25-scored result set)
results = search_client.search(query_type='simple',
search_text="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']}")
セマンティック クエリを実行する
次に、セマンティック ランク付けを追加します。 新しいパラメータには、 query_type
と semantic_configuration_name
が含まれます。
これは同じクエリですが、セマンティック ランカーにより、最初のクエリを前提に、Gastronomic Landscape Hotel がより関連性の高い結果として正しく識別されていることに注目してください。 このクエリでは、モデルによって生成されたキャプションも返されます。 このサンプルでは入力が最小限であるため興味深いキャプションを作成できませんでしたが、この例では正常に構文がデモンストレーションされています。
# 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='semantic-config',
search_text="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")
セマンティック回答を返す
この最後のクエリでは、セマンティック回答を返します。
セマンティック ランカーでは、質問の特性を持つクエリ文字列に対する回答を生成できます。 生成された回答は、コンテンツから逐語的に抽出されます。 セマンティック回答を取得するには、質問と回答を厳密に揃え、モデルは質問に明確に答えるコンテンツを見つける必要があります。 候補の回答が信頼度のしきい値を満たしていない場合、モデルは回答を返しません。 この例ではデモ用に、質問が応答を取得するように設計されているため、構文を確認できます。
# Run a semantic query that returns semantic answers
results = search_client.search(query_type='semantic', semantic_configuration_name='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")