이 빠른 시작 가이드에서는 Azure AI Search에서 인덱싱된 콘텐츠를 통해 대화 기반 검색 환경을 위한 채팅 완성 모델에 쿼리를 보냅니다. Azure Portal에서 Azure OpenAI 및 Azure AI Search 리소스를 설정한 후 코드를 실행하여 API를 호출합니다.
필수 조건
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
Azure OpenAI 리소스입니다.
- 사용하려는 채팅 완료 모델(gpt-4o, gpt-4o-mini 또는 동등한 모델)을 지원하는 지역을 선택합니다.
- Azure AI Foundry에서 채팅 완료 모델을 배포하거나 다른 방법을 사용합니다.
-
- 기본 계층 이상을 사용하는 것이 좋습니다.
- 의미 체계 순위를 사용하도록 설정합니다.
.NET 9.0 이 설치되었습니다.
액세스 구성
검색 엔드포인트에 대한 요청은 인증 및 권한 부여를 받아야 합니다. 이 작업에 API 키 또는 역할을 사용할 수 있습니다. 키는 시작하기가 더 쉽지만 역할이 더 안전합니다. 이 빠른 시작에서는 역할을 가정합니다.
두 개의 클라이언트를 설정하므로 두 리소스 모두에 대한 권한이 필요합니다.
Azure AI 검색은 로컬 시스템에서 쿼리 요청을 수신합니다. 호텔 샘플 인덱스가 이미 있는 경우 검색 인덱스 데이터 판독기 역할 할당을 자신에게 할당합니다. 없는 경우 인덱스 만들기 및 쿼리를 수행할 수 있도록 Search Service 기여자 및 검색 인덱스 데이터 기여자 역할을 자신에게 할당합니다.
Azure OpenAI는 로컬 시스템에서 쿼리 및 검색 결과를 수신합니다. Azure OpenAI에서 Cognitive Services OpenAI 사용자 역할을 자신에게 할당합니다.
Azure Portal에 로그인합니다.
역할 기반 액세스에 대한 Azure AI 검색 구성:
Azure Portal에서 Azure AI 검색 서비스를 찾습니다.
왼쪽 메뉴에서 설정>키를 선택한 다음 역할 기반 액세스 제어 또는 둘 다를 선택합니다.
역할 할당:
왼쪽 메뉴에서 IAM(액세스 제어)을 선택합니다.
Azure AI Search에서 이러한 역할을 선택하여 검색 인덱스 만들기, 로드 및 쿼리를 수행하고 Microsoft Entra ID 사용자 ID에 할당합니다.
- 검색 인덱스 데이터 기여자
- Search 서비스 기여자
Azure OpenAI에서 IAM(액세스 제어)을 선택하여 Azure OpenAI에서 이 역할을 자신에게 할당합니다.
- Cognitive Services OpenAI 사용자
사용 권한이 적용되는 데 몇 분 정도 걸릴 수 있습니다.
인덱스 만들기
검색 인덱스가 채팅 모델에 대한 접지 데이터를 제공합니다. 몇 분 안에 만들 수 있고 모든 검색 서비스 계층에서 실행되는 hotels-sample-index를 권장합니다. 이 인덱스는 기본 제공 샘플 데이터를 사용하여 생성됩니다.
Azure Portal에서 검색 서비스를 찾습니다.
개요 홈페이지에서 데이터 가져오기를 선택하여 마법사를 시작합니다.
데이터에 연결 페이지의 드롭다운 목록에서 샘플을 선택합니다.
hotels-sample을 선택합니다.
나머지 페이지에서 다음을 선택하고 기본값을 적용합니다.
인덱스가 만들어지면 왼쪽 메뉴에서 Search 관리>인덱스를 선택하여 인덱스를 엽니다.
JSON 편집을 선택합니다.
인덱스의 끝으로 스크롤하여 인덱스에 추가할 수 있는 구문의 자리 표시자를 찾을 수 있습니다.
"analyzers": [], "tokenizers": [], "tokenFilters": [], "charFilters": [], "normalizers": [],
"normalizers" 뒤의 새 줄에서 다음 의미 체계 구성을 붙여넣습니다. 이 예제에서는 이 빠른 시작을 실행하는 데 중요한
"defaultConfiguration"
을 지정합니다."semantic":{ "defaultConfiguration":"semantic-config", "configurations":[ { "name":"semantic-config", "prioritizedFields":{ "titleField":{ "fieldName":"HotelName" }, "prioritizedContentFields":[ { "fieldName":"Description" } ], "prioritizedKeywordsFields":[ { "fieldName":"Category" }, { "fieldName":"Tags" } ] } } ] },
변경 내용을 저장합니다.
검색 탐색기에서 다음 쿼리를 실행하여 인덱스를 테스트합니다.
complimentary breakfast
.출력은 다음 예제와 비슷해야 합니다. 검색 엔진에서 직접 반환되는 결과는 의미 순위매기기를 사용하는 경우 의미 체계 순위 지정 점수 및 캡션과 검색 점수와 같은 메타데이터와 함께 필드 및 해당 문자값으로 구성됩니다. Select 문을 사용하여 HotelName, Description 및 Tags 필드만 반환했습니다.
{ "@odata.count": 18, "@search.answers": [], "value": [ { "@search.score": 2.2896252, "@search.rerankerScore": 2.506816864013672, "@search.captions": [ { "text": "Head Wind Resort. Suite. coffee in lobby\r\nfree wifi\r\nview. The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a **complimentary continental breakfast** in the lobby, and free Wi-Fi throughout the hotel..", "highlights": "" } ], "HotelName": "Head Wind Resort", "Description": "The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a complimentary continental breakfast in the lobby, and free Wi-Fi throughout the hotel.", "Tags": [ "coffee in lobby", "free wifi", "view" ] }, { "@search.score": 2.2158256, "@search.rerankerScore": 2.288334846496582, "@search.captions": [ { "text": "Swan Bird Lake Inn. Budget. continental breakfast\r\nfree wifi\r\n24-hour front desk service. We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins..", "highlights": "" } ], "HotelName": "Swan Bird Lake Inn", "Description": "We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins.", "Tags": [ "continental breakfast", "free wifi", "24-hour front desk service" ] }, { "@search.score": 0.92481667, "@search.rerankerScore": 2.221315860748291, "@search.captions": [ { "text": "White Mountain Lodge & Suites. Resort and Spa. continental breakfast\r\npool\r\nrestaurant. Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings..", "highlights": "" } ], "HotelName": "White Mountain Lodge & Suites", "Description": "Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings.", "Tags": [ "continental breakfast", "pool", "restaurant" ] }, . . . ]}
서비스 엔드포인트 가져오기
나머지 섹션에서는 Azure OpenAI 및 Azure AI 검색에 대한 API 호출을 설정합니다. 서비스 엔드포인트를 가져와서 코드에서 변수로 제공할 수 있습니다.
Azure Portal에 로그인합니다.
개요 홈페이지에서 URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.search.windows.net
개요 홈페이지에서 엔드포인트를 볼 링크를 선택합니다. URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.openai.azure.com/
Azure에 로그인
연결에 Microsoft Entra ID 및 역할 할당을 사용하고 있습니다. Azure AI Search 및 Azure OpenAI와 동일한 테넌트 및 구독에 로그인했는지 확인합니다. 명령줄에서 Azure CLI를 사용하여 현재 속성을 표시하고, 속성을 변경하고, 로그인할 수 있습니다. 자세한 내용은 키 없이 연결을 참조 하세요.
다음 각 명령을 순서대로 실행합니다.
az account show
az account set --subscription <PUT YOUR SUBSCRIPTION ID HERE>
az login --tenant <PUT YOUR TENANT ID HERE>
이제 로컬 디바이스에서 Azure에 로그인해야 합니다.
.NET 앱 설정
앞으로의 단계를 따르려면 GitHub에서 완료된 샘플 앱을 복제하거나 직접 앱을 만들 수 있습니다.
샘플 앱 복제
이 문서의 완료된 샘플 앱에 액세스하려면 다음을 수행합니다.
GitHub에서 azure-search-dotnet-samples 리포지토리를 복제합니다.
git clone https://github.com/Azure-Samples/azure-search-dotnet-samples
quickstart-rag
폴더로 들어가세요.Visual Studio Code에서 폴더를
quickstart-rag
열거나 Visual Studio를 사용하여 솔루션 파일을 엽니다.
샘플 앱 만들기
다음 단계를 완료하여 AI 모델에 연결할 .NET 콘솔 앱을 만듭니다.
컴퓨터의 빈 디렉터리에서
dotnet new
명령을 사용하여 새 콘솔 앱을 만듭니다.dotnet new console -o AISearchRag
디렉터리를 앱 폴더로 변경합니다.
cd AISearchRag
필요한 패키지를 설치합니다.
dotnet add package Azure.AI.OpenAI dotnet add package Azure.Identity dotnet add package Azure.Search.Documents
Visual Studio Code(또는 선택한 편집기)에서 앱을 엽니다.
code .
쿼리 및 채팅 스레드 설정
다음 예제에서는 Azure AI Search를 사용하여 최소한의 RAG 시나리오를 설정하여 OpenAI 모델에 컨텍스트 리소스를 제공하여 생성된 응답을 개선하는 방법을 보여 줍니다.
minimal-query
샘플 리포지토리의 프로젝트에서 파일을 열어Program.cs
첫 번째 예제를 봅니다. 프로젝트를 직접 만든 경우 다음 코드를 추가하여 Azure AI Search 및 Azure OpenAI 서비스에 연결하고 쿼리합니다.비고
Azure OpenAI 엔드포인트 및 모델 이름뿐만 아니라 Azure AI Search 엔드포인트 및 인덱스 이름에 대한 자리 표시자를 바꿔야 합니다.
using System; using System.Collections.Generic; using System.Threading.Tasks; using Azure.Identity; using Azure.Search.Documents; using Azure.Search.Documents.Models; using Azure.AI.OpenAI; using OpenAI.Chat; using System.Text.Json; using Microsoft.Extensions.Logging; // Azure resource endpoints and deployment info string azureSearchServiceEndpoint = "azure-ai-search-endpoint"; string azureOpenAIEndpoint = "azure-ai-openai-endpoint"; string azureDeploymentModel = "azure-ai-deployment-name"; string indexName = "hotels-sample-index"; // Set up Azure credentials and clients var credential = new DefaultAzureCredential(); var searchClient = new SearchClient(new Uri(azureSearchServiceEndpoint), indexName, credential); var openAIClient = new AzureOpenAIClient(new Uri(azureOpenAIEndpoint), credential); // Prompt template for grounding the LLM response in search results string GROUNDED_PROMPT = @"You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {0} Sources: {1}"; // The user's query string query = "Can you recommend a few hotels with complimentary breakfast?"; // Configure search options: top 5 results, select relevant fields var options = new SearchOptions { Size = 5 }; options.Select.Add("Description"); options.Select.Add("HotelName"); options.Select.Add("Tags"); // Execute the search var searchResults = await searchClient.SearchAsync<SearchDocument>(query, options); var sources = new List<string>(); await foreach (var result in searchResults.Value.GetResultsAsync()) { var doc = result.Document; // Format each result as: HotelName:Description:Tags sources.Add($"{doc["HotelName"]}:{doc["Description"]}:{doc["Tags"]}"); } string sourcesFormatted = string.Join("\n", sources); // Format the prompt with the query and sources string formattedPrompt = string.Format(GROUNDED_PROMPT, query, sourcesFormatted); // Create a chat client for the specified deployment/model ChatClient chatClient = openAIClient.GetChatClient(azureDeploymentModel); // Send the prompt to the LLM and stream the response var chatUpdates = chatClient.CompleteChatStreamingAsync( [ new UserChatMessage(formattedPrompt) ] ); // Print the streaming response to the console await foreach (var chatUpdate in chatUpdates) { if (chatUpdate.Role.HasValue) { Console.Write($"{chatUpdate.Role} : "); } foreach (var contentPart in chatUpdate.ContentUpdate) { Console.Write(contentPart.Text); } }
위의 코드는 다음을 수행합니다.
- 무료 아침 식사, 호텔 이름, 설명 및 태그 검색에 대한 사용자 쿼리와 일치하는 호텔을 Azure Search 인덱스를 검색합니다.
- 검색 결과를 구조화된 목록으로 서식을 지정하여 생성 AI 모델의 컨텍스트 소스 역할을 합니다.
- 제공된 원본만 사용하여 응답하도록 Azure OpenAI 모델에 지시하는 프롬프트를 생성합니다.
- AI 모델에 프롬프트를 보내고 생성된 응답을 스트리밍합니다.
- 콘솔에 AI의 응답을 출력하여 스트리밍할 때 역할과 콘텐츠를 모두 표시합니다.
프로젝트를 실행하여 기본 RAG 시나리오를 시작합니다. Azure OpenAI의 출력은 다음 예제와 같은 여러 호텔에 대한 권장 사항으로 구성됩니다.
Sure! Here are a few hotels that offer complimentary breakfast: - **Head Wind Resort** - Complimentary continental breakfast in the lobby - Free Wi-Fi throughout the hotel - **Double Sanctuary Resort** - Continental breakfast included - **White Mountain Lodge & Suites** - Continental breakfast available - **Swan Bird Lake Inn** - Continental-style breakfast each morning with a variety of food and drinks such as caramel cinnamon rolls, coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins
더 자세히 실험하려면 쿼리를 변경하고 마지막 단계를 다시 실행하여 모델이 접지 데이터로 작동하는 방식을 더 잘 이해합니다. 프롬프트를 수정하여 출력의 톤이나 구조를 변경할 수도 있습니다.
문제 해결
테스트하는 동안 다음 오류가 발생할 수 있습니다.
- 사용할 수 없음: Azure AI Search 구성을 확인하여 역할 기반 액세스가 사용하도록 설정되어 있는지 확인합니다.
- 권한 부여 실패: 몇 분 정도 기다렸다가 다시 시도하세요. 역할 할당이 작동하려면 몇 분 정도 걸릴 수 있습니다.
- 리소스를 찾을 수 없음: 리소스 URI를 확인하고 채팅 모델의 API 버전이 유효한지 확인합니다.
복잡한 RAG 쿼리 보내기
Azure AI Search는 중첩된 JSON 구조에 대한 복합 형식 을 지원합니다. hotels-sample-index에서 , Address
, Address.StreetAddress
Address.City
및 Address.StateProvince
로 구성된 복합 형식의 Address.PostalCode
Address.Country
예입니다. 인덱스는 각 호텔에 대한 복잡한 컬렉션 Rooms
도 가지고 있습니다. 인덱스 형식이 복잡한 경우 검색 결과 출력을 JSON으로 변환한 다음 JSON을 채팅 모델에 전달하는 경우 쿼리에서 해당 필드를 제공할 수 있습니다.
complex-query
샘플 리포지토리 프로젝트의Program.cs
파일을 엽니다. 프로젝트를 직접 만든 경우 코드를 다음으로 바꿉다.using System; using System.Collections.Generic; using System.Threading.Tasks; using Azure.Identity; using Azure.Search.Documents; using Azure.Search.Documents.Models; using Azure.AI.OpenAI; using OpenAI.Chat; using System.Text.Json; using Microsoft.Extensions.Logging; // Azure resource endpoints and deployment info string azureSearchServiceEndpoint = "azure-ai-search-endpoint"; string azureOpenAIEndpoint = "azure-ai-openai-endpoint"; string azureDeploymentModel = "azure-ai-deployment-name"; string indexName = "hotels-sample-index"; // Set up Azure credentials and clients var credential = new DefaultAzureCredential(); var searchClient = new SearchClient(new Uri(azureSearchServiceEndpoint), indexName, credential); var openAIClient = new AzureOpenAIClient(new Uri(azureOpenAIEndpoint), credential); // Prompt template for the OpenAI model string groundedPrompt = @"You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner. Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {0} Sources: {1}"; // The user query and fields to select from search var query = "Can you recommend a few hotels that offer complimentary breakfast? Tell me their description, address, tags, and the rate for one room that sleeps 4 people."; var selectedFields = new[] { "HotelName", "Description", "Address", "Rooms", "Tags" }; // Configure search options var options = new SearchOptions { Size = 5 }; foreach (var field in selectedFields) { options.Select.Add(field); } // Run Azure Cognitive Search var searchResults = await searchClient.SearchAsync<SearchDocument>(query, options); // Filter and format search results var sourcesFiltered = new List<Dictionary<string, object>>(); await foreach (var result in searchResults.Value.GetResultsAsync()) { sourcesFiltered.Add( selectedFields .Where(f => result.Document.TryGetValue(f, out _)) .ToDictionary(f => f, f => result.Document[f]) ); } var sourcesFormatted = string.Join("\n", sourcesFiltered.ConvertAll(source => JsonSerializer.Serialize(source))); // Format the prompt for OpenAI string formattedPrompt = string.Format(groundedPrompt, query, sourcesFormatted); // Get a chat client for the OpenAI deployment ChatClient chatClient = openAIClient.GetChatClient(azureDeploymentModel); // Send the prompt to Azure OpenAI and stream the response var chatUpdates = chatClient.CompleteChatStreamingAsync( new[] { new UserChatMessage(formattedPrompt) } ); // Output the streamed chat response await foreach (var chatUpdate in chatUpdates) { if (chatUpdate.Role.HasValue) { Console.Write($"{chatUpdate.Role} : "); } foreach (var contentPart in chatUpdate.ContentUpdate) { Console.Write(contentPart.Text); } }
프로젝트를 실행하여 기본 RAG 시나리오를 시작합니다. Azure OpenAI의 출력은 다음 예제와 같은 여러 호텔에 대한 권장 사항으로 구성됩니다.
1. **Double Sanctuary Resort** - **Description**: 5-star luxury hotel with the biggest rooms in the city. Recognized as the #1 hotel in the area by Traveler magazine. Features include free WiFi, flexible check-in/out, a fitness center, and in-room espresso. - **Address**: 2211 Elliott Ave, Seattle, WA, 98121, USA - **Tags**: view, pool, restaurant, bar, continental breakfast - **Room Rate for 4 People**: - Suite, 2 Queen Beds: $254.99 per night 2. **Starlight Suites** - **Description**: Spacious all-suite hotel with complimentary airport shuttle and WiFi. Facilities include an indoor/outdoor pool, fitness center, and Florida Green certification. Complimentary coffee and HDTV are also available. - **Address**: 19575 Biscayne Blvd, Aventura, FL, 33180, USA - **Tags**: pool, coffee in lobby, free wifi - **Room Rate for 4 People**: - Suite, 2 Queen Beds (Cityside): $231.99 per night - Deluxe Room, 2 Queen Beds (Waterfront View): $148.99 per night 3. **Good Business Hotel** - **Description**: Located one mile from the airport with free WiFi, an outdoor pool, and a complimentary airport shuttle. Close proximity to Lake Lanier and downtown. The business center includes printers, a copy machine, fax, and a work area. - **Address**: 4400 Ashford Dunwoody Rd NE, Atlanta, GA, 30346, USA - **Tags**: pool, continental breakfast, free parking - **Room Rate for 4 People**: - Budget Room, 2 Queen Beds (Amenities): $60.99 per night - Deluxe Room, 2 Queen Beds (Amenities): $139.99 per night
문제 해결
디버깅하는 동안 토큰 획득 실패와 관련된 ManagedIdentityCredential
출력 메시지가 표시되면 테넌트가 여러 개 있고 Azure 로그인에서 검색 서비스가 없는 테넌트를 사용하는 것일 수 있습니다. 테넌트 ID를 가져오려면 Azure Portal에서 "테넌트 속성"을 검색하거나 az login tenant list
을(를) 실행합니다.
테넌트 ID가 있으면 명령 프롬프트에서 az login --tenant <YOUR-TENANT-ID>
을(를) 실행한 다음 스크립트를 다시 실행합니다.
다음 인스턴스 ILogger
를 만들어 코드에 오류를 기록할 수도 있습니다.
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
정리
본인 소유의 구독으로 이 모듈을 진행하고 있는 경우에는 프로젝트가 끝날 때 여기에서 만든 리소스가 계속 필요한지 확인하는 것이 좋습니다. 계속 실행되는 리소스에는 요금이 부과될 수 있습니다. 리소스를 개별적으로 삭제하거나 리소스 그룹을 삭제하여 전체 리소스 세트를 삭제할 수 있습니다.
맨 왼쪽 창의 모든 리소스 또는 리소스 그룹 링크를 사용하여 Azure Portal에서 리소스를 찾고 관리할 수 있습니다.
필수 조건
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
Azure OpenAI 리소스입니다.
- 사용하려는 채팅 완료 모델(gpt-4o, gpt-4o-mini 또는 동등한 모델)을 지원하는 지역을 선택합니다.
- Azure AI Foundry에서 채팅 완료 모델을 배포하거나 다른 방법을 사용합니다.
-
- 기본 계층 이상을 사용하는 것이 좋습니다.
- 의미 체계 순위를 사용하도록 설정합니다.
액세스 구성
검색 엔드포인트에 대한 요청은 인증 및 권한 부여를 받아야 합니다. 이 작업에 API 키 또는 역할을 사용할 수 있습니다. 키는 시작하기가 더 쉽지만 역할이 더 안전합니다. 이 빠른 시작에서는 역할을 가정합니다.
두 개의 클라이언트를 설정하므로 두 리소스 모두에 대한 권한이 필요합니다.
Azure AI 검색은 로컬 시스템에서 쿼리 요청을 수신합니다. 호텔 샘플 인덱스가 이미 있는 경우 검색 인덱스 데이터 판독기 역할 할당을 자신에게 할당합니다. 없는 경우 인덱스 만들기 및 쿼리를 수행할 수 있도록 Search Service 기여자 및 검색 인덱스 데이터 기여자 역할을 자신에게 할당합니다.
Azure OpenAI는 로컬 시스템에서 쿼리 및 검색 결과를 수신합니다. Azure OpenAI에서 Cognitive Services OpenAI 사용자 역할을 자신에게 할당합니다.
Azure Portal에 로그인합니다.
역할 기반 액세스에 대한 Azure AI 검색 구성:
Azure Portal에서 Azure AI 검색 서비스를 찾습니다.
왼쪽 메뉴에서 설정>키를 선택한 다음 역할 기반 액세스 제어 또는 둘 다를 선택합니다.
역할 할당:
왼쪽 메뉴에서 IAM(액세스 제어)을 선택합니다.
Azure AI Search에서 이러한 역할을 선택하여 검색 인덱스 만들기, 로드 및 쿼리를 수행하고 Microsoft Entra ID 사용자 ID에 할당합니다.
- 검색 인덱스 데이터 기여자
- Search 서비스 기여자
Azure OpenAI에서 IAM(액세스 제어)을 선택하여 Azure OpenAI에서 이 역할을 자신에게 할당합니다.
- Cognitive Services OpenAI 사용자
사용 권한이 적용되는 데 몇 분 정도 걸릴 수 있습니다.
인덱스 만들기
검색 인덱스가 채팅 모델에 대한 접지 데이터를 제공합니다. 몇 분 안에 만들 수 있고 모든 검색 서비스 계층에서 실행되는 hotels-sample-index를 권장합니다. 이 인덱스는 기본 제공 샘플 데이터를 사용하여 생성됩니다.
Azure Portal에서 검색 서비스를 찾습니다.
개요 홈페이지에서 데이터 가져오기를 선택하여 마법사를 시작합니다.
데이터에 연결 페이지의 드롭다운 목록에서 샘플을 선택합니다.
hotels-sample을 선택합니다.
나머지 페이지에서 다음을 선택하고 기본값을 적용합니다.
인덱스가 만들어지면 왼쪽 메뉴에서 Search 관리>인덱스를 선택하여 인덱스를 엽니다.
JSON 편집을 선택합니다.
인덱스의 끝으로 스크롤하여 인덱스에 추가할 수 있는 구문의 자리 표시자를 찾을 수 있습니다.
"analyzers": [], "tokenizers": [], "tokenFilters": [], "charFilters": [], "normalizers": [],
"normalizers" 뒤의 새 줄에서 다음 의미 체계 구성을 붙여넣습니다. 이 예제에서는 이 빠른 시작을 실행하는 데 중요한
"defaultConfiguration"
을 지정합니다."semantic":{ "defaultConfiguration":"semantic-config", "configurations":[ { "name":"semantic-config", "prioritizedFields":{ "titleField":{ "fieldName":"HotelName" }, "prioritizedContentFields":[ { "fieldName":"Description" } ], "prioritizedKeywordsFields":[ { "fieldName":"Category" }, { "fieldName":"Tags" } ] } } ] },
변경 내용을 저장합니다.
검색 탐색기에서 다음 쿼리를 실행하여 인덱스를 테스트합니다.
complimentary breakfast
.출력은 다음 예제와 비슷해야 합니다. 검색 엔진에서 직접 반환되는 결과는 의미 순위매기기를 사용하는 경우 의미 체계 순위 지정 점수 및 캡션과 검색 점수와 같은 메타데이터와 함께 필드 및 해당 문자값으로 구성됩니다. Select 문을 사용하여 HotelName, Description 및 Tags 필드만 반환했습니다.
{ "@odata.count": 18, "@search.answers": [], "value": [ { "@search.score": 2.2896252, "@search.rerankerScore": 2.506816864013672, "@search.captions": [ { "text": "Head Wind Resort. Suite. coffee in lobby\r\nfree wifi\r\nview. The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a **complimentary continental breakfast** in the lobby, and free Wi-Fi throughout the hotel..", "highlights": "" } ], "HotelName": "Head Wind Resort", "Description": "The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a complimentary continental breakfast in the lobby, and free Wi-Fi throughout the hotel.", "Tags": [ "coffee in lobby", "free wifi", "view" ] }, { "@search.score": 2.2158256, "@search.rerankerScore": 2.288334846496582, "@search.captions": [ { "text": "Swan Bird Lake Inn. Budget. continental breakfast\r\nfree wifi\r\n24-hour front desk service. We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins..", "highlights": "" } ], "HotelName": "Swan Bird Lake Inn", "Description": "We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins.", "Tags": [ "continental breakfast", "free wifi", "24-hour front desk service" ] }, { "@search.score": 0.92481667, "@search.rerankerScore": 2.221315860748291, "@search.captions": [ { "text": "White Mountain Lodge & Suites. Resort and Spa. continental breakfast\r\npool\r\nrestaurant. Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings..", "highlights": "" } ], "HotelName": "White Mountain Lodge & Suites", "Description": "Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings.", "Tags": [ "continental breakfast", "pool", "restaurant" ] }, . . . ]}
서비스 엔드포인트 가져오기
나머지 섹션에서는 Azure OpenAI 및 Azure AI 검색에 대한 API 호출을 설정합니다. 서비스 엔드포인트를 가져와서 코드에서 변수로 제공할 수 있습니다.
Azure Portal에 로그인합니다.
개요 홈페이지에서 URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.search.windows.net
개요 홈페이지에서 엔드포인트를 볼 링크를 선택합니다. URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.openai.azure.com/
로컬 개발을 위한 환경 변수 설정
.env
파일을 만듭니다.다음 환경 변수를
.env
파일에 추가하여 값을 사용자 고유의 서비스 엔드포인트 및 키로 바꿉니다.AZURE_SEARCH_ENDPOINT=<YOUR AZURE AI SEARCH ENDPOINT> AZURE_SEARCH_INDEX_NAME=hotels-sample-index AZURE_OPENAI_ENDPOINT=<YOUR AZURE OPENAI ENDPOINT> AZURE_OPENAI_VERSION=<YOUR AZURE OPENAI API VERSION> AZURE_DEPLOYMENT_MODEL=<YOUR DEPLOYMENT NAME>
Node.JS 프로젝트 설정
Visual Studio Code 및 TypeScript를 사용하여 프로젝트를 설정합니다.
새 디렉터리에서 Visual Studio Code를 시작합니다.
mkdir rag-quickstart && cd rag-quickstart code .
프로젝트 디렉터리에서 ESM 모듈에 대한 새 패키지를 만듭니다.
npm init -y npm pkg set type=module
그러면 기본값이 있는
package.json
파일이 만들어집니다.다음 npm 패키지를 설치합니다.
npm install @azure/identity @azure/search-documents openai dotenv
src
프로젝트 디렉터리에 디렉터리를 만듭니다.mkdir src
Azure에 로그인
연결에 Microsoft Entra ID 및 역할 할당을 사용하고 있습니다. Azure AI Search 및 Azure OpenAI와 동일한 테넌트 및 구독에 로그인했는지 확인합니다. 명령줄에서 Azure CLI를 사용하여 현재 속성을 표시하고, 속성을 변경하고, 로그인할 수 있습니다. 자세한 내용은 키 없이 연결을 참조 하세요.
다음 각 명령을 순서대로 실행합니다.
az account show
az account set --subscription <PUT YOUR SUBSCRIPTION ID HERE>
az login --tenant <PUT YOUR TENANT ID HERE>
이제 로컬 디바이스에서 Azure에 로그인해야 합니다.
쿼리 및 채팅 스레드 설정
Azure AI Search 인덱스 및 채팅 모델을 사용하여 접지 데이터를 기반으로 응답을 생성하는 쿼리 스크립트를 만듭니다. 다음 단계에서는 쿼리 스크립트를 설정하는 방법에 대해 설명합니다.
다음 코드를 사용하여
query.js
디렉터리에src
파일을 만듭니다.// This is a RAG (Retrieval Augmented Generation) implementation that: // 1. Takes a user query about hotels // 2. Searches a hotel database using Azure AI Search // 3. Formats the search results for the LLM // 4. Sends the query and formatted results to Azure OpenAI // 5. Returns a grounded response based only on the retrieved information import { SearchClient } from "@azure/search-documents"; import { AzureOpenAI } from "openai"; import { DefaultAzureCredential, getBearerTokenProvider } from "@azure/identity"; function getClients() { const credential = new DefaultAzureCredential(); // Search const azureSearchEndpoint = process.env.AZURE_SEARCH_ENDPOINT; const azureSearchIndexName = process.env.AZURE_SEARCH_INDEX_NAME; const searchClient = new SearchClient( azureSearchEndpoint, azureSearchIndexName, credential ); // OpenAI const azureOpenAiEndpoint = process.env.AZURE_OPENAI_ENDPOINT; const azureOpenAiApiVersion = process.env.AZURE_OPENAI_VERSION; const azureOpenAiDeploymentName = process.env.AZURE_DEPLOYMENT_MODEL; const scope = "https://cognitiveservices.azure.com/.default"; const azureADTokenProvider = getBearerTokenProvider(credential, scope); const options = { azureADTokenProvider, deployment: azureOpenAiDeploymentName, apiVersion: azureOpenAiApiVersion, endpoint: azureOpenAiEndpoint } const openaiClient = new AzureOpenAI(options); return { openaiClient, searchClient, modelName: azureOpenAiDeploymentName }; } async function queryAISearchForSources(searchClient, query) { console.log(`Searching for: "${query}"\n`); const searchResults = await searchClient.search(query, { top: 5, select: ["Description", "HotelName", "Tags"] }); const sources = []; for await (const result of searchResults.results) { const doc = result.document; sources.push( `Hotel: ${doc.HotelName}\n` + `Description: ${doc.Description}\n` + `Tags: ${Array.isArray(doc.Tags) ? doc.Tags.join(', ') : doc.Tags}\n` ); } const sourcesFormatted = sources.join("\n---\n"); return sourcesFormatted; } async function queryOpenAIForResponse(openaiClient, query, sourcesFormatted, modelName) { const GROUNDED_PROMPT = ` You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner. Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {query} Sources: {sources} `; return openaiClient.chat.completions.create({ model: modelName, messages: [ { role: "user", content: GROUNDED_PROMPT.replace("{query}", query).replace("{sources}", sourcesFormatted), } ], temperature: 0.7, max_tokens: 800, }); } async function main() { const { openaiClient, searchClient, modelName } = getClients(); const query = "Can you recommend a few hotels with complimentary breakfast?"; const sources = await queryAISearchForSources(searchClient, query); const response = await queryOpenAIForResponse(openaiClient, query, sources, modelName); // Print the response from the chat model const content = response.choices[0].message.content; if (content) { console.log(content); } else { console.log("No content available in the response."); } } main().catch((error) => { console.error("An error occurred:", error); process.exit(1); });
위의 코드는 다음을 수행합니다.
- Azure AI Search 및 Azure OpenAI에 필요한 라이브러리를 가져옵니다.
- 환경 변수를 사용하여 Azure AI Search 및 Azure OpenAI 클라이언트를 구성합니다.
- 구성에 환경 변수를 사용하여 Azure AI Search 및 Azure OpenAI에 대한 클라이언트를 가져오는 함수를 정의합니다.
- 사용자 쿼리를 기반으로 원본에 대한 Azure AI Search를 쿼리하는 함수를 정의합니다.
- 사용자 쿼리 및 Azure AI Search에서 검색된 원본에 따라 응답을 위해 Azure OpenAI를 쿼리하는 함수를 정의합니다.
- 함수는
main
검색 및 OpenAI 함수를 호출하여 흐름을 오케스트레이션한 다음 응답을 출력합니다.
터미널에서 다음 명령을 실행하여 쿼리 스크립트를 실행합니다.
node -r dotenv/config query.js
.env
는-r dotenv/config
을 사용하여 런타임에 전달됩니다.여러 호텔에 대한 권장 사항으로 구성된 출력을 봅니다. 다음은 출력의 모양에 대한 예입니다.
Sure! Here are a few hotels that offer complimentary breakfast: - **Head Wind Resort** - Complimentary continental breakfast in the lobby - Free Wi-Fi throughout the hotel - **Double Sanctuary Resort** - Continental breakfast included - **White Mountain Lodge & Suites** - Continental breakfast available - **Swan Bird Lake Inn** - Continental-style breakfast each morning with a variety of food and drinks such as caramel cinnamon rolls, coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins
문제 해결
사용할 수 없음 오류 메시지가 표시되면 Azure AI 검색 구성을 확인하여 역할 기반 액세스가 사용하도록 설정되어 있는지 확인합니다.
권한 부여 실패 오류 메시지가 표시되면 몇 분 정도 기다렸다가 다시 시도하세요. 역할 할당이 작동하려면 몇 분 정도 걸릴 수 있습니다.
리소스를 찾을 수 없는 오류 메시지가 표시되면 리소스 URI를 확인하고 채팅 모델의 API 버전이 유효한지 확인합니다.
그렇지 않은 경우 더 실험하려면 쿼리를 변경하고 마지막 단계를 다시 실행하여 모델이 기초 데이터를 사용하여 작동하는 방식을 더 잘 이해합니다.
프롬프트를 수정하여 출력의 톤이나 구조를 변경할 수도 있습니다.
쿼리 매개 변수 단계에서 use_semantic_reranker=False
를 설정하여 의미론적 순위 지정 없이 쿼리를 시도할 수도 있습니다. 의미론적 순위 지정은 쿼리 결과의 관련성과 LLM이 유용한 정보를 반환하는 기능을 눈에 띄게 개선할 수 있습니다. 실험을 통해 콘텐츠에 변화를 가져올지 여부를 결정할 수 있습니다.
복잡한 RAG 쿼리 보내기
Azure AI Search는 중첩된 JSON 구조에 대한 복합 형식 을 지원합니다. hotels-sample-index에서 , Address
, Address.StreetAddress
Address.City
및 Address.StateProvince
로 구성된 복합 형식의 Address.PostalCode
Address.Country
예입니다. 인덱스는 각 호텔에 대한 복잡한 컬렉션 Rooms
도 가지고 있습니다.
인덱스에 복합 형식이 있는 경우 서식 지정 지침을 포함하도록 프롬프트를 변경합니다.
Can you recommend a few hotels that offer complimentary breakfast?
Tell me their description, address, tags, and the rate for one room that sleeps 4 people.
새 파일을
queryComplex.js
만듭니다.파일에 다음 코드를 복사합니다.
// This is a RAG (Retrieval Augmented Generation) implementation that: // 1. Takes a user query about hotels // 2. Searches a hotel database using Azure AI Search // 3. Formats the search results for the LLM // 4. Sends the query and formatted results to Azure OpenAI // 5. Returns a grounded response based only on the retrieved information import { SearchClient } from "@azure/search-documents"; import { AzureOpenAI } from "openai"; import { DefaultAzureCredential, getBearerTokenProvider } from "@azure/identity"; function getClients() { const credential = new DefaultAzureCredential(); // Search const azureSearchEndpoint = process.env.AZURE_SEARCH_ENDPOINT; const azureSearchIndexName = process.env.AZURE_SEARCH_INDEX_NAME; const searchClient = new SearchClient( azureSearchEndpoint, azureSearchIndexName, credential ); // OpenAI const azureOpenAiEndpoint = process.env.AZURE_OPENAI_ENDPOINT; const azureOpenAiApiVersion = process.env.AZURE_OPENAI_VERSION; const azureOpenAiDeploymentName = process.env.AZURE_DEPLOYMENT_MODEL; const scope = "https://cognitiveservices.azure.com/.default"; const azureADTokenProvider = getBearerTokenProvider(credential, scope); const options = { azureADTokenProvider, deployment: azureOpenAiDeploymentName, apiVersion: azureOpenAiApiVersion, endpoint: azureOpenAiEndpoint } const openaiClient = new AzureOpenAI(options); return { openaiClient, searchClient, modelName: azureOpenAiDeploymentName }; } async function queryAISearchForSources( searchClient, query ) { console.log(`Searching for: "${query}"\n`); const selectedFields = ["HotelName", "Description", "Address", "Rooms", "Tags"]; const searchResults = await searchClient.search(query, { top: 5, select: selectedFields, queryType: "semantic", semanticSearchOptions: {}, }); return searchResults; } async function queryOpenAIForResponse( openaiClient, query, sourcesFormatted, modelName ){ const GROUNDED_PROMPT = ` You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner. Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {query} Sources: {sources} `; return openaiClient.chat.completions.create({ model: modelName, messages: [ { role: "user", content: GROUNDED_PROMPT.replace("{query}", query).replace("{sources}", sourcesFormatted), } ], temperature: 0.7, max_tokens: 800, }); } async function main() { const { openaiClient, searchClient, modelName } = getClients(); const query = ` Can you recommend a few hotels that offer complimentary breakfast? Tell me their description, address, tags, and the rate for one room that sleeps 4 people. `; const sourcesResult = await queryAISearchForSources(searchClient, query); let sourcesFormatted = ""; for await (const result of sourcesResult.results) { // Explicitly typing result to ensure compatibility sourcesFormatted += JSON.stringify(result.document) + "\n"; } const response = await queryOpenAIForResponse(openaiClient, query, sourcesFormatted.trim(), modelName); // Print the response from the chat model const content = response.choices[0].message.content; if (content) { console.log(content); } else { console.log("No content available in the response."); } } main().catch((error) => { console.error("An error occurred:", error); process.exit(1); });
터미널에서 다음 명령을 실행하여 쿼리 스크립트를 실행합니다.
node -r dotenv/config queryComplex.js
.env
는-r dotenv/config
을 사용하여 런타임에 전달됩니다.Azure OpenAI의 출력을 보고 복잡한 형식의 콘텐츠를 추가합니다.
Here are a few hotels that offer complimentary breakfast and have rooms that sleep 4 people: 1. **Head Wind Resort** - **Description:** The best of old town hospitality combined with views of the river and cool breezes off the prairie. Enjoy a complimentary continental breakfast in the lobby, and free Wi-Fi throughout the hotel. - **Address:** 7633 E 63rd Pl, Tulsa, OK 74133, USA - **Tags:** Coffee in lobby, free Wi-Fi, view - **Room for 4:** Suite, 2 Queen Beds (Amenities) - $254.99 2. **Double Sanctuary Resort** - **Description:** 5-star Luxury Hotel - Biggest Rooms in the city. #1 Hotel in the area listed by Traveler magazine. Free WiFi, Flexible check in/out, Fitness Center & espresso in room. Offers continental breakfast. - **Address:** 2211 Elliott Ave, Seattle, WA 98121, USA - **Tags:** View, pool, restaurant, bar, continental breakfast - **Room for 4:** Suite, 2 Queen Beds (Amenities) - $254.99 3. **Swan Bird Lake Inn** - **Description:** Continental-style breakfast featuring a variety of food and drinks. Locally made caramel cinnamon rolls are a favorite. - **Address:** 1 Memorial Dr, Cambridge, MA 02142, USA - **Tags:** Continental breakfast, free Wi-Fi, 24-hour front desk service - **Room for 4:** Budget Room, 2 Queen Beds (City View) - $85.99 4. **Gastronomic Landscape Hotel** - **Description:** Known for its culinary excellence under the management of William Dough, offers continental breakfast. - **Address:** 3393 Peachtree Rd, Atlanta, GA 30326, USA - **Tags:** Restaurant, bar, continental breakfast - **Room for 4:** Budget Room, 2 Queen Beds (Amenities) - $66.99 ... - **Tags:** Pool, continental breakfast, free parking - **Room for 4:** Budget Room, 2 Queen Beds (Amenities) - $60.99 Enjoy your stay! Let me know if you need any more information.
오류 문제 해결
Azure SDK 오류를 디버그하려면 환경 변수 AZURE_LOG_LEVEL
를 다음 verbose
info
warning
error
중 하나로 설정합니다. 이렇게 하면 인증, 네트워크 연결 또는 기타 문제와 관련된 문제를 식별하는 데 도움이 되는 Azure SDK에 대한 자세한 로깅이 가능합니다.
쿼리 스크립트를 다시 실행합니다. 이제 SDK에서 출력으로 제공되는 정보 문구로 문제에 대한 자세한 정보를 얻을 수 있습니다.
ManagedIdentityCredential 및 토큰 획득 실패와 관련된 출력 메시지가 표시되면 테넌트가 여러 개 있고 Azure 로그인에서 검색 서비스가 없는 테넌트를 사용하고 있을 수 있습니다. 테넌트 ID를 가져오려면 Azure Portal에서 "테넌트 속성"을 검색하거나 az login tenant list
을(를) 실행합니다.
테넌트 ID가 있으면 명령 프롬프트에서 az login --tenant <YOUR-TENANT-ID>
을(를) 실행한 다음 스크립트를 다시 실행합니다.
정리
본인 소유의 구독으로 이 모듈을 진행하고 있는 경우에는 프로젝트가 끝날 때 여기에서 만든 리소스가 계속 필요한지 확인하는 것이 좋습니다. 계속 실행되는 리소스에는 요금이 부과될 수 있습니다. 리소스를 개별적으로 삭제하거나 리소스 그룹을 삭제하여 전체 리소스 세트를 삭제할 수 있습니다.
맨 왼쪽 창의 모든 리소스 또는 리소스 그룹 링크를 사용하여 Azure Portal에서 리소스를 찾고 관리할 수 있습니다.
필수 조건
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
Azure OpenAI 리소스입니다.
- 사용하려는 채팅 완료 모델(gpt-4o, gpt-4o-mini 또는 동등한 모델)을 지원하는 지역을 선택합니다.
- Azure AI Foundry에서 채팅 완료 모델을 배포하거나 다른 방법을 사용합니다.
-
- 기본 계층 이상을 사용하는 것이 좋습니다.
- 의미 체계 순위를 사용하도록 설정합니다.
Python 확장 및 Jupyter 패키지가 있는 Visual Studio Code. 자세한 내용은 Visual Studio Code의 Python을 참조하세요.
파일 다운로드
GitHub에서 Jupyter Notebook을 다운로드하여 이 빠른 시작에서 요청을 보냅니다. 자세한 내용은 GitHub에서 파일 다운로드를 참조하세요.
이 문서의 지침을 사용하여 로컬 시스템에서 새 파일을 시작하고 수동으로 요청을 만들 수도 있습니다.
액세스 구성
검색 엔드포인트에 대한 요청은 인증 및 권한 부여를 받아야 합니다. 이 작업에 API 키 또는 역할을 사용할 수 있습니다. 키는 시작하기가 더 쉽지만 역할이 더 안전합니다. 이 빠른 시작에서는 역할을 가정합니다.
두 개의 클라이언트를 설정하므로 두 리소스 모두에 대한 권한이 필요합니다.
Azure AI 검색은 로컬 시스템에서 쿼리 요청을 수신합니다. 호텔 샘플 인덱스가 이미 있는 경우 검색 인덱스 데이터 판독기 역할 할당을 자신에게 할당합니다. 없는 경우 인덱스 만들기 및 쿼리를 수행할 수 있도록 Search Service 기여자 및 검색 인덱스 데이터 기여자 역할을 자신에게 할당합니다.
Azure OpenAI는 로컬 시스템에서 쿼리 및 검색 결과를 수신합니다. Azure OpenAI에서 Cognitive Services OpenAI 사용자 역할을 자신에게 할당합니다.
Azure Portal에 로그인합니다.
역할 기반 액세스에 대한 Azure AI 검색 구성:
Azure Portal에서 Azure AI 검색 서비스를 찾습니다.
왼쪽 메뉴에서 설정>키를 선택한 다음 역할 기반 액세스 제어 또는 둘 다를 선택합니다.
역할 할당:
왼쪽 메뉴에서 IAM(액세스 제어)을 선택합니다.
Azure AI Search에서 이러한 역할을 선택하여 검색 인덱스 만들기, 로드 및 쿼리를 수행하고 Microsoft Entra ID 사용자 ID에 할당합니다.
- 검색 인덱스 데이터 기여자
- Search 서비스 기여자
Azure OpenAI에서 IAM(액세스 제어)을 선택하여 Azure OpenAI에서 이 역할을 자신에게 할당합니다.
- Cognitive Services OpenAI 사용자
사용 권한이 적용되는 데 몇 분 정도 걸릴 수 있습니다.
hotels-sample-index를 업데이트
검색 인덱스가 채팅 모델에 대한 접지 데이터를 제공합니다. 몇 분 안에 만들 수 있고 모든 검색 서비스 계층에서 실행되는 hotels-sample-index를 권장합니다. 이 인덱스는 기본 제공 샘플 데이터를 사용하여 생성됩니다.
Azure Portal에서 검색 서비스를 찾습니다.
개요 홈페이지에서 데이터 가져오기를 선택하여 마법사를 시작합니다.
데이터에 연결 페이지의 드롭다운 목록에서 샘플을 선택합니다.
hotels-sample을 선택합니다.
나머지 페이지에서 다음을 선택하고 기본값을 적용합니다.
인덱스가 만들어지면 왼쪽 메뉴에서 Search 관리>인덱스를 선택하여 인덱스를 엽니다.
JSON 편집을 선택합니다.
인덱스의 끝으로 스크롤하여 인덱스에 추가할 수 있는 구문의 자리 표시자를 찾을 수 있습니다.
"analyzers": [], "tokenizers": [], "tokenFilters": [], "charFilters": [], "normalizers": [],
"normalizers" 뒤의 새 줄에서 다음 의미 체계 구성을 붙여넣습니다. 이 예제에서는 이 빠른 시작을 실행하는 데 중요한
"defaultConfiguration"
을 지정합니다."semantic":{ "defaultConfiguration":"semantic-config", "configurations":[ { "name":"semantic-config", "prioritizedFields":{ "titleField":{ "fieldName":"HotelName" }, "prioritizedContentFields":[ { "fieldName":"Description" } ], "prioritizedKeywordsFields":[ { "fieldName":"Category" }, { "fieldName":"Tags" } ] } } ] },
변경 내용을 저장합니다.
검색 탐색기에서 다음 쿼리를 실행하여 인덱스를 테스트합니다.
complimentary breakfast
.출력은 다음 예제와 비슷해야 합니다. 검색 엔진에서 직접 반환되는 결과는 의미 순위매기기를 사용하는 경우 의미 체계 순위 지정 점수 및 캡션과 검색 점수와 같은 메타데이터와 함께 필드 및 해당 문자값으로 구성됩니다. Select 문을 사용하여 HotelName, Description 및 Tags 필드만 반환했습니다.
{ "@odata.count": 18, "@search.answers": [], "value": [ { "@search.score": 2.2896252, "@search.rerankerScore": 2.506816864013672, "@search.captions": [ { "text": "Head Wind Resort. Suite. coffee in lobby\r\nfree wifi\r\nview. The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a **complimentary continental breakfast** in the lobby, and free Wi-Fi throughout the hotel..", "highlights": "" } ], "HotelName": "Head Wind Resort", "Description": "The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a complimentary continental breakfast in the lobby, and free Wi-Fi throughout the hotel.", "Tags": [ "coffee in lobby", "free wifi", "view" ] }, { "@search.score": 2.2158256, "@search.rerankerScore": 2.288334846496582, "@search.captions": [ { "text": "Swan Bird Lake Inn. Budget. continental breakfast\r\nfree wifi\r\n24-hour front desk service. We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins..", "highlights": "" } ], "HotelName": "Swan Bird Lake Inn", "Description": "We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins.", "Tags": [ "continental breakfast", "free wifi", "24-hour front desk service" ] }, { "@search.score": 0.92481667, "@search.rerankerScore": 2.221315860748291, "@search.captions": [ { "text": "White Mountain Lodge & Suites. Resort and Spa. continental breakfast\r\npool\r\nrestaurant. Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings..", "highlights": "" } ], "HotelName": "White Mountain Lodge & Suites", "Description": "Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings.", "Tags": [ "continental breakfast", "pool", "restaurant" ] }, . . . ]}
서비스 엔드포인트 가져오기
나머지 섹션에서는 Azure OpenAI 및 Azure AI 검색에 대한 API 호출을 설정합니다. 서비스 엔드포인트를 가져와서 코드에서 변수로 제공할 수 있습니다.
Azure Portal에 로그인합니다.
개요 홈페이지에서 URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.search.windows.net
개요 홈페이지에서 엔드포인트를 볼 링크를 선택합니다. URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.openai.azure.com/
가상 환경 만들기
이 단계에서는 로컬 시스템 및 Visual Studio Code로 다시 전환합니다. 격리된 상태로 종속성을 설치할 수 있도록 가상 환경을 만드는 것이 좋습니다.
Visual Studio Code에서 Quickstart-RAG.ipynb가 포함된 폴더를 엽니다.
Ctrl-shift-P를 눌러 명령 팔레트를 열고 "Python: 환경 만들기"를 검색한 다음 현재 작업 영역에서 가상 환경을 만들도록 선택합니다
Venv
.종속성에 대해 빠른 시작-RAG\requirements.txt 선택합니다.
환경을 만드는 데 몇 분이 걸립니다. 환경이 준비되면 다음 단계를 계속 진행합니다.
Azure에 로그인
연결에 Microsoft Entra ID 및 역할 할당을 사용하고 있습니다. Azure AI Search 및 Azure OpenAI와 동일한 테넌트 및 구독에 로그인했는지 확인합니다. 명령줄에서 Azure CLI를 사용하여 현재 속성을 표시하고, 속성을 변경하고, 로그인할 수 있습니다. 자세한 내용은 키 없이 연결을 참조 하세요.
다음 각 명령을 순서대로 실행합니다.
az account show
az account set --subscription <PUT YOUR SUBSCRIPTION ID HERE>
az login --tenant <PUT YOUR TENANT ID HERE>
이제 로컬 디바이스에서 Azure에 로그인해야 합니다.
쿼리 및 채팅 스레드 설정
이 섹션에서는 Visual Studio Code와 Python을 사용하여 Azure OpenAI에서 채팅 완료 API를 호출합니다.
Visual Studio Code를 시작하고 .ipynb 파일을 열거나 새 Python 파일을 만듭니다.
다음 Python 패키지를 설치합니다.
! pip install azure-search-documents==11.6.0b5 --quiet ! pip install azure-identity==1.16.1 --quiet ! pip install openai --quiet ! pip install aiohttp --quiet ! pip install ipykernel --quiet
다음 변수를 설정하여 자리 표시자를 이전 단계에서 수집한 엔드포인트로 대체합니다.
AZURE_SEARCH_SERVICE: str = "PUT YOUR SEARCH SERVICE ENDPOINT HERE" AZURE_OPENAI_ACCOUNT: str = "PUT YOUR AZURE OPENAI ENDPOINT HERE" AZURE_DEPLOYMENT_MODEL: str = "gpt-4o"
클라이언트, 프롬프트, 쿼리 및 응답을 설정합니다.
Azure Government 클라우드의 경우 토큰 공급자의 API 엔드포인트를 수정합니다
"https://cognitiveservices.azure.us/.default"
.# Set up the query for generating responses from azure.identity import DefaultAzureCredential from azure.identity import get_bearer_token_provider from azure.search.documents import SearchClient from openai import AzureOpenAI credential = DefaultAzureCredential() token_provider = get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default") openai_client = AzureOpenAI( api_version="2024-06-01", azure_endpoint=AZURE_OPENAI_ACCOUNT, azure_ad_token_provider=token_provider ) search_client = SearchClient( endpoint=AZURE_SEARCH_SERVICE, index_name="hotels-sample-index", credential=credential ) # This prompt provides instructions to the model GROUNDED_PROMPT=""" You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner. Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {query} Sources:\n{sources} """ # Query is the question being asked. It's sent to the search engine and the chat model query="Can you recommend a few hotels with complimentary breakfast?" # Search results are created by the search client # Search results are composed of the top 5 results and the fields selected from the search index # Search results include the top 5 matches to your query search_results = search_client.search( search_text=query, top=5, select="Description,HotelName,Tags" ) sources_formatted = "\n".join([f'{document["HotelName"]}:{document["Description"]}:{document["Tags"]}' for document in search_results]) # Send the search results and the query to the LLM to generate a response based on the prompt. response = openai_client.chat.completions.create( messages=[ { "role": "user", "content": GROUNDED_PROMPT.format(query=query, sources=sources_formatted) } ], model=AZURE_DEPLOYMENT_MODEL ) # Here is the response from the chat model. print(response.choices[0].message.content)
출력은 Azure OpenAI의 결과이며 여러 호텔에 대한 권장 사항으로 구성됩니다. 다음은 출력의 모양에 대한 예입니다.
Sure! Here are a few hotels that offer complimentary breakfast: - **Head Wind Resort** - Complimentary continental breakfast in the lobby - Free Wi-Fi throughout the hotel - **Double Sanctuary Resort** - Continental breakfast included - **White Mountain Lodge & Suites** - Continental breakfast available - **Swan Bird Lake Inn** - Continental-style breakfast each morning with a variety of food and drinks such as caramel cinnamon rolls, coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins
사용할 수 없음 오류 메시지가 표시되면 Azure AI 검색 구성을 확인하여 역할 기반 액세스가 사용하도록 설정되어 있는지 확인합니다.
권한 부여 실패 오류 메시지가 표시되면 몇 분 정도 기다렸다가 다시 시도하세요. 역할 할당이 작동하려면 몇 분 정도 걸릴 수 있습니다.
리소스를 찾을 수 없는 오류 메시지가 표시되면 리소스 URI를 확인하고 채팅 모델의 API 버전이 유효한지 확인합니다.
그렇지 않은 경우 더 실험하려면 쿼리를 변경하고 마지막 단계를 다시 실행하여 모델이 기초 데이터를 사용하여 작동하는 방식을 더 잘 이해합니다.
프롬프트를 수정하여 출력의 톤이나 구조를 변경할 수도 있습니다.
쿼리 매개 변수 단계에서
use_semantic_reranker=False
를 설정하여 의미론적 순위 지정 없이 쿼리를 시도할 수도 있습니다. 의미론적 순위 지정은 쿼리 결과의 관련성과 LLM이 유용한 정보를 반환하는 기능을 눈에 띄게 개선할 수 있습니다. 실험을 통해 콘텐츠에 변화를 가져올지 여부를 결정할 수 있습니다.
복잡한 RAG 쿼리 보내기
Azure AI Search는 중첩된 JSON 구조에 대한 복합 형식 을 지원합니다. hotels-sample-index에서 , Address
, Address.StreetAddress
Address.City
및 Address.StateProvince
로 구성된 복합 형식의 Address.PostalCode
Address.Country
예입니다. 인덱스는 각 호텔에 대한 복잡한 컬렉션 Rooms
도 가지고 있습니다.
인덱스 형식이 복잡한 경우 검색 결과 출력을 JSON으로 변환한 다음 JSON을 채팅 모델에 전달하는 경우 쿼리에서 해당 필드를 제공할 수 있습니다. 다음 예제에서는 요청에 복합 형식을 추가합니다. 서식 지정 지침에는 JSON 사양이 포함됩니다.
import json
# Query is the question being asked. It's sent to the search engine and the LLM.
query="Can you recommend a few hotels that offer complimentary breakfast?
Tell me their description, address, tags, and the rate for one room that sleeps 4 people."
# Set up the search results and the chat thread.
# Retrieve the selected fields from the search index related to the question.
selected_fields = ["HotelName","Description","Address","Rooms","Tags"]
search_results = search_client.search(
search_text=query,
top=5,
select=selected_fields,
query_type="semantic"
)
sources_filtered = [{field: result[field] for field in selected_fields} for result in search_results]
sources_formatted = "\n".join([json.dumps(source) for source in sources_filtered])
response = openai_client.chat.completions.create(
messages=[
{
"role": "user",
"content": GROUNDED_PROMPT.format(query=query, sources=sources_formatted)
}
],
model=AZURE_DEPLOYMENT_MODEL
)
print(response.choices[0].message.content)
출력은 Azure OpenAI의 출력이며 복잡한 형식의 콘텐츠를 추가합니다.
Here are a few hotels that offer complimentary breakfast and have rooms that sleep 4 people:
1. **Head Wind Resort**
- **Description:** The best of old town hospitality combined with views of the river and
cool breezes off the prairie. Enjoy a complimentary continental breakfast in the lobby,
and free Wi-Fi throughout the hotel.
- **Address:** 7633 E 63rd Pl, Tulsa, OK 74133, USA
- **Tags:** Coffee in lobby, free Wi-Fi, view
- **Room for 4:** Suite, 2 Queen Beds (Amenities) - $254.99
2. **Double Sanctuary Resort**
- **Description:** 5-star Luxury Hotel - Biggest Rooms in the city. #1 Hotel in the area
listed by Traveler magazine. Free WiFi, Flexible check in/out, Fitness Center & espresso
in room. Offers continental breakfast.
- **Address:** 2211 Elliott Ave, Seattle, WA 98121, USA
- **Tags:** View, pool, restaurant, bar, continental breakfast
- **Room for 4:** Suite, 2 Queen Beds (Amenities) - $254.99
3. **Swan Bird Lake Inn**
- **Description:** Continental-style breakfast featuring a variety of food and drinks.
Locally made caramel cinnamon rolls are a favorite.
- **Address:** 1 Memorial Dr, Cambridge, MA 02142, USA
- **Tags:** Continental breakfast, free Wi-Fi, 24-hour front desk service
- **Room for 4:** Budget Room, 2 Queen Beds (City View) - $85.99
4. **Gastronomic Landscape Hotel**
- **Description:** Known for its culinary excellence under the management of William Dough,
offers continental breakfast.
- **Address:** 3393 Peachtree Rd, Atlanta, GA 30326, USA
- **Tags:** Restaurant, bar, continental breakfast
- **Room for 4:** Budget Room, 2 Queen Beds (Amenities) - $66.99
...
- **Tags:** Pool, continental breakfast, free parking
- **Room for 4:** Budget Room, 2 Queen Beds (Amenities) - $60.99
Enjoy your stay! Let me know if you need any more information.
오류 문제 해결
인증 오류를 디버그하려면 검색 엔진 및 LLM을 호출하는 단계 앞에 다음 코드를 삽입합니다.
import sys
import logging # Set the logging level for all azure-storage-* libraries
logger = logging.getLogger('azure.identity')
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(stream=sys.stdout)
formatter = logging.Formatter('[%(levelname)s %(name)s] %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
쿼리 스크립트를 다시 실행합니다. 이제 문제에 대한 자세한 정보를 제공하는 INFO 및 DEBUG 문을 출력에 가져와야 합니다.
ManagedIdentityCredential 및 토큰 획득 실패와 관련된 출력 메시지가 표시되면 테넌트가 여러 개 있고 Azure 로그인에서 검색 서비스가 없는 테넌트를 사용하고 있을 수 있습니다. 테넌트 ID를 가져오려면 Azure Portal에서 "테넌트 속성"을 검색하거나 az login tenant list
을(를) 실행합니다.
테넌트 ID가 있으면 명령 프롬프트에서 az login --tenant <YOUR-TENANT-ID>
을(를) 실행한 다음 스크립트를 다시 실행합니다.
정리
본인 소유의 구독으로 이 모듈을 진행하고 있는 경우에는 프로젝트가 끝날 때 여기에서 만든 리소스가 계속 필요한지 확인하는 것이 좋습니다. 계속 실행되는 리소스에는 요금이 부과될 수 있습니다. 리소스를 개별적으로 삭제하거나 리소스 그룹을 삭제하여 전체 리소스 세트를 삭제할 수 있습니다.
맨 왼쪽 창의 모든 리소스 또는 리소스 그룹 링크를 사용하여 Azure Portal에서 리소스를 찾고 관리할 수 있습니다.
필수 조건
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
Azure OpenAI 리소스입니다.
- 사용하려는 채팅 완료 모델(gpt-4o, gpt-4o-mini 또는 동등한 모델)을 지원하는 지역을 선택합니다.
- Azure AI Foundry에서 채팅 완료 모델을 배포하거나 다른 방법을 사용합니다.
-
- 기본 계층 이상을 사용하는 것이 좋습니다.
- 의미 체계 순위를 사용하도록 설정합니다.
설명 또는 자세한 텍스트 필드가 포함된 새로운 인덱스 또는 기존 인덱스는 인덱스 내에서 검색 가능하도록 설정되어 있습니다. 이 빠른 시작에서는 hotels-sample-index를 가정합니다.
Microsoft Entra ID를 사용한 키 없는 인증을 위한 Azure CLI 입니다.
파일 다운로드
GitHub에서 .rest 파일을 다운로드하여 이 빠른 시작에서 요청을 보냅니다. 자세한 내용은 GitHub에서 파일 다운로드를 참조하세요.
이 문서의 지침을 사용하여 로컬 시스템에서 새 파일을 시작하고 수동으로 요청을 만들 수도 있습니다.
액세스 구성
검색 엔드포인트에 대한 요청은 인증 및 권한 부여를 받아야 합니다. 이 작업에 API 키 또는 역할을 사용할 수 있습니다. 키는 시작하기가 더 쉽지만 역할이 더 안전합니다. 이 빠른 시작에서는 역할을 가정합니다.
두 개의 클라이언트를 설정하므로 두 리소스 모두에 대한 권한이 필요합니다.
Azure AI 검색은 로컬 시스템에서 쿼리 요청을 수신합니다. 호텔 샘플 인덱스가 이미 있는 경우 검색 인덱스 데이터 판독기 역할 할당을 자신에게 할당합니다. 없는 경우 인덱스 만들기 및 쿼리를 수행할 수 있도록 Search Service 기여자 및 검색 인덱스 데이터 기여자 역할을 자신에게 할당합니다.
Azure OpenAI는 로컬 시스템에서 쿼리 및 검색 결과를 수신합니다. Azure OpenAI에서 Cognitive Services OpenAI 사용자 역할을 자신에게 할당합니다.
Azure Portal에 로그인합니다.
역할 기반 액세스에 대한 Azure AI 검색 구성:
Azure Portal에서 Azure AI 검색 서비스를 찾습니다.
왼쪽 메뉴에서 설정>키를 선택한 다음 역할 기반 액세스 제어 또는 둘 다를 선택합니다.
역할 할당:
왼쪽 메뉴에서 IAM(액세스 제어)을 선택합니다.
Azure AI Search에서 이러한 역할을 선택하여 검색 인덱스 만들기, 로드 및 쿼리를 수행하고 Microsoft Entra ID 사용자 ID에 할당합니다.
- 검색 인덱스 데이터 기여자
- Search 서비스 기여자
Azure OpenAI에서 IAM(액세스 제어)을 선택하여 Azure OpenAI에서 이 역할을 자신에게 할당합니다.
- Cognitive Services OpenAI 사용자
사용 권한이 적용되는 데 몇 분 정도 걸릴 수 있습니다.
서비스 엔드포인트 및 토큰 가져오기
나머지 섹션에서는 Azure OpenAI 및 Azure AI 검색에 대한 API 호출을 설정합니다. 코드에서 변수로 제공할 수 있도록 서비스 엔드포인트 및 토큰을 가져옵니다.
Azure Portal에 로그인합니다.
개요 홈페이지에서 URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.search.windows.net
개요 홈페이지에서 엔드포인트를 볼 링크를 선택합니다. URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.openai.azure.com/
명령 프롬프트에서 Azure CLI에서 개인용 액세스 토큰을 가져옵니다. 각 리소스에 대한 명령은 다음과 같습니다.
az account get-access-token --resource https://search.azure.com --query "accessToken" -o tsv
az account get-access-token --resource https://cognitiveservices.azure.com --query "accessToken" -o tsv
클라이언트 설정
이 빠른 시작에서는 REST 클라이언트 및 Azure AI Search REST API 를 사용하여 RAG 패턴을 구현합니다.
이 빠른 시작에서는 REST 클라이언트 확장을 사용하는 Visual Studio Code를 사용하는 것이 좋습니다.
팁 (조언)
소스 코드를 다운로드하여 완료된 프로젝트로 시작하거나 다음 단계에 따라 직접 만들 수 있습니다.
Visual Studio Code를 시작하고 quickstart-rag.rest 파일을 열거나 새 파일을 만듭니다.
맨 위에서 검색 서비스, 권한 부여 및 인덱스 이름에 대한 환경 변수를 설정합니다.
- 의 경우 @searchUrl검색 엔드포인트에 붙여넣습니다.
- 의 경우 @aoaiUrlAzure OpenAI 엔드포인트에 붙여넣습니다.
-
@searchAccessToken에 대해 범위가 지정된
https://search.azure.com
액세스 토큰을 붙여넣으세요. -
@aoaiAccessToken에 대한 범위가 지정된 액세스 토큰을
https://cognitiveservices.azure.com
에 붙여넣으세요.
연결을 테스트하려면 첫 번째 요청을 보냅니다.
### List existing indexes by name (verify the connection) GET {{searchUrl}}/indexes?api-version=2025-05-01-preview&$select=name HTTP/1.1 Authorization: Bearer {{personalAccessToken}}
보낸 요청을 선택합니다.
이 GET 요청에 대한 출력은 인덱스 목록이어야 합니다. 그 중 호텔 샘플 인덱 스가 표시됩니다.
쿼리 및 채팅 스레드 설정
이 섹션에서는 Visual Studio Code 및 REST를 사용하여 Azure OpenAI에서 채팅 완료 API를 호출합니다.
"무료 아침 식사가 포함된 몇 가지 호텔을 추천할 수 있나요?"라는 문구에 대한 쿼리 요청을 설정합니다. 이 쿼리는 의미 체계 순위를 사용하여 축자 텍스트가 정확히 일치하지 않더라도 관련 일치 항목을 반환합니다. 결과는 searchRequest 변수에 보관되어 다음 요청에서 다시 사용됩니다.
# @name searchRequest POST {{searchUrl}}/indexes/{{index-name}}/docs/search?api-version={{api-version}} HTTP/1.1 Content-Type: application/json Authorization: Bearer {{searchAccessToken}} { "search": "Can you recommend a few hotels with complimentary breakfast?", "queryType": "semantic", "semanticConfiguration": "semantic-config", "select": "Description,HotelName,Tags", "top": 5 } ### 3 - Use search results in Azure OpenAI call to a chat completion model POST {{aoaiUrl}}/openai/deployments/{{aoaiGptDeployment}}/chat/completions?api-version=2024-08-01-preview HTTP/1.1 Content-Type: application/json Authorization: Bearer {{aoaiAccessToken}} { "messages": [ { "role": "system", "content": "You recommend hotels based on activities and amenities. Answer the query using only the search result. Answer in a friendly and concise manner. Answer ONLY with the facts provided. If there isn't enough information below, say you don't know." }, { "role": "user", "content": "Based on the hotel search results, can you recommend hotels with breakfast? Here are all the hotels I found:\n\nHotel 1: {{searchRequest.response.body.value[0].HotelName}}\nDescription: {{searchRequest.response.body.value[0].Description}}\n\nHotel 2: {{searchRequest.response.body.value[1].HotelName}}\nDescription: {{searchRequest.response.body.value[1].Description}}\n\nHotel 3: {{searchRequest.response.body.value[2].HotelName}}\nDescription: {{searchRequest.response.body.value[2].Description}}\n\nHotel 4: {{searchRequest.response.body.value[3].HotelName}}\nDescription: {{searchRequest.response.body.value[3].Description}}\n\nHotel 5: {{searchRequest.response.body.value[4].HotelName}}\nDescription: {{searchRequest.response.body.value[4].Description}}\n\nPlease recommend which hotels offer breakfast based on their descriptions." } ], "max_tokens": 1000, "temperature": 0.7 }`
요청을 보냅니다.
출력은 다음 예제와 유사합니다.
"value": [ { "@search.score": 3.9269178, "@search.rerankerScore": 2.380699872970581, "HotelName": "Head Wind Resort", "Description": "The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a complimentary continental breakfast in the lobby, and free Wi-Fi throughout the hotel.", "Tags": [ "coffee in lobby", "free wifi", "view" ] }, { "@search.score": 1.5450059, "@search.rerankerScore": 2.1258809566497803, "HotelName": "Thunderbird Motel", "Description": "Book Now & Save. Clean, Comfortable rooms at the lowest price. Enjoy complimentary coffee and tea in common areas.", "Tags": [ "coffee in lobby", "free parking", "free wifi" ] }, { "@search.score": 2.2158256, "@search.rerankerScore": 2.121671438217163, "HotelName": "Swan Bird Lake Inn", "Description": "We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins.", "Tags": [ "continental breakfast", "free wifi", "24-hour front desk service" ] }, { "@search.score": 0.6395861, "@search.rerankerScore": 2.116753339767456, "HotelName": "Waterfront Scottish Inn", "Description": "Newly Redesigned Rooms & airport shuttle. Minutes from the airport, enjoy lakeside amenities, a resort-style pool & stylish new guestrooms with Internet TVs.", "Tags": [ "24-hour front desk service", "continental breakfast", "free wifi" ] }, { "@search.score": 4.885111, "@search.rerankerScore": 2.0008862018585205, "HotelName": "Double Sanctuary Resort", "Description": "5 star Luxury Hotel - Biggest Rooms in the city. #1 Hotel in the area listed by Traveler magazine. Free WiFi, Flexible check in/out, Fitness Center & espresso in room.", "Tags": [ "view", "pool", "restaurant", "bar", "continental breakfast" ] } ]
채팅 완료 모델을 사용하여 대화 턴을 설정합니다. 이 요청에는 응답에 대한 지침을 제공하는 프롬프트가 포함됩니다. 값은
max_tokens
이전 쿼리의 검색 결과를 수용할 수 있을 만큼 충분히 큽합니다.POST {{aoaiUrl}}/openai/deployments/{{aoaiGptDeployment}}/chat/completions?api-version=2024-08-01-preview HTTP/1.1 Content-Type: application/json Authorization: Bearer {{aoaiAccessToken}} { "messages": [ { "role": "system", "content": "You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the search result. Answer in a friendly and concise manner. Answer ONLY with the facts provided. If there isn't enough information below, say you don't know." }, { "role": "user", "content": "Based on the hotel search results, can you recommend hotels with breakfast? Here are all the hotels I found:\n\nHotel 1: {{searchRequest.response.body.value[0].HotelName}}\nDescription: {{searchRequest.response.body.value[0].Description}}\n\nHotel 2: {{searchRequest.response.body.value[1].HotelName}}\nDescription: {{searchRequest.response.body.value[1].Description}}\n\nHotel 3: {{searchRequest.response.body.value[2].HotelName}}\nDescription: {{searchRequest.response.body.value[2].Description}}\n\nHotel 4: {{searchRequest.response.body.value[3].HotelName}}\nDescription: {{searchRequest.response.body.value[3].Description}}\n\nHotel 5: {{searchRequest.response.body.value[4].HotelName}}\nDescription: {{searchRequest.response.body.value[4].Description}}\n\nPlease recommend which hotels offer breakfast based on their descriptions." } ], "max_tokens": 1000, "temperature": 0.7 }
요청을 보냅니다.
출력은 HTTP 200 성공 상태 메시지여야 합니다. 출력에는 질문에 답변하는 콘텐츠가 포함됩니다.
"message": { "annotations": [], "content": "I recommend the following hotels that offer breakfast:\n\n1. **Head Wind Resort** - Offers a complimentary continental breakfast in the lobby.\n2. **Swan Bird Lake Inn** - Serves a continental-style breakfast each morning, including a variety of food and drinks. \n\nEnjoy your stay!", "refusal": null, "role": "assistant" }
태그 필드에서 아침 식사를 언급하는 여러 호텔이 출력에 없습니다. 태그 필드는 배열이며 이 필드를 포함하면 결과의 JSON 구조가 중단됩니다. REST 클라이언트에는 문자열 변환 기능이 없으므로 배열을 포함해야 하는 경우 JSON을 문자열로 수동으로 변환하기 위한 추가 코드가 필요합니다. 이 빠른 시작에서는 이 단계를 생략합니다.
정리
본인 소유의 구독으로 이 모듈을 진행하고 있는 경우에는 프로젝트가 끝날 때 여기에서 만든 리소스가 계속 필요한지 확인하는 것이 좋습니다. 계속 실행되는 리소스에는 요금이 부과될 수 있습니다. 리소스를 개별적으로 삭제하거나 리소스 그룹을 삭제하여 전체 리소스 세트를 삭제할 수 있습니다.
맨 왼쪽 창의 모든 리소스 또는 리소스 그룹 링크를 사용하여 Azure Portal에서 리소스를 찾고 관리할 수 있습니다.
필수 조건
활성 구독이 있는 Azure 계정. 무료로 계정을 만듭니다.
Azure AI Search 서비스, 모든 등급 및 지역
Azure OpenAI 리소스입니다.
- 사용하려는 채팅 완료 모델(gpt-4o, gpt-4o-mini 또는 동등한 모델)을 지원하는 지역을 선택합니다.
- Azure AI Foundry에서 채팅 완료 모델을 배포하거나 다른 방법을 사용합니다.
-
- 기본 계층 이상을 사용하는 것이 좋습니다.
- 의미 체계 순위를 사용하도록 설정합니다.
TypeScript. npm을 사용하여 TypeScript를 전역적으로 설치할 수 있습니다.
npm install -g typescript
액세스 구성
검색 엔드포인트에 대한 요청은 인증 및 권한 부여를 받아야 합니다. 이 작업에 API 키 또는 역할을 사용할 수 있습니다. 키는 시작하기가 더 쉽지만 역할이 더 안전합니다. 이 빠른 시작에서는 역할을 가정합니다.
두 개의 클라이언트를 설정하므로 두 리소스 모두에 대한 권한이 필요합니다.
Azure AI 검색은 로컬 시스템에서 쿼리 요청을 수신합니다. 호텔 샘플 인덱스가 이미 있는 경우 검색 인덱스 데이터 판독기 역할 할당을 자신에게 할당합니다. 없는 경우 인덱스 만들기 및 쿼리를 수행할 수 있도록 Search Service 기여자 및 검색 인덱스 데이터 기여자 역할을 자신에게 할당합니다.
Azure OpenAI는 로컬 시스템에서 쿼리 및 검색 결과를 수신합니다. Azure OpenAI에서 Cognitive Services OpenAI 사용자 역할을 자신에게 할당합니다.
Azure Portal에 로그인합니다.
역할 기반 액세스에 대한 Azure AI 검색 구성:
Azure Portal에서 Azure AI 검색 서비스를 찾습니다.
왼쪽 메뉴에서 설정>키를 선택한 다음 역할 기반 액세스 제어 또는 둘 다를 선택합니다.
역할 할당:
왼쪽 메뉴에서 IAM(액세스 제어)을 선택합니다.
Azure AI Search에서 이러한 역할을 선택하여 검색 인덱스 만들기, 로드 및 쿼리를 수행하고 Microsoft Entra ID 사용자 ID에 할당합니다.
- 검색 인덱스 데이터 기여자
- Search 서비스 기여자
Azure OpenAI에서 IAM(액세스 제어)을 선택하여 Azure OpenAI에서 이 역할을 자신에게 할당합니다.
- Cognitive Services OpenAI 사용자
사용 권한이 적용되는 데 몇 분 정도 걸릴 수 있습니다.
인덱스 만들기
검색 인덱스가 채팅 모델에 대한 접지 데이터를 제공합니다. 몇 분 안에 만들 수 있고 모든 검색 서비스 계층에서 실행되는 hotels-sample-index를 권장합니다. 이 인덱스는 기본 제공 샘플 데이터를 사용하여 생성됩니다.
Azure Portal에서 검색 서비스를 찾습니다.
개요 홈페이지에서 데이터 가져오기를 선택하여 마법사를 시작합니다.
데이터에 연결 페이지의 드롭다운 목록에서 샘플을 선택합니다.
hotels-sample을 선택합니다.
나머지 페이지에서 다음을 선택하고 기본값을 적용합니다.
인덱스가 만들어지면 왼쪽 메뉴에서 Search 관리>인덱스를 선택하여 인덱스를 엽니다.
JSON 편집을 선택합니다.
인덱스의 끝으로 스크롤하여 인덱스에 추가할 수 있는 구문의 자리 표시자를 찾을 수 있습니다.
"analyzers": [], "tokenizers": [], "tokenFilters": [], "charFilters": [], "normalizers": [],
"normalizers" 뒤의 새 줄에서 다음 의미 체계 구성을 붙여넣습니다. 이 예제에서는 이 빠른 시작을 실행하는 데 중요한
"defaultConfiguration"
을 지정합니다."semantic":{ "defaultConfiguration":"semantic-config", "configurations":[ { "name":"semantic-config", "prioritizedFields":{ "titleField":{ "fieldName":"HotelName" }, "prioritizedContentFields":[ { "fieldName":"Description" } ], "prioritizedKeywordsFields":[ { "fieldName":"Category" }, { "fieldName":"Tags" } ] } } ] },
변경 내용을 저장합니다.
검색 탐색기에서 다음 쿼리를 실행하여 인덱스를 테스트합니다.
complimentary breakfast
.출력은 다음 예제와 비슷해야 합니다. 검색 엔진에서 직접 반환되는 결과는 의미 순위매기기를 사용하는 경우 의미 체계 순위 지정 점수 및 캡션과 검색 점수와 같은 메타데이터와 함께 필드 및 해당 문자값으로 구성됩니다. Select 문을 사용하여 HotelName, Description 및 Tags 필드만 반환했습니다.
{ "@odata.count": 18, "@search.answers": [], "value": [ { "@search.score": 2.2896252, "@search.rerankerScore": 2.506816864013672, "@search.captions": [ { "text": "Head Wind Resort. Suite. coffee in lobby\r\nfree wifi\r\nview. The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a **complimentary continental breakfast** in the lobby, and free Wi-Fi throughout the hotel..", "highlights": "" } ], "HotelName": "Head Wind Resort", "Description": "The best of old town hospitality combined with views of the river and cool breezes off the prairie. Our penthouse suites offer views for miles and the rooftop plaza is open to all guests from sunset to 10 p.m. Enjoy a complimentary continental breakfast in the lobby, and free Wi-Fi throughout the hotel.", "Tags": [ "coffee in lobby", "free wifi", "view" ] }, { "@search.score": 2.2158256, "@search.rerankerScore": 2.288334846496582, "@search.captions": [ { "text": "Swan Bird Lake Inn. Budget. continental breakfast\r\nfree wifi\r\n24-hour front desk service. We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins..", "highlights": "" } ], "HotelName": "Swan Bird Lake Inn", "Description": "We serve a continental-style breakfast each morning, featuring a variety of food and drinks. Our locally made, oh-so-soft, caramel cinnamon rolls are a favorite with our guests. Other breakfast items include coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins.", "Tags": [ "continental breakfast", "free wifi", "24-hour front desk service" ] }, { "@search.score": 0.92481667, "@search.rerankerScore": 2.221315860748291, "@search.captions": [ { "text": "White Mountain Lodge & Suites. Resort and Spa. continental breakfast\r\npool\r\nrestaurant. Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings..", "highlights": "" } ], "HotelName": "White Mountain Lodge & Suites", "Description": "Live amongst the trees in the heart of the forest. Hike along our extensive trail system. Visit the Natural Hot Springs, or enjoy our signature hot stone massage in the Cathedral of Firs. Relax in the meditation gardens, or join new friends around the communal firepit. Weekend evening entertainment on the patio features special guest musicians or poetry readings.", "Tags": [ "continental breakfast", "pool", "restaurant" ] }, . . . ]}
서비스 엔드포인트 가져오기
나머지 섹션에서는 Azure OpenAI 및 Azure AI 검색에 대한 API 호출을 설정합니다. 서비스 엔드포인트를 가져와서 코드에서 변수로 제공할 수 있습니다.
Azure Portal에 로그인합니다.
개요 홈페이지에서 URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.search.windows.net
개요 홈페이지에서 엔드포인트를 볼 링크를 선택합니다. URL을 복사합니다. 엔드포인트의 예는 다음과 같습니다.
https://example.openai.azure.com/
로컬 개발을 위한 환경 변수 설정
.env
파일을 만듭니다.다음 환경 변수를
.env
파일에 추가하여 값을 사용자 고유의 서비스 엔드포인트 및 키로 바꿉니다.AZURE_SEARCH_ENDPOINT=<YOUR AZURE AI SEARCH ENDPOINT> AZURE_SEARCH_INDEX_NAME=hotels-sample-index AZURE_OPENAI_ENDPOINT=<YOUR AZURE OPENAI ENDPOINT> AZURE_OPENAI_VERSION=<YOUR AZURE OPENAI API VERSION> AZURE_DEPLOYMENT_MODEL=<YOUR DEPLOYMENT NAME>
Node.JS 프로젝트 설정
Visual Studio Code 및 TypeScript를 사용하여 프로젝트를 설정합니다.
새 디렉터리에서 Visual Studio Code를 시작합니다.
mkdir rag-quickstart && cd rag-quickstart code .
프로젝트 디렉터리에서 ESM 모듈에 대한 새 패키지를 만듭니다.
npm init -y npm pkg set type=module
그러면 기본값이 있는
package.json
파일이 만들어집니다.다음 npm 패키지를 설치합니다.
npm install @azure/identity @azure/search-documents openai dotenv @types/node
src
프로젝트 디렉터리에 디렉터리를 만듭니다.mkdir src
다음 콘텐츠를 사용하여
tsconfig.json
ESM용 프로젝트 디렉터리에 파일을 만듭니다.{ "compilerOptions": { "target": "esnext", "module": "NodeNext", "moduleResolution": "nodenext", "rootDir": "./src", "outDir": "./dist/", "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "strict": true, "skipLibCheck": true, "declaration": true, "sourceMap": true, "resolveJsonModule": true, "moduleDetection": "force", // Add this for ESM "allowSyntheticDefaultImports": true // Helpful for ESM interop }, "include": [ "src/**/*.ts" ] }
Azure에 로그인
연결에 Microsoft Entra ID 및 역할 할당을 사용하고 있습니다. Azure AI Search 및 Azure OpenAI와 동일한 테넌트 및 구독에 로그인했는지 확인합니다. 명령줄에서 Azure CLI를 사용하여 현재 속성을 표시하고, 속성을 변경하고, 로그인할 수 있습니다. 자세한 내용은 키 없이 연결을 참조 하세요.
다음 각 명령을 순서대로 실행합니다.
az account show
az account set --subscription <PUT YOUR SUBSCRIPTION ID HERE>
az login --tenant <PUT YOUR TENANT ID HERE>
이제 로컬 디바이스에서 Azure에 로그인해야 합니다.
쿼리 및 채팅 스레드 설정
Azure AI Search 인덱스 및 채팅 모델을 사용하여 접지 데이터를 기반으로 응답을 생성하는 쿼리 스크립트를 만듭니다. 다음 단계에서는 쿼리 스크립트를 설정하는 방법에 대해 설명합니다.
다음 코드를 사용하여
query.ts
디렉터리에src
파일을 만듭니다.// This is a RAG (Retrieval Augmented Generation) implementation that: // 1. Takes a user query about hotels // 2. Searches a hotel database using Azure AI Search // 3. Formats the search results for the LLM // 4. Sends the query and formatted results to Azure OpenAI // 5. Returns a grounded response based only on the retrieved information import { SearchClient, AzureKeyCredential, SearchDocumentsResult } from "@azure/search-documents"; import { AzureOpenAI } from "openai"; import { DefaultAzureCredential, getBearerTokenProvider } from "@azure/identity"; function getClients(): { openaiClient: AzureOpenAI, searchClient: SearchClient<{ HotelName: string; Description: string; Tags: string[] | string }>, modelName: string } { const credential = new DefaultAzureCredential(); // Search const azureSearchEndpoint = process.env.AZURE_SEARCH_ENDPOINT!; const azureSearchIndexName = process.env.AZURE_SEARCH_INDEX_NAME!; const searchClient = new SearchClient<{ HotelName: string; Description: string; Tags: string[] | string }>( azureSearchEndpoint, azureSearchIndexName, credential ); // OpenAI const azureOpenAiEndpoint = process.env.AZURE_OPENAI_ENDPOINT!; const azureOpenAiApiVersion = process.env.AZURE_OPENAI_VERSION!; const azureOpenAiDeploymentName = process.env.AZURE_DEPLOYMENT_MODEL!; const scope = "https://cognitiveservices.azure.com/.default"; const azureADTokenProvider = getBearerTokenProvider(credential, scope); const options = { azureADTokenProvider, deployment: azureOpenAiDeploymentName, apiVersion: azureOpenAiApiVersion, endpoint: azureOpenAiEndpoint } const openaiClient = new AzureOpenAI(options); return { openaiClient, searchClient, modelName: azureOpenAiDeploymentName }; } async function queryAISearchForSources(searchClient: SearchClient<{ HotelName: string; Description: string; Tags: string[] | string }>, query: string): Promise<string> { console.log(`Searching for: "${query}"\n`); const searchResults: SearchDocumentsResult<{ HotelName: string; Description: string; Tags: string[] | string }> = await searchClient.search(query, { top: 5, select: ["Description", "HotelName", "Tags"] }); const sources: string[] = []; for await (const result of searchResults.results) { const doc = result.document; sources.push( `Hotel: ${doc.HotelName}\n` + `Description: ${doc.Description}\n` + `Tags: ${Array.isArray(doc.Tags) ? doc.Tags.join(', ') : doc.Tags}\n` ); } const sourcesFormatted = sources.join("\n---\n"); return sourcesFormatted; } async function queryOpenAIForResponse( openaiClient: AzureOpenAI, query: string, sourcesFormatted: string, modelName: string ): Promise<{ choices: { message: { content: string | null } }[] }> { const GROUNDED_PROMPT = ` You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner. Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {query} Sources: {sources} `; return openaiClient.chat.completions.create({ model: modelName, messages: [ { role: "user", content: GROUNDED_PROMPT.replace("{query}", query).replace("{sources}", sourcesFormatted), } ], temperature: 0.7, max_tokens: 800, }); } async function main():Promise<void> { const { openaiClient, searchClient, modelName } = getClients(); const query = "Can you recommend a few hotels with complimentary breakfast?"; const sources = await queryAISearchForSources(searchClient, query); const response = await queryOpenAIForResponse(openaiClient, query, sources, modelName); // Print the response from the chat model const content = response.choices[0].message.content; if (content) { console.log(content); } else { console.log("No content available in the response."); } } main().catch((error) => { console.error("An error occurred:", error); process.exit(1); });
위의 코드는 다음을 수행합니다.
- Azure AI Search 및 Azure OpenAI에 필요한 라이브러리를 가져옵니다.
- 환경 변수를 사용하여 Azure AI Search 및 Azure OpenAI 클라이언트를 구성합니다.
- 구성에 환경 변수를 사용하여 Azure AI Search 및 Azure OpenAI에 대한 클라이언트를 가져오는 함수를 정의합니다.
- 사용자 쿼리를 기반으로 원본에 대한 Azure AI Search를 쿼리하는 함수를 정의합니다.
- 사용자 쿼리 및 Azure AI Search에서 검색된 원본에 따라 응답을 위해 Azure OpenAI를 쿼리하는 함수를 정의합니다.
- 함수는
main
검색 및 OpenAI 함수를 호출하여 흐름을 오케스트레이션한 다음 응답을 출력합니다.
JavaScript에 TypeScript 코드를 빌드합니다.
tsc
이 명령은 디렉터리의 TypeScript 코드를
src
컴파일하고 디렉터리에 JavaScript 파일을dist
출력합니다.터미널에서 다음 명령을 실행하여 쿼리 스크립트를 실행합니다.
node -r dotenv/config dist/query.js
.env
는-r dotenv/config
을 사용하여 런타임에 전달됩니다.출력은 여러 호텔에 대한 권장 사항으로 구성됩니다. 다음은 출력의 모양에 대한 예입니다.
Sure! Here are a few hotels that offer complimentary breakfast: - **Head Wind Resort** - Complimentary continental breakfast in the lobby - Free Wi-Fi throughout the hotel - **Double Sanctuary Resort** - Continental breakfast included - **White Mountain Lodge & Suites** - Continental breakfast available - **Swan Bird Lake Inn** - Continental-style breakfast each morning with a variety of food and drinks such as caramel cinnamon rolls, coffee, orange juice, milk, cereal, instant oatmeal, bagels, and muffins
문제 해결
사용할 수 없음 오류 메시지가 표시되면 Azure AI 검색 구성을 확인하여 역할 기반 액세스가 사용하도록 설정되어 있는지 확인합니다.
권한 부여 실패 오류 메시지가 표시되면 몇 분 정도 기다렸다가 다시 시도하세요. 역할 할당이 작동하려면 몇 분 정도 걸릴 수 있습니다.
리소스를 찾을 수 없는 오류 메시지가 표시되면 리소스 URI를 확인하고 채팅 모델의 API 버전이 유효한지 확인합니다.
그렇지 않은 경우 더 실험하려면 쿼리를 변경하고 마지막 단계를 다시 실행하여 모델이 기초 데이터를 사용하여 작동하는 방식을 더 잘 이해합니다.
프롬프트를 수정하여 출력의 톤이나 구조를 변경할 수도 있습니다.
쿼리 매개 변수 단계에서 use_semantic_reranker=False
를 설정하여 의미론적 순위 지정 없이 쿼리를 시도할 수도 있습니다. 의미론적 순위 지정은 쿼리 결과의 관련성과 LLM이 유용한 정보를 반환하는 기능을 눈에 띄게 개선할 수 있습니다. 실험을 통해 콘텐츠에 변화를 가져올지 여부를 결정할 수 있습니다.
복잡한 RAG 쿼리 보내기
Azure AI Search는 중첩된 JSON 구조에 대한 복합 형식 을 지원합니다. hotels-sample-index에서 , Address
, Address.StreetAddress
Address.City
및 Address.StateProvince
로 구성된 복합 형식의 Address.PostalCode
Address.Country
예입니다. 인덱스는 각 호텔에 대한 복잡한 컬렉션 Rooms
도 가지고 있습니다.
인덱스에 복합 형식이 있는 경우 서식 지정 지침을 포함하도록 프롬프트를 변경합니다.
Can you recommend a few hotels that offer complimentary breakfast?
Tell me their description, address, tags, and the rate for one room that sleeps 4 people.
디렉터리에 새 파일을
queryComplex.ts
src
만듭니다.파일에 다음 코드를 복사합니다.
// This is a RAG (Retrieval Augmented Generation) implementation that: // 1. Takes a user query about hotels // 2. Searches a hotel database using Azure AI Search // 3. Formats the search results for the LLM // 4. Sends the query and formatted results to Azure OpenAI // 5. Returns a grounded response based only on the retrieved information import { SearchClient, SearchDocumentsResult } from "@azure/search-documents"; import { AzureOpenAI } from "openai"; import { DefaultAzureCredential, getBearerTokenProvider } from "@azure/identity"; function getClients(): { openaiClient: AzureOpenAI; searchClient: SearchClient<{ HotelName: string; Description: string; Tags: string[] | string; Address: string; Rooms: string }>; modelName: string } { const credential = new DefaultAzureCredential(); // Search const azureSearchEndpoint = process.env.AZURE_SEARCH_ENDPOINT!; const azureSearchIndexName = process.env.AZURE_SEARCH_INDEX_NAME!; const searchClient = new SearchClient<{ HotelName: string; Description: string; Tags: string[] | string; Address: string; Rooms: string }>( azureSearchEndpoint, azureSearchIndexName, credential ); // OpenAI const azureOpenAiEndpoint = process.env.AZURE_OPENAI_ENDPOINT!; const azureOpenAiApiVersion = process.env.AZURE_OPENAI_VERSION!; const azureOpenAiDeploymentName = process.env.AZURE_DEPLOYMENT_MODEL!; const scope = "https://cognitiveservices.azure.com/.default"; const azureADTokenProvider = getBearerTokenProvider(credential, scope); const options = { azureADTokenProvider, deployment: azureOpenAiDeploymentName, apiVersion: azureOpenAiApiVersion, endpoint: azureOpenAiEndpoint } const openaiClient = new AzureOpenAI(options); return { openaiClient, searchClient, modelName: azureOpenAiDeploymentName }; } async function queryAISearchForSources( searchClient: SearchClient<{ HotelName: string; Description: string; Tags: string[] | string; Address: string; Rooms: string }>, query: string ): Promise<SearchDocumentsResult<{ HotelName: string; Description: string; Tags: string[] | string; Address: string; Rooms: string }>> { console.log(`Searching for: "${query}"\n`); const selectedFields: readonly ["HotelName", "Description", "Address", "Rooms", "Tags"] = ["HotelName", "Description", "Address", "Rooms", "Tags"]; const searchResults = await searchClient.search(query, { top: 5, select: selectedFields, queryType: "semantic", semanticSearchOptions: {}, }); return searchResults; } async function queryOpenAIForResponse( openaiClient: AzureOpenAI, query: string, sourcesFormatted: string, modelName: string ): Promise<{ choices: { message: { content: string | null } }[] }> { const GROUNDED_PROMPT = ` You are a friendly assistant that recommends hotels based on activities and amenities. Answer the query using only the sources provided below in a friendly and concise bulleted manner. Answer ONLY with the facts listed in the list of sources below. If there isn't enough information below, say you don't know. Do not generate answers that don't use the sources below. Query: {query} Sources: {sources} `; return openaiClient.chat.completions.create({ model: modelName, messages: [ { role: "user", content: GROUNDED_PROMPT.replace("{query}", query).replace("{sources}", sourcesFormatted), } ], temperature: 0.7, max_tokens: 800, }); } async function main(): Promise<void> { const { openaiClient, searchClient, modelName } = getClients(); const query = ` Can you recommend a few hotels that offer complimentary breakfast? Tell me their description, address, tags, and the rate for one room that sleeps 4 people. `; const sourcesResult = await queryAISearchForSources(searchClient, query); let sourcesFormatted = ""; for await (const result of sourcesResult.results) { // Explicitly typing result to ensure compatibility sourcesFormatted += JSON.stringify(result.document) + "\n"; } const response = await queryOpenAIForResponse(openaiClient, query, sourcesFormatted.trim(), modelName); // Print the response from the chat model const content = response.choices[0].message.content; if (content) { console.log(content); } else { console.log("No content available in the response."); } } main().catch((error) => { console.error("An error occurred:", error); process.exit(1); });
JavaScript에 TypeScript 코드를 빌드합니다.
tsc
이 명령은 디렉터리의 TypeScript 코드를
src
컴파일하고 디렉터리에 JavaScript 파일을dist
출력합니다.터미널에서 다음 명령을 실행하여 쿼리 스크립트를 실행합니다.
node -r dotenv/config dist/queryComplex.js
.env
는-r dotenv/config
을 사용하여 런타임에 전달됩니다.Azure OpenAI의 출력을 보고 복잡한 형식의 콘텐츠를 추가합니다.
Here are a few hotels that offer complimentary breakfast and have rooms that sleep 4 people:
1. **Head Wind Resort**
- **Description:** The best of old town hospitality combined with views of the river and
cool breezes off the prairie. Enjoy a complimentary continental breakfast in the lobby,
and free Wi-Fi throughout the hotel.
- **Address:** 7633 E 63rd Pl, Tulsa, OK 74133, USA
- **Tags:** Coffee in lobby, free Wi-Fi, view
- **Room for 4:** Suite, 2 Queen Beds (Amenities) - $254.99
2. **Double Sanctuary Resort**
- **Description:** 5-star Luxury Hotel - Biggest Rooms in the city. #1 Hotel in the area
listed by Traveler magazine. Free WiFi, Flexible check in/out, Fitness Center & espresso
in room. Offers continental breakfast.
- **Address:** 2211 Elliott Ave, Seattle, WA 98121, USA
- **Tags:** View, pool, restaurant, bar, continental breakfast
- **Room for 4:** Suite, 2 Queen Beds (Amenities) - $254.99
3. **Swan Bird Lake Inn**
- **Description:** Continental-style breakfast featuring a variety of food and drinks.
Locally made caramel cinnamon rolls are a favorite.
- **Address:** 1 Memorial Dr, Cambridge, MA 02142, USA
- **Tags:** Continental breakfast, free Wi-Fi, 24-hour front desk service
- **Room for 4:** Budget Room, 2 Queen Beds (City View) - $85.99
4. **Gastronomic Landscape Hotel**
- **Description:** Known for its culinary excellence under the management of William Dough,
offers continental breakfast.
- **Address:** 3393 Peachtree Rd, Atlanta, GA 30326, USA
- **Tags:** Restaurant, bar, continental breakfast
- **Room for 4:** Budget Room, 2 Queen Beds (Amenities) - $66.99
...
- **Tags:** Pool, continental breakfast, free parking
- **Room for 4:** Budget Room, 2 Queen Beds (Amenities) - $60.99
Enjoy your stay! Let me know if you need any more information.
오류 문제 해결
Azure SDK 오류를 디버그하려면 환경 변수 AZURE_LOG_LEVEL
를 다음 verbose
info
warning
error
중 하나로 설정합니다. 이렇게 하면 인증, 네트워크 연결 또는 기타 문제와 관련된 문제를 식별하는 데 도움이 되는 Azure SDK에 대한 자세한 로깅이 가능합니다.
쿼리 스크립트를 다시 실행합니다. 이제 SDK에서 출력으로 제공되는 정보 문구로 문제에 대한 자세한 정보를 얻을 수 있습니다.
ManagedIdentityCredential 및 토큰 획득 실패와 관련된 출력 메시지가 표시되면 테넌트가 여러 개 있고 Azure 로그인에서 검색 서비스가 없는 테넌트를 사용하고 있을 수 있습니다. 테넌트 ID를 가져오려면 Azure Portal에서 "테넌트 속성"을 검색하거나 az login tenant list
을(를) 실행합니다.
테넌트 ID가 있으면 명령 프롬프트에서 az login --tenant <YOUR-TENANT-ID>
을(를) 실행한 다음 스크립트를 다시 실행합니다.
정리
본인 소유의 구독으로 이 모듈을 진행하고 있는 경우에는 프로젝트가 끝날 때 여기에서 만든 리소스가 계속 필요한지 확인하는 것이 좋습니다. 계속 실행되는 리소스에는 요금이 부과될 수 있습니다. 리소스를 개별적으로 삭제하거나 리소스 그룹을 삭제하여 전체 리소스 세트를 삭제할 수 있습니다.
맨 왼쪽 창의 모든 리소스 또는 리소스 그룹 링크를 사용하여 Azure Portal에서 리소스를 찾고 관리할 수 있습니다.