このクイック スタートでは、Azure AI Search のインデックス付きコンテンツに対する会話検索エクスペリエンスのクエリをチャット完了モデルに送信します。 Azure portal で Azure OpenAI リソースと Azure AI Search リソースを設定した後、API を呼び出すコードを実行します。
前提条件
アクティブなサブスクリプションを持つ Azure アカウント。 無料でアカウントを作成できます。
-
- 使用するチャット完了モデル (gpt-4o、gpt-4o-mini、または同等のモデル) をサポートするリージョンを選択します。
- Azure AI Foundry でチャット補完モデルをデプロイするか、別の方法を使用します。
-
- Basic レベル以上を使用することをお勧めします。
- セマンティック ランク付けを有効にします。
Python 拡張機能と Jupyter パッケージを持つ Visual Studio Code。 詳細については、「Visual Studio Code での Python」を参照してください。
ファイルをダウンロードする
GitHub から Jupyter ノートブックをダウンロードして、このクイックスタートの要求を送信します。 詳細については、「GitHub からファイルをダウンロードする」を参照してください。
ローカル システムで新しいファイルを開始し、この記事の手順を使用して手動で要求を作成することもできます。
アクセスを構成する
検索エンドポイントへの要求は、認証および承認する必要があります。 このタスクには API キーまたはロールを使用できます。 キーの方が簡単に使い始めることができますが、安全性が高いのはロールの方です。 このクイックスタートでは、ロールを前提としています。
2 つのクライアントを設定するため、両方のリソースに対するアクセス許可が必要です。
Azure AI 検索は、ローカル システムからクエリ要求を受信します。 ホテル サンプルのインデックスが既に存在する場合は、検索インデックス データ閲覧者ロールの割り当てを自分に割り当てます。 存在しない場合は、Search Service 共同作成者と検索インデックス データ共同作成者のロールを自分に割り当てて、インデックスを作成してクエリを実行できるようにします。
Azure OpenAI は、ローカル システムからクエリと検索結果を受け取ります。 Azure OpenAI で Cognitive Services OpenAI ユーザー ロールを自分に割り当てます。
Azure portal にサインインします。
ロールベースのアクセス用に Azure AI 検索を構成します。
Azure portal で Azure AI 検索サービスを見つけます。
左側のメニューで、[設定]>[キー] を選択して、[ロールベースのアクセス制御] または [両方] のいずれかを選択します。
ロールを割り当てます。
左側のメニューで、[アクセス制御 (IAM)] を選択します。
Azure AI 検索で、検索インデックスを作成、読み込み、クエリを実行するために次のロールを選択し、それらを Microsoft Entra ID ユーザー ID に割り当てます。
- 検索インデックス データ共同作成者
- Search Service サービス貢献者
Azure OpenAI で、[アクセス制御 (IAM)] を選択して、このロールを Azure OpenAI で自分に割り当てます。
- Cognitive Services OpenAI ユーザー
アクセス許可が有効になるまで数分かかる場合があります。
インデックスを作成する
検索インデックスは、チャット モデルの基となるデータを提供します。 hotels-sample-index をお勧めします。これは数分で作成でき、任意の検索サービス レベルで実行されます。 このインデックスは、組み込みのサンプル データを使って作成します。
Azure portal で、ご利用の検索サービスを探します。
[概要] ホーム ページで、[データのインポート] を選択して、ウィザードを開始します。
[データへの接続] ページで、ドロップダウン リストから [サンプル] を選択します。
[hotels-sample] を選択します。
[次へ] を選んで残りのページに進み、既定値をそのまま使用します。
インデックスが作成されたら、左側のメニューの [検索管理]>[インデックス] を選択して、インデックスを開きます。
[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" } ] } } ] },
変更内容を保存します。
インデックスをテストするには、Search エクスプローラーで次のクエリを実行します:
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
を選択して現在のワークスペースに仮想環境を作成します。依存関係には Quickstart-RAG\requirements.txt を選択します。
環境の作成には数分かかります。 環境の準備ができたら、次の手順に進みます。
Azure へのサインイン
接続には Microsoft Entra ID とロールの割り当てを使用しています。 Azure AI 検索および 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
[Forbidden] エラー メッセージが表示された場合は、Azure AI Search 構成を確認し、ロールベースのアクセスが有効になっていることを確認してください。
[承認に失敗しました]エラー メッセージが表示された場合は、数分待ってからもう一度お試しください。 ロールの割り当てが操作可能になるまでに数分かかる場合があります。
"リソースが見つかりません" エラー メッセージが表示された場合は、リソース URI を確認し、チャット モデルの API バージョンが有効であることを確認します。
それ以外の場合、さらに実験するには、クエリを変更し、最後の手順を再実行して、モデルがグラウンディング データでどのように機能するかを理解します。
プロンプトを変更して、出力のトーンや構造を変更することもできます。
また、クエリ パラメーター ステップで
use_semantic_reranker=False
を設定して、セマンティック ランク付けなしにクエリを試すこともできます。 セマンティック ランク付けを使用すると、クエリ結果の関連性と、LLM が有用な情報を返す能力を著しく向上させることができます。 実験は、コンテンツに違いがあるかどうかを判断するのに役立ちます。
複雑な RAG クエリを送信する
Azure AI 検索は、入れ子になった 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 アカウント。 無料でアカウントを作成できます。
-
- 使用するチャット完了モデル (gpt-4o、gpt-4o-mini、または同等のモデル) をサポートするリージョンを選択します。
- Azure AI Foundry でチャット補完モデルをデプロイするか、別の方法を使用します。
-
- Basic レベル以上を使用することをお勧めします。
- セマンティック ランク付けを有効にします。
TypeScript。 npm を使用して TypeScript をグローバルにインストールできます。
npm install -g typescript
アクセスを構成する
検索エンドポイントへの要求は、認証および承認する必要があります。 このタスクには API キーまたはロールを使用できます。 キーの方が簡単に使い始めることができますが、安全性が高いのはロールの方です。 このクイックスタートでは、ロールを前提としています。
2 つのクライアントを設定するため、両方のリソースに対するアクセス許可が必要です。
Azure AI 検索は、ローカル システムからクエリ要求を受信します。 ホテル サンプルのインデックスが既に存在する場合は、検索インデックス データ閲覧者ロールの割り当てを自分に割り当てます。 存在しない場合は、Search Service 共同作成者と検索インデックス データ共同作成者のロールを自分に割り当てて、インデックスを作成してクエリを実行できるようにします。
Azure OpenAI は、ローカル システムからクエリと検索結果を受け取ります。 Azure OpenAI で Cognitive Services OpenAI ユーザー ロールを自分に割り当てます。
Azure portal にサインインします。
ロールベースのアクセス用に Azure AI 検索を構成します。
Azure portal で Azure AI 検索サービスを見つけます。
左側のメニューで、[設定]>[キー] を選択して、[ロールベースのアクセス制御] または [両方] のいずれかを選択します。
ロールを割り当てます。
左側のメニューで、[アクセス制御 (IAM)] を選択します。
Azure AI 検索で、検索インデックスを作成、読み込み、クエリを実行するために次のロールを選択し、それらを Microsoft Entra ID ユーザー ID に割り当てます。
- 検索インデックス データ共同作成者
- Search Service サービス貢献者
Azure OpenAI で、[アクセス制御 (IAM)] を選択して、このロールを Azure OpenAI で自分に割り当てます。
- Cognitive Services OpenAI ユーザー
アクセス許可が有効になるまで数分かかる場合があります。
インデックスを作成する
検索インデックスは、チャット モデルの基となるデータを提供します。 hotels-sample-index をお勧めします。これは数分で作成でき、任意の検索サービス レベルで実行されます。 このインデックスは、組み込みのサンプル データを使って作成します。
Azure portal で、ご利用の検索サービスを探します。
[概要] ホーム ページで、[データのインポート] を選択して、ウィザードを開始します。
[データへの接続] ページで、ドロップダウン リストから [サンプル] を選択します。
[hotels-sample] を選択します。
[次へ] を選んで残りのページに進み、既定値をそのまま使用します。
インデックスが作成されたら、左側のメニューの [検索管理]>[インデックス] を選択して、インデックスを開きます。
[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" } ] } } ] },
変更内容を保存します。
インデックスをテストするには、Search エクスプローラーで次のクエリを実行します:
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
次の内容を含む ESM のプロジェクト ディレクトリに
tsconfig.json
ファイルを作成します。{ "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 検索および 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 インデックスとチャット モデルを使用して、グラウンド データに基づいて応答を生成するクエリ スクリプトを作成します。 次の手順では、クエリ スクリプトを設定する手順について説明します。
次のコードを使用して、
src
ディレクトリにquery.ts
ファイルを作成します。// 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 関数を呼び出してフローを調整し、応答を出力します。
TypeScript コードを JavaScript にビルドします。
tsc
このコマンドは、
src
ディレクトリ内の TypeScript コードをコンパイルし、dist
ディレクトリに JavaScript ファイルを出力します。ターミナルで次のコマンドを実行して、クエリ スクリプトを実行します。
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
トラブルシューティング
[Forbidden] エラー メッセージが表示された場合は、Azure AI Search 構成を確認し、ロールベースのアクセスが有効になっていることを確認してください。
[承認に失敗しました]エラー メッセージが表示された場合は、数分待ってからもう一度お試しください。 ロールの割り当てが操作可能になるまでに数分かかる場合があります。
"リソースが見つかりません" エラー メッセージが表示された場合は、リソース URI を確認し、チャット モデルの API バージョンが有効であることを確認します。
それ以外の場合、さらに実験するには、クエリを変更し、最後の手順を再実行して、モデルがグラウンディング データでどのように機能するかを理解します。
プロンプトを変更して、出力のトーンや構造を変更することもできます。
また、クエリ パラメーター ステップで use_semantic_reranker=False
を設定して、セマンティック ランク付けなしにクエリを試すこともできます。 セマンティック ランク付けを使用すると、クエリ結果の関連性と、LLM が有用な情報を返す能力を著しく向上させることができます。 実験は、コンテンツに違いがあるかどうかを判断するのに役立ちます。
複雑な RAG クエリを送信する
Azure AI 検索は、入れ子になった 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.
src
ディレクトリに新しいファイルqueryComplex.ts
を作成します。次のコードをファイルにコピーします。
// 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); });
TypeScript コードを JavaScript にビルドします。
tsc
このコマンドは、
src
ディレクトリ内の TypeScript コードをコンパイルし、dist
ディレクトリに JavaScript ファイルを出力します。ターミナルで次のコマンドを実行して、クエリ スクリプトを実行します。
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 内でリソースを検索および管理できます。
前提条件
アクティブなサブスクリプションを持つ Azure アカウント。 無料でアカウントを作成できます。
-
- 使用するチャット完了モデル (gpt-4o、gpt-4o-mini、または同等のモデル) をサポートするリージョンを選択します。
- Azure AI Foundry でチャット補完モデルをデプロイするか、別の方法を使用します。
-
- Basic レベル以上を使用することをお勧めします。
- セマンティック ランク付けを有効にします。
アクセスを構成する
検索エンドポイントへの要求は、認証および承認する必要があります。 このタスクには API キーまたはロールを使用できます。 キーの方が簡単に使い始めることができますが、安全性が高いのはロールの方です。 このクイックスタートでは、ロールを前提としています。
2 つのクライアントを設定するため、両方のリソースに対するアクセス許可が必要です。
Azure AI 検索は、ローカル システムからクエリ要求を受信します。 ホテル サンプルのインデックスが既に存在する場合は、検索インデックス データ閲覧者ロールの割り当てを自分に割り当てます。 存在しない場合は、Search Service 共同作成者と検索インデックス データ共同作成者のロールを自分に割り当てて、インデックスを作成してクエリを実行できるようにします。
Azure OpenAI は、ローカル システムからクエリと検索結果を受け取ります。 Azure OpenAI で Cognitive Services OpenAI ユーザー ロールを自分に割り当てます。
Azure portal にサインインします。
ロールベースのアクセス用に Azure AI 検索を構成します。
Azure portal で Azure AI 検索サービスを見つけます。
左側のメニューで、[設定]>[キー] を選択して、[ロールベースのアクセス制御] または [両方] のいずれかを選択します。
ロールを割り当てます。
左側のメニューで、[アクセス制御 (IAM)] を選択します。
Azure AI 検索で、検索インデックスを作成、読み込み、クエリを実行するために次のロールを選択し、それらを Microsoft Entra ID ユーザー ID に割り当てます。
- 検索インデックス データ共同作成者
- Search Service サービス貢献者
Azure OpenAI で、[アクセス制御 (IAM)] を選択して、このロールを Azure OpenAI で自分に割り当てます。
- Cognitive Services OpenAI ユーザー
アクセス許可が有効になるまで数分かかる場合があります。
インデックスを作成する
検索インデックスは、チャット モデルの基となるデータを提供します。 hotels-sample-index をお勧めします。これは数分で作成でき、任意の検索サービス レベルで実行されます。 このインデックスは、組み込みのサンプル データを使って作成します。
Azure portal で、ご利用の検索サービスを探します。
[概要] ホーム ページで、[データのインポート] を選択して、ウィザードを開始します。
[データへの接続] ページで、ドロップダウン リストから [サンプル] を選択します。
[hotels-sample] を選択します。
[次へ] を選んで残りのページに進み、既定値をそのまま使用します。
インデックスが作成されたら、左側のメニューの [検索管理]>[インデックス] を選択して、インデックスを開きます。
[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" } ] } } ] },
変更内容を保存します。
インデックスをテストするには、Search エクスプローラーで次のクエリを実行します:
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 検索および 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 インデックスとチャット モデルを使用して、グラウンド データに基づいて応答を生成するクエリ スクリプトを作成します。 次の手順では、クエリ スクリプトを設定する手順について説明します。
次のコードを使用して、
src
ディレクトリにquery.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 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
トラブルシューティング
[Forbidden] エラー メッセージが表示された場合は、Azure AI Search 構成を確認し、ロールベースのアクセスが有効になっていることを確認してください。
[承認に失敗しました]エラー メッセージが表示された場合は、数分待ってからもう一度お試しください。 ロールの割り当てが操作可能になるまでに数分かかる場合があります。
"リソースが見つかりません" エラー メッセージが表示された場合は、リソース URI を確認し、チャット モデルの API バージョンが有効であることを確認します。
それ以外の場合、さらに実験するには、クエリを変更し、最後の手順を再実行して、モデルがグラウンディング データでどのように機能するかを理解します。
プロンプトを変更して、出力のトーンや構造を変更することもできます。
また、クエリ パラメーター ステップで use_semantic_reranker=False
を設定して、セマンティック ランク付けなしにクエリを試すこともできます。 セマンティック ランク付けを使用すると、クエリ結果の関連性と、LLM が有用な情報を返す能力を著しく向上させることができます。 実験は、コンテンツに違いがあるかどうかを判断するのに役立ちます。
複雑な RAG クエリを送信する
Azure AI 検索は、入れ子になった 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 内でリソースを検索および管理できます。