Azure AI Search での取得拡張生成 (RAG)

取得拡張生成 (RAG) は、グラウンディング データを提供する情報取得システムを追加することで、ChatGPT などの大規模言語モデル (LLM) の機能を拡張するアーキテクチャです。 情報取得システムを追加すると、応答を作成するときに LLM によって使用されるグラウンディング データを制御できます。 エンタープライズ ソリューションの場合、RAG アーキテクチャは、ベクトル化されたドキュメントや画像、およびそのコンテンツの埋め込みモデルがある場合は、その他のデータ形式から取得された "エンタープライズ コンテンツ" に生成 AI を制限できることを意味します。

どの情報取得システムを使用するかによって LLM への入力が決定されるため、この決定は重要です。 情報取得システムは、次の情報を提供する必要があります。

  • 必要な頻度で、すべてのコンテンツに対して、大規模に読み込んで更新するインデックス作成戦略。

  • クエリ機能と関連性のチューニング。 システムは、関連する結果を、LLM 入力のトークンの長さの要件を満たすのに必要な短い形式で返す必要があります。

  • データと操作の両方のセキュリティ、グローバル展開、信頼性。

  • インデックス作成用の埋め込みモデル、および取得のためのチャット モデルまたは言語理解モデルとの統合。

Azure AI Search は、RAG アーキテクチャにおける 情報取得のための実証済みのソリューション です。 Azure クラウドのインフラストラクチャとセキュリティを備えたインデックス作成とクエリ機能を提供します。 コードやその他のコンポーネントを使用して、財産的価値のあるコンテンツに対する生成 AI のすべての要素を含む包括的な RAG ソリューションを設計できます。

Note

Copilot と RAG の概念は初めてですか? 「ベクトル検索と、生成 AI アプリの最新の取得」をご覧ください。

Microsoft は、RAG ソリューションで Azure AI Search を使用するためのいくつかの組み込み実装を用意しています。

キュレーションされたアプローチを使用すると、簡単に作業を開始できますが、アーキテクチャをより詳細に制御するには、カスタム ソリューションが必要です。 これらのテンプレートでは、以下でエンド ツー エンドのソリューションが作成されます。

この記事の残りの部分では、Azure AI Search がカスタム RAG ソリューションにどのように適合するかについて説明します。

パターンの大まかな概要は次のとおりです。

  • ユーザーの質問または要求 (プロンプト) から始めます。
  • Azure AI Search に送信して、関連情報を見つけます。
  • 上位の検索結果を LLM に送信します。
  • LLM の自然言語理解と推論機能を使用して、最初のプロンプトに対する応答を生成します。

Azure AI Search が LLM プロンプトに入力を提供しますが、モデルのトレーニングはしません。 RAG アーキテクチャでは、追加のトレーニングはありません。 LLM はパブリック データを使用して事前トレーニングされますが、取得コンポーネントからの情報によって拡張された応答を生成します。

Azure AI Search を含む RAG パターンには、次の図に示す要素があります。

検索と ChatGPT を使用した情報取得のアーキテクチャ図。

  • ユーザー エクスペリエンスのためのアプリ UX (Web アプリ)
  • アプリ サーバーまたはオーケストレーター (統合と調整レイヤー)
  • Azure AI Search (情報取得システム)
  • Azure OpenAI (生成 AI 用の LLM)

Web アプリはユーザー エクスペリエンスを提供し、プレゼンテーション、コンテキスト、ユーザー操作を提供します。 ユーザーからの質問またはプロンプトは、ここから始まります。 入力は統合レイヤーを通過します。最初に情報を取得して検索結果を取得しますが、さらに LLM に移動してコンテキストと意図を設定します。

アプリ サーバーまたはオーケストレーターは、情報の取得と LLM の間のハンドオフを調整する統合コードです。 1 つのオプションは、LangChain を使用してワークフローを調整することです。 LangChain は Azure AI Search と統合されるため、Azure AI Search を 取得コンポーネント としてワークフローに簡単に含めることができます。 セマンティック カーネルも別のオプションです。

情報取得システムは、検索可能なインデックス、クエリ ロジック、ペイロード (クエリ応答) を提供します。 検索インデックスには、ベクトルまたはベクトル以外のコンテンツを含めることができます。 ほとんどのサンプルとデモにはベクトル フィールドが含まれていますが、必須ではありません。 クエリは、キーワード (または用語) とベクトル クエリを処理できる Azure AI Search の既存の検索エンジンを使用して実行されます。 インデックスは、定義したスキーマに基づいて事前に作成され、ファイル、データベース、またはストレージからソース化されたコンテンツと共に読み込まれます。

LLM は、元のプロンプトに加えて、Azure AI Search からの結果を受け取ります。 LLM は結果を分析し、応答を作成します。 LLM が ChatGPT の場合、ユーザーの対話は会話のやり取りである可能性があります。 Davinci を使用している場合、プロンプトは完全に構成された回答である可能性があります。 Azure ソリューションでは Azure OpenAI が使用される可能性が最も高いですが、この特定のサービスに対するハードな依存関係はありません。

Azure AI 検索では、プロンプト フローやチャットの保持のためのネイティブ LLM 統合は提供されないため、オーケストレーションと状態を処理するコードを記述する必要があります。 完全なソリューションに必要なブループリントについては、デモ ソース (Azure-Samples/azure-search-openai-demo) を確認できます。 また、LLM と統合する RAG ベースの Azure AI 検索ソリューションを作成するには、Azure AI Studio または Azure OpenAI Studio を使用することをお勧めします。

Azure AI Search では、検索可能なすべてのコンテンツは、検索サービスでホストされている検索インデックスに格納されます。 検索インデックスは、応答時間がミリ秒レベルの高速なクエリを実現するために設計されているため、内部データ構造はその目標をサポートするために存在します。 そのため、検索インデックスにはインデックス付きコンテンツが保存されます。コンテンツ ファイル全体 (PDF 全体や画像など) は保存されません。 内部では、データ構造にはトークン化されたテキストの逆インデックス、埋め込み用のベクトル インデックス、逐語的一致が必要な場合 (フィルター、あいまい検索、正規表現クエリなど) の変更されていないテキストが含まれます。

RAG ソリューションのデータを設定するときは、Azure AI Search でインデックスを作成して読み込む機能を使用します。 インデックスには、ソース コンテンツを複製または表すフィールドが含まれます。 インデックス フィールドは単純な転送 (ソース ドキュメントのタイトルまたは説明が検索インデックスのタイトルまたは説明になる) であるか、画像の表現またはテキストの説明を生成するベクトル化やスキル処理などの外部プロセスの出力を含む場合があります。

検索するコンテンツの種類をご存知のことと思われるので、各コンテンツ タイプに適用できるインデックス作成機能を検討します。

コンテンツ タイプ 付けられたインデックス 機能
text トークン、変更されていないテキスト インデクサーは、Azure Storage や Cosmos DB などの他の Azure リソースからプレーンテキストをプルできます。 インデックスに任意の JSON コンテンツをプッシュすることもできます。 処理中のテキストを変更するには、アナライザーノーマライザーを使用して、インデックス作成中に字句処理を追加します。 同意語マップは、クエリで使用される可能性のある用語がソース ドキュメントにない場合に便利です。
text ベクトル 1 テキストをチャンクして外部でベクトル化し、インデックスにベクトル フィールドとしてインデックスを付けることができます。
image トークン、変更されていないテキスト 2 OCR と画像解析のスキルでは、テキスト認識やイメージ特性のために画像を処理できます。 画像情報は検索可能なテキストに変換され、インデックスに追加されます。 スキルにはインデクサーの要件があります。
image ベクトル 1 画像は、画像コンテンツを数学的に表現するために外部でベクトル化し、インデックスにベクトル フィールドとしてインデックスを付けることができます。 OpenAI CLIP などのオープン ソース モデルを使用して、同じ埋め込み空間内のテキストと画像をベクトル化できます。

1ベクトル サポートの一般提供機能では、データ チャンクとベクトル化のために他のライブラリまたはモデルを呼び出す必要があります。 しかし、垂直統合 (プレビュー) にはこれらの手順が埋め込まれています。 両方のアプローチを示すコード サンプルについては、azure-search-vector リポジトリを参照してください。

2スキルAI エンリッチメントの組み込みサポートです。 OCR と画像分析の場合、インデックス作成パイプラインは Azure AI Vision API の内部呼び出しを行います。 これらのスキルは、抽出された画像を処理のために Azure AI に渡し、Azure AI Search によってインデックス付けされたテキストとして出力を 受け取ります。

ベクトルは、異なるコンテンツ (複数のファイル形式と言語) に最適な設備を提供します。これは、コンテンツが数学表現で汎用的に表現されるためです。 また、ベクトルでは類似性検索もサポートされています。つまり、ベクトル クエリに最も似た座標で照合します。 トークン化された用語で照合するキーワード検索 (または用語検索) と比較すると、類似性検索の方が微妙です。 コンテンツまたはクエリにあいまいな点や解釈の要件がある場合は、より適切な選択肢です。

データが検索インデックスに格納されたら、Azure AI Search のクエリ機能を使用してコンテンツを取得します。

RAG 以外のパターンでは、クエリは検索クライアントからラウンド トリップを行います。 クエリが送信され、検索エンジンで実行され、応答がクライアント アプリケーションに返されます。 応答 (検索結果) は、インデックス内で見つかった逐語的なコンテンツのみで構成されます。

RAG パターンでは、検索エンジンと LLM の間でクエリと応答が調整されます。 ユーザーの質問またはクエリは、検索エンジンと LLM の両方にプロンプトとして転送されます。 検索結果は検索エンジンから戻り、LLM にリダイレクトされます。 ユーザーに返される応答は生成 AI で、LLM からの合計または回答のどちらかです。

Azure AI Search には、新しい回答を構成するクエリの種類はありません (セマンティック検索やベクトル検索でさえも)。 LLM だけが生成 AI を提供します。 クエリの作成に使用される Azure AI Search の機能を次に示します。

クエリ機能 目的 使用する理由
単純または完全な Lucene 構文 テキストと非ベクトル数値コンテンツに対するクエリ実行 フルテキスト検索は、類似一致ではなく、完全一致に最適です。 フルテキスト検索クエリは、BM25 アルゴリズムを使用してランク付けされ、スコアリング プロファイルによる関連性チューニングをサポートします。 また、フィルターとファセットもサポートされています。
フィルターファセット テキストまたは数値 (非ベクトル) フィールドにのみ適用されます。 包含条件または除外条件に基づいて検索対象領域を減らします。 クエリに精度を追加します。
セマンティック ランク付け セマンティック モデルを使用して BM25 結果セットを再ランク付けします。 LLM 入力として役立つ短い形式のキャプションと回答を生成します。 スコアリング プロファイルよりも簡単で、コンテンツによっては、関連性チューニングのためのより信頼性の高い手法です。
ベクトル検索 クエリ文字列が 1 つ以上のベクトルである類似性検索のベクトル フィールドに対するクエリ実行。 ベクトルは、あらゆる種類のコンテンツを任意の言語で表すことができます。
ハイブリッド検索 上記のクエリ手法の一部またはすべてを組み合わせます。 ベクトルおよびと非ベクトル クエリは並列で実行され、統合された結果セットで返されます。 ハイブリッド クエリを使用した場合、精度とリコールにおけるメリットが最も多くなります。

クエリ応答を構造化する

クエリの応答は LLM に入力を提供するため、検索結果の品質は成功に不可欠です。 結果は表形式の行セットです。 結果の構成や構造は次に依存します。

  • 応答に含まれるインデックスの部分を決定するフィールド。
  • インデックスにおける一致を示す行。

フィールドは、属性が "取得可能" である場合に検索結果に表示されます。 インデックス スキーマ内のフィールド定義には属性があり、これがフィールドが応答で使用されるかどうかを決定します。 "取得可能" フィールドだけがフル テキスト クエリまたはベクトル クエリ結果で返されます。 既定では、すべての "取得可能" フィールドが返されますが、"選択" を使用してサブセットを指定できます。 "取得可能" 以外に、フィールドに制限はありません。 フィールドには、任意の長さまたは型を指定できます。 長さについて、Azure AI Search にはフィールド長の上限はありませんが、API 要求のサイズには制限があります。

行ではクエリとの一致が、関連性、類似性、またはその両方でランク付けされます。 既定では、結果はフル テキスト検索の場合は上位 50 件、ベクトル検索の場合は K ニアレスト ネイバーに制限されます。 既定値を変更して制限を (最大 1000 ドキュメントまで) 増減できます。 top および skip ページング パラメーターを使用して、結果を一連のページングされた結果として取得することもできます。

関連性でランク付けする

複雑なプロセスや大量のデータを処理する場合や、ミリ秒レベルの応答が期待されている場合、各ステップで価値を高め、最終的な結果の品質を向上させることが重要です。 情報取得側において、関連性のチューニングは、LLM に送信される結果の品質を向上させるアクティビティです。 結果には、最も関連性の高い、または最も類似した一致するドキュメントだけを含める必要があります。

関連性は、キーワード (非ベクトル) 検索とハイブリッド クエリ (非ベクトル フィールドに対する) に適用されます。 Azure AI Search では、類似性検索とベクトル クエリの関連性のチューニングはありません。 BM25 ランク付けは、フル テキスト検索のランク付けアルゴリズムです。

関連性のチューニングは、BM25 ランク付けを強化する機能によってサポートされます。 これらのアプローチには、次が含まれます。

  • スコアリング プロファイル。これは、特定の検索フィールドまたはその他の条件で一致が見つかった場合に検索スコアを向上させます。
  • セマンティックランク付け。これは、Bing のセマンティック モデルを使用して結果を並べ替え、元のクエリに合わせてセマンティックに適合するように BM25 結果セットを再ランク付けします。

比較テストおよびベンチマーク テストでは、テキストフィールドとベクトル フィールドを含むハイブリッド クエリに、BM25 ランクの結果に対するセマンティック ランク付けを補足することで、最も関連性の高い結果が生成されます。

RAG シナリオの Azure AI Search クエリのコード例

次のコードは、デモ サイトからの retrievethenread.py ファイルからコピーされます。 ハイブリッド クエリの検索結果から LLM の content が生成されます。 より簡単なクエリを記述できますが、この例では、セマンティック再ランク付けとスペル チェックを使用したベクトル検索とキーワード検索が含まれています。 デモでは、このクエリを使用して初期コンテンツを取得します。

# Use semantic ranker if requested and if retrieval mode is text or hybrid (vectors + text)
if overrides.get("semantic_ranker") and has_text:
    r = await self.search_client.search(query_text,
                                  filter=filter,
                                  query_type=QueryType.SEMANTIC,
                                  query_language="en-us",
                                  query_speller="lexicon",
                                  semantic_configuration_name="default",
                                  top=top,
                                  query_caption="extractive|highlight-false" if use_semantic_captions else None,
                                  vector=query_vector,
                                  top_k=50 if query_vector else None,
                                  vector_fields="embedding" if query_vector else None)
else:
    r = await self.search_client.search(query_text,
                                  filter=filter,
                                  top=top,
                                  vector=query_vector,
                                  top_k=50 if query_vector else None,
                                  vector_fields="embedding" if query_vector else None)
if use_semantic_captions:
    results = [doc[self.sourcepage_field] + ": " + nonewlines(" . ".join([c.text for c in doc['@search.captions']])) async for doc in r]
else:
    results = [doc[self.sourcepage_field] + ": " + nonewlines(doc[self.content_field]) async for doc in r]
content = "\n".join(results)

統合コードと LLM

Azure AI Search を含む RAG ソリューションでは、完全なソリューションを作成するために、他のコンポーネントとコードが必要です。 前のセクションでは、Azure AI Search を使用した情報の取得と、検索可能なコンテンツの作成とクエリに使用される機能について説明しましたが、このセクションでは LLM の統合と相互作用について説明します。

デモ リポジトリ内のノートブックは、LLM に検索結果を渡すためのパターンを示しているため、出発点として最適です。 RAG ソリューションのほとんどのコードは LLM の呼び出しで構成されているため、この記事の範囲外であるこれらの API のしくみを理解する必要があります。

chat-read-retrieve-read.ipynb ノートブックの次のセル ブロックは、チャット セッションのコンテキストでの検索呼び出しを示しています。

# Execute this cell multiple times updating user_input to accumulate chat history
user_input = "Does my plan cover annual eye exams?"

# Exclude category, to simulate scenarios where there's a set of docs you can't see
exclude_category = None

if len(history) > 0:
    completion = openai.Completion.create(
        engine=AZURE_OPENAI_GPT_DEPLOYMENT,
        prompt=summary_prompt_template.format(summary="\n".join(history), question=user_input),
        temperature=0.7,
        max_tokens=32,
        stop=["\n"])
    search = completion.choices[0].text
else:
    search = user_input

# Alternatively simply use search_client.search(q, top=3) if not using semantic ranking
print("Searching:", search)
print("-------------------")
filter = "category ne '{}'".format(exclude_category.replace("'", "''")) if exclude_category else None
r = search_client.search(search, 
                         filter=filter,
                         query_type=QueryType.SEMANTIC, 
                         query_language="en-us", 
                         query_speller="lexicon", 
                         semantic_configuration_name="default", 
                         top=3)
results = [doc[KB_FIELDS_SOURCEPAGE] + ": " + doc[KB_FIELDS_CONTENT].replace("\n", "").replace("\r", "") for doc in r]
content = "\n".join(results)

prompt = prompt_prefix.format(sources=content) + prompt_history + user_input + turn_suffix

completion = openai.Completion.create(
    engine=AZURE_OPENAI_CHATGPT_DEPLOYMENT, 
    prompt=prompt, 
    temperature=0.7, 
    max_tokens=1024,
    stop=["<|im_end|>", "<|im_start|>"])

prompt_history += user_input + turn_suffix + completion.choices[0].text + "\n<|im_end|>" + turn_prefix
history.append("user: " + user_input)
history.append("assistant: " + completion.choices[0].text)

print("\n-------------------\n".join(history))
print("\n-------------------\nPrompt:\n" + prompt)

ファースト ステップ

  • インデックス作成の概念と戦略を確認して、データを取り込む方法と更新方法を決定します。 ベクトル検索、キーワード検索、ハイブリッド検索のどれを使用するかを決定します。 検索する必要があるコンテンツの種類と実行するクエリの種類によって、インデックスの設計が決まります。

  • クエリの作成について確認して、検索要求の構文と要件の詳細について確認します。

Note

一部の Azure AI Search 機能は人による操作を目的としており、RAG パターンでは役に立ちません。 具体的には、オートコンプリートと候補をスキップできます。 ファセットや orderby などの他の機能は役立つ可能性がありますが、RAG シナリオでは一般的ではありません。

関連項目