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;
두 개의 클라이언트를 만듭니다. 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("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);
}
다음 코드는 검색 서비스에 인덱스를 만듭니다.
// Create index
Console.WriteLine("{0}", "Creating index...\n");
CreateIndex(indexName, adminClient);
SearchClient ingesterClient = adminClient.GetSearchClient(indexName);
문서 로드
Azure AI 검색은 서비스에 저장된 콘텐츠를 검색합니다. 문서를 업로드하는 코드는 전체 텍스트 검색을 위한 C# 빠른 시작과 동일하므로 여기에서 중복할 필요가 없습니다. 이름, 주소 및 설명이 있는 4개의 호텔이 있어야 합니다. 솔루션에는 호텔 및 주소 유형이 있어야 합니다.
인덱스 검색
매개 변수를 지정하기 위한 검색 옵션을 사용하여 의미 순위매기기를 호출하는 쿼리는 다음과 같습니다.
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);
비교를 위해 용어 빈도 및 근접성을 기반으로 기본 BM25 순위를 사용하는 쿼리의 결과는 다음과 같습니다. BM25 순위 알고리즘은 "어떤 호텔 구내에 좋은 레스토랑이 있는가?" 쿼리를 감안할 때 다음 스크린샷에 표시된 순서대로 일치 항목을 반환합니다.
반면, 의미 체계 순위가 동일한 쿼리("어떤 호텔 구내에 좋은 레스토랑이 있는가?")에 적용되는 경우 쿼리에 대한 의미 체계 관련성에 따라 결과 순위가 다시 지정됩니다. 여기서 가장 좋은 결과는 레스토랑이 있는 호텔이며, 이는 사용자의 기대에 더욱 잘 부합합니다.
프로그램 실행
F5 키를 눌러서 애플리케이션을 다시 빌드하고 프로그램 전체를 실행합니다.
출력에는 쿼리 정보 및 결과가 추가된 Console.WriteLine의 메시지가 포함됩니다.
Python용 Azure SDK의 Jupyter Notebook 및 azure-search-documents 라이브러리를 사용하여 의미 체계 순위 지정에 대해 알아봅니다.
또는 완성된 전자 필기장을 다운로드하고 실행할 수 있습니다.
환경 설정
Python 3.10 이상에서 Python 확장이 포함된 Visual Studio Code 또는 동등한 IDE를 사용합니다.
이 빠른 시작에서는 가상 환경을 사용하는 것이 좋습니다.
Visual Studio Code 시작
새 ipynb 파일을 만듭니다.
Ctrl+Shift+P를 사용하여 명령 팔레트를 엽니다.
Python: 환경 만들기를 검색합니다.
Venv.
선택
Python 인터프리터를 선택합니다. 3.10 이상을 선택합니다.
설정하는 데 1분 정도 걸릴 수 있습니다. 문제가 발생하면 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="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')
문서 페이로드 만들기
JSON 문서를 검색 인덱스로 푸시할 수 있습니다. 문서는 인덱스 스키마와 일치해야 합니다.
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"
}
}
]
인덱스로 문서 업로드
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 관련성 점수 매기기를 사용하여 텍스트 쿼리를 실행합니다. 쿼리 문자열을 제공할 때 전체 텍스트 검색이 호출됩니다. 응답은 순위가 지정된 결과로 구성되며, 일치하는 용어 또는 더 중요한 용어의 인스턴스가 더 많은 문서에 더 높은 점수가 부여됩니다.
어떤 호텔에 좋은 레스토랑이 있는지에 대한 이 쿼리에서 Sublime Palace Hotel은 설명에 사이트가 포함되어 있기 때문에 맨 위에 나옵니다. 자주 발생하지 않는 용어는 문서의 검색 점수를 높입니다.
# 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']}")
의미 체계 쿼리 실행
이제 의미 체계 순위를 추가합니다. 새 매개 변수에는 query_type
및 semantic_configuration_name
이(가) 포함됩니다.
동일한 쿼리이지만 의미 체계 순위는 초기 쿼리를 고려할 때 미식 가로 호텔을 보다 관련성이 높은 결과로 올바르게 식별합니다. 이 쿼리는 모델에서 생성된 캡션도 반환합니다. 이 샘플에서는 입력이 너무 최소화되어 흥미로운 캡션을 만들 수 없지만 예제에서는 구문을 성공적으로 보여 줍니다.
# 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")
의미 체계 답변 반환
이 최종 쿼리에서 의미 체계 답변을 반환합니다.
의미 순위매기기는 질문의 특징이 있는 쿼리 문자열에 대한 답변을 생성할 수 있습니다. 생성된 답변은 콘텐츠의 축자에서 추출됩니다. 의미 체계 답변을 얻으려면 질문과 답변을 밀접하게 정렬해야 하며 모델은 질문에 명확하게 답변하는 콘텐츠를 찾아야 합니다. 잠재적인 답변이 신뢰도 임계값을 충족하지 못하면 모델은 답변을 반환하지 않습니다. 이 예제의 질문은 데모 목적으로 응답을 가져오도록 설계되었으므로 구문을 볼 수 있습니다.
# 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")