親子インデックス作成用のインデックス プロジェクションを定義する

メモ

Azure AI 検索は、Azure ポータルREST APIおよびAzure SDKから使用できます。 また、Foundry IQ は、エンタープライズ コンテンツを、Microsoft Foundry ポータルのエージェントの再利用可能なアクセス許可に対応したナレッジ ベースに変換するマネージド ナレッジ レイヤーです。

RAG パターンまたはベクター化のコンテンツをチャンクする場合は、1 対多のインデックス作成を制御するインデックス プロジェクションを指定できます。ソース コンテンツ (1 つ) は 1 つ以上のインデックス (多) に投影されます。 インデックス プロジェクションの目的は、親ドキュメントの要素 (ファイル名や作成日など) を制御することです。

  • 1 つのインデックス内の子 (チャンク) ごとに繰り返します
  • 同じインデックス内で独立した検索ドキュメントとしてインデックスされる
  • または、別のインデックスに取り込まれます

ドキュメントの図形が異なるか、コンテンツを 2 つのインデックスに分割するとクエリが困難になる可能性があるため、特にインデックス結合がサポートされていない従来の検索では、親フィールドを 1 つのインデックスで繰り返することをお勧めします。

Azure AI 検索では、チャンクはスキルによって実行されるため、インデクサーに依存します。 インデックス プロジェクションを定義するには、 スキルセットで指定します。

前提 条件

スキルセットには、一対多のインデックス作成のためにデータを作成するインデクサー プロジェクションが含まれています。 また、シナリオに統合ベクター化が含まれている場合は、 AzureOpenAIEmbedding などの埋め込みスキルなど、スキルセットに他のスキルを持つこともできます。

アプローチを選択する

インデックス プロジェクションでは、"親" ドキュメントごとに "子" ドキュメント (チャンク) が生成されます。 親コンテンツの処理方法を選択します。

アプローチ 説明 構成
単一インデックス、繰り返し親フィールド (推奨) 親フィールドはチャンクごとに繰り返されます。 すべてのドキュメントの形状は統一されています。 インデクサー targetIndexName とインデックス プロジェクション targetIndexName の両方を同じインデックスに設定します。 projectionModeskipIndexingParentDocumentsに設定します。
単一インデックス、混合ドキュメント図形 親ドキュメントとチャンク ドキュメントが共存します。 親ドキュメントには null チャンク フィールドがあります。 両方の targetIndexName 値を同じインデックスに設定します。 projectionModeincludeIndexingParentDocumentsに設定します(既定値として省略可能です)。
2 つ以上の独立したインデックス メタデータ参照の親インデックス、検索用の子インデックス。 クエリ実行時の結合はありません。 インデクサー targetIndexName を親インデックスに設定します。 インデックス プロジェクション targetIndexName を子インデックスに設定します。 selectors配列は、子インデックスの数量と構成を決定します。

ほとんどの RAG シナリオでは、最初のアプローチを使用します。 classic RAG の例を参照してください。

  1. 親フィールドを含むチャンク用に設計されたインデックスを作成します。
  2. チャンク スキルおよび を使用して、indexProjectionsします。
  3. サポートされているデータ ソースを指すインデクサーを作成します。

データ ソースで変更の追跡がサポートされている場合、インデクサーは変更を自動的に同期します。

一対多インデックス作成用のインデックスを作成する

親値を繰り返すチャンクに対して 1 つのインデックスを作成する場合でも、親子フィールド配置用に個別のインデックスを作成する場合でも、検索に使用されるプライマリ インデックスはデータ チャンクを中心に設計されています。 インデックス スキーマには、次のフィールドが必要です。

  • 各ドキュメントを一意に識別するドキュメント キー フィールド。 Edm.String アナライザーでkeyword型として定義する必要があります。

  • 各チャンクを親に関連付けするフィールド。 Edm.String型である必要があります。 ドキュメント キー フィールドにすることはできません。また、 filterable を true に設定する必要があります。 この例では、parent_idと呼ばれ、この記事では 予測されるキー値 と呼ばれます。

  • テキストやベクター化されたチャンク フィールドなど、コンテンツのその他のフィールド。

スキルセットを作成またはインデクサーを実行する前に、検索サービスにインデックスが存在している必要があります。 スキルセットで定義する selectors には、これらのフィールドが含まれている必要があります。

親フィールドと子フィールドを含む単一インデックス スキーマ

各チャンクに対して親コンテンツが繰り返されるチャンクを中心に設計された 1 つのインデックスは、RAG およびベクター検索シナリオの主要なパターンです。 正しい親コンテンツを各チャンクに関連付ける機能は、インデックス プロジェクションによって有効になります。

次のスキーマは、インデックス プロジェクションの要件を満たす例です。 この例では、次の操作を行います。

  • 親フィールドはparent_idとタイトルであり、チャンクごとに繰り返されます
  • 子フィールドは、ベクトルと非ベクトルのチャンクです。 chunk_idは、このインデックスのドキュメント ID です。

Azure ポータル、REST API、またはAzure SDKを使用して、インデックスを作成できます。

インデックスを作成するには、REST クライアントまたは Azure ポータル add index アクションと JSON オプションを使用します。

{
    "name": "my_consolidated_index",
    "fields": [
        {"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
        {"name": "parent_id", "type": "Edm.String", "filterable": true},
        {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
        {"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
        {"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
    ],
    "vectorSearch": {
        "algorithms": [{"name": "hnsw", "kind": "hnsw", "hnswParameters": {}}],
        "profiles": [{"name": "hnsw", "algorithm": "hnsw"}]
    }
}

スキルセットにインデックス プロジェクションを追加する

インデックス プロジェクションはスキルセット定義内で定義され、主に selectorsの配列として定義されます。各セレクターは、検索サービス上の異なるターゲット インデックスに対応します。 このセクションでは、コンテキストの構文と例から始まり、その後に パラメーター参照を続けて説明します

インデックス プロジェクションは一般提供されています。 最新の安定した API をお勧めします。

テキスト分割スキルによって出力された個々のページを検索インデックス内の独自のドキュメントとして射影するために使用できるインデックス プロジェクション定義のペイロードの例を次に示します。

親ドキュメントにドキュメント レベルのアクセス許可メタデータ ( metadata_user_idsmetadata_group_idsmetadata_spo_site_urlなど) が含まれている場合は、それらのフィールドを mappingsに含めます。 クエリ時のアクセス許可フィルターを適用するには、すべてのチャンクがそれらを継承する必要があります。 詳細については、「 ACL フィールドを設定する場所を選択する」を参照してください。

"indexProjections": {
    "selectors": [
        {
            "targetIndexName": "my_consolidated_index",
            "parentKeyFieldName": "parent_id",
            "sourceContext": "/document/pages/*",
            "mappings": [
                {
                    "name": "chunk",
                    "source": "/document/pages/*",
                    "sourceContext": null,
                    "inputs": []
                },
                {
                    "name": "chunk_vector",
                    "source": "/document/pages/*/chunk_vector",
                    "sourceContext": null,
                    "inputs": []
                },
                {
                    "name": "title",
                    "source": "/document/title",
                    "sourceContext": null,
                    "inputs": []
                }
            ]
        }
    ],
    "parameters": {
        "projectionMode": "skipIndexingParentDocuments"
    }
}

パラメーター参照

インデックス プロジェクション パラメーター 定義
selectors メイン検索コーパス (通常はチャンクを中心に設計されたインデックス) のパラメーターを持つ配列。 複数のセレクターを指定することで、複数の子インデックスにコンテンツを送信できます。 インデクサーを実行する前に、検索サービスにインデックス スキーマが存在している必要があります。
parameters インデックス プロジェクション固有の構成プロパティのパラメーター ディクショナリ。

パラメーターには、定義の一部として次の要素があります。

パラメーター 定義
parameters.projectionMode インデクサーに命令を提供する省略可能なパラメーター。 有効な値には、 includeIndexingParentDocumentsskipIndexingParentDocumentsが含まれます。

このパラメーターの最適な値は skipIndexingParentDocumentsです。 チャンクされたドキュメントが主要な検索ターゲットである場合に使用する必要があります。

skipIndexingParentDocumentsprojectionModeを設定しない場合は、既定値であるため、includeIndexingParentDocuments自動的に取得されます。 インデックスには、追加の検索ドキュメントが追加され、チャンクに対しては null のままですが、親固有のコンテンツで埋められています。 たとえば、5 つの PDF がインデックスに 100 個のチャンクを提供する場合、インデックス内のドキュメントの数は 105 になります。 親フィールド用に作成された 5 つのドキュメントは、チャンク (子) フィールドに対して null 値を持ち、インデックス内のドキュメントの大部分とは大きく異なります。 このため、projectionModeskipIndexingParentDocuments に設定することをお勧めします。

セレクターには、定義の一部として次の要素があります。

セレクター 定義
selectors.targetIndexName インデックス データが投影されるインデックスの名前。 これは、親フィールドを繰り返す単一のチャンク インデックスか、親子コンテンツに 個別のインデックス を使用している場合は子インデックスです。
selectors.parentKeyFieldName 親ドキュメントのキーを提供するフィールドの名前。
selectors.sourceContext 個々の検索ドキュメントにデータをマップする粒度を定義するエンリッチメント注釈。 詳細については、「 スキル コンテキストと入力注釈言語」を参照してください。
selectors.mappings 検索インデックス内のフィールドへのエンリッチされたデータのマッピングの配列。 各マッピングは次で構成されます。
name: データのインデックスを作成する検索インデックス内のフィールドの名前。
source: データを取得すべきエンリッチメントの注釈パス。

mappingでは、sourceContextinputs と同様に、オプションのフィールドと フィールドを使用してデータを再帰的に定義することもできます。 これらのパラメーターを使用すると、アプリケーションに応じて、検索インデックス内の Edm.ComplexType 型のフィールドにデータを整形できます。 一部の LLM は検索結果で複合型を受け入れないので、使用している LLM によって複合型マッピングが役に立つかどうかが決まります。

mappings パラメーターは重要です。 ドキュメント キーや親 ID などの ID フィールドを除き、子インデックスのすべてのフィールドを明示的にマップする必要があります。

この要件は、Azure AI 検索の他のフィールド マッピング規則とは対照的です。 一部のデータ ソースの種類では、インデクサーは、同様の名前または既知の特性に基づいてフィールドを暗黙的にマップできます (たとえば、BLOB インデクサーは、既定のドキュメント キーとして一意のメタデータ ストレージ パスを使用します)。 ただし、インデクサー プロジェクションの場合は、リレーションシップの "多" 側のすべてのフィールド マッピングを明示的に指定する必要があります。

重要

親キー フィールドのフィールド マッピングを作成しないでください。 これにより、変更の追跡と同期されたデータ更新が中断されます。

フィールド マッピングを確認する

インデクサーは、3 種類のフィールド マッピングに関連付けられます。 インデクサーを実行する前に、フィールド マッピングを確認し、各型を使用するタイミングを把握します。

フィールド マッピング はインデクサーで定義され、ソース フィールドをインデックス フィールドにマップするために使用されます。 フィールド マッピングは、中間スキル処理手順なしで、ソースからデータをリフトアップし、インデックス作成に渡すデータ パスに使用されます。 通常、インデクサーは、同じ名前と種類のフィールドを自動的にマップできます。 明示的なフィールド マッピングは、不一致がある場合にのみ必要です。 一対多のインデックス作成とこれまでに説明したパターンでは、フィールド マッピングが必要ない場合があります。

出力フィールド マッピング はインデクサーで定義され、スキルセットによって生成されたエンリッチされたコンテンツをフィールドにメイン インデックスにマップするために使用されます。 チャンクはスキルによる作成 (テキスト分割) によってエンリッチされたコンテンツと見なされますが、チャンクやセレクター マッピングによって定義されるインデックス プロジェクションの出力フィールド マッピングは必要ありません。

Selectors.mappings はスキルセットで定義され、子インデックスのフィールドにマップされます。 子インデックスに親フィールドも含まれている場合 ( 統合インデックス ソリューションと同様)、コンテンツを含むすべてのフィールド (親レベルのタイトル フィールドを含む) に対してフィールド マッピングを設定する必要があります。これは、タイトルがチャンクされた各ドキュメントに表示されることを前提とします。 個別の親インデックスと子インデックスを使用している場合、セレクターには子レベルのフィールドに対するフィールド マッピングが必要です。

メモ

出力フィールド マッピングとセレクター マッピングはどちらも、強化されたドキュメント ツリー ノードをソース入力として受け入れます。 各ノードへのパスを指定する方法を知ることは、データ パスを設定するために不可欠です。 パス構文の詳細については、例として エンリッチされたノードスキルセット定義 へのパスの参照を参照するを参照してください。

インデクサーを実行する

データ ソース、インデックス、スキルセットを作成したら、 インデクサーを作成して実行する準備ができました。 この手順では、パイプラインを実行します。

処理の完了後に検索インデックスに対してクエリを実行して、ソリューションをテストできます。

コンテンツのライフサイクル

基になるデータ ソースに応じて、インデクサーは通常、継続的な変更の追跡と削除の検出を提供できます。 このセクションでは、データ更新に関連する一対多インデックス作成のコンテンツ ライフサイクルについて説明します。

変更の追跡と削除の検出を提供するデータ ソースの場合、インデクサー プロセスはソース データの変更を取得できます。 インデクサーとスキルセットを実行するたびに、スキルセットまたは基になるソース データが変更されると、インデックス プロジェクションが更新されます。 インデクサーによって取得されたすべての変更は、エンリッチメント プロセスを通じてインデックス内のプロジェクションに反映され、投影されたデータが元のデータ ソース内のコンテンツの現在の表現であることを確認します。 データ更新アクティビティは、チャンクごとに投影されたキー値でキャプチャされます。 基になるデータが変更されると、この値が更新されます。

メモ

インデックス プッシュ API を使用して、投影されたドキュメント内のデータを手動で編集できますが、この操作は避ける必要があります。 インデックスの手動更新は、次のパイプライン呼び出し時に上書きされます。ソース データ内のドキュメントが更新され、データ ソースで変更の追跡または削除の検出が有効になっていると仮定します。

更新されたコンテンツ

データ ソースに新しいコンテンツを追加すると、次のインデクサーの実行時に新しいチャンクまたは子ドキュメントがインデックスに追加されます。

データ ソース内の既存のコンテンツを変更する場合、使用しているデータ ソースが変更の追跡と削除の検出をサポートしている場合、チャンクは検索インデックス内で増分更新されます。 たとえば、文書内の単語または文が変更された場合、その単語または文を含むターゲット インデックス内のチャンクは、次のインデクサー実行時に更新されます。 フィールドの種類や属性の変更など、他の種類の更新プログラムは、既存のフィールドではサポートされていません。 許可される更新の詳細については、「 インデックス スキーマの更新」を参照してください。

Azure Storage などの一部のデータ ソースでは、タイムスタンプに基づく変更と削除の追跡が既定でサポートされています。 Microsoft OneLakeAzure SQLAzure Cosmos DB などの他のデータ ソースは、変更の追跡用に構成する必要があります。

削除されたコンテンツ

ソース コンテンツが存在しなくなった場合 (たとえば、テキストがチャンク数を減らすように短縮された場合)、検索インデックス内の対応する子ドキュメントが削除されます。 残りの子ドキュメントでは、コンテンツが変更されていなくても、新しいハッシュ値を含むようにキーが更新されます。

親ドキュメントがデータソースから完全に削除された場合、対応する子ドキュメントは、データソース定義で定義された dataDeletionDetectionPolicy によって削除が検出された場合にのみ削除されます。 dataDeletionDetectionPolicyが構成されておらず、親ドキュメントをデータソースから削除する必要がある場合は、不要になった子ドキュメントを手動で削除する必要があります。

予測キーの値

更新および削除されたコンテンツのデータ整合性を確保するために、一対多インデックス作成でのデータ更新は、"多" 側の 投影キー値 に依存します。 統合ベクター化または データのインポート ウィザードを使用している場合、投影されるキー値は、インデックスのチャンクまたは "多" 側の parent_id フィールドです。

投影キー値は、インデクサーがドキュメントごとに生成する一意の識別子です。 これにより、一意性が確保され、変更と削除の追跡が正しく機能します。 このキーには、次のセグメントが含まれています。

  • 一意性を保証するランダム ハッシュ。 このハッシュは、後続のインデクサーの実行時に親ドキュメントが更新された場合に変更されます。
  • 親ドキュメントのキー。
  • 生成されたドキュメントのコンテキストを識別するエンリッチメント注釈パス。

たとえば、キー値 "aa1b22c33" を持つ親ドキュメントを 4 ページに分割し、それらの各ページがインデックス プロジェクションを介して独自のドキュメントとして投影される場合は、次のようになります。

  • aa1b22c33
  • aa1b22c33_pages_0
  • aa1b22c33_pages_1
  • aa1b22c33_pages_2

ソース データで親ドキュメントが更新されると、チャンクされたページが増える可能性があり、ランダム ハッシュが変更され、ページが追加され、各チャンクの内容がソース ドキュメント内の内容と一致するように更新されます。

個別の親子インデックスの例

このセクションでは、個別の親インデックスと子インデックスの例を示します。 これは珍しいパターンですが、このアプローチを使用して最適に満たすアプリケーション要件がある可能性があります。 このシナリオでは、親子コンテンツを 2 つの個別のインデックスに投影します。

  1. 2 つのインデックス スキーマを作成します。

    各スキーマには特定のグレインのフィールドがあり、親 ID フィールドは両方のインデックスに共通して ルックアップ クエリで使用されます。 プライマリ検索コーパスは子インデックスですが、検索クエリを発行して、結果の一致ごとに親フィールドを取得できます。 Azure AI 検索はクエリ時の結合をサポートしていないため、アプリケーション コードまたはオーケストレーション レイヤーは、アプリまたはプロセスに渡すことができる結果をマージまたは照合する必要があります。

    親インデックスには、parent_idフィールドとタイトルがあります。 parent_idはドキュメント キーです。 親ドキュメント レベルでフィールドをベクター化しない限り、ベクター検索の構成は必要ありません。

    {
        "name": "my-parent-index",
        "fields": [
    
            {"name": "parent_id", "type": "Edm.String", "key":true, "filterable": true},
            {"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true}
        ]
    }
    

    子インデックスには、チャンクフィールドとparent_idフィールドがあります。 統合ベクター化、スコア付けプロファイル、セマンティック ランカー、またはアナライザーを使用している場合は、子インデックスでこれらを設定します。

    {
        "name": "my-child-index",
        "fields": [
            {"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
            {"name": "parent_id", "type": "Edm.String", "filterable": true},
             {"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
            {"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
        ],
        "vectorSearch": {
            "algorithms": [{"name": "hsnw", "kind": "hnsw", "hnswParameters": {}}],
            "profiles": [{"name": "hsnw", "algorithm": "hnsw"}]
        },
        "scoringProfiles": [],
        "semanticConfiguration": [],
        "analyzers": []
    }
    
  2. ターゲットとして親インデックスを指定するようにインデクサーを更新します。

    インデクサー定義では、パイプラインのコンポーネントを指定します。 インデクサー定義では、指定するインデックス名は親インデックスです。 親レベルのフィールドのフィールド マッピングが必要な場合は、outputFieldMappings で定義します。 個別のインデックスを使用する一対多インデックスの場合、インデクサー定義は次の例のようになります。

    {
      "name": "my-indexer",
      "dataSourceName": "my-ds",
      "targetIndexName": "my-parent-index",
      "skillsetName" : "my-skillset",
      "parameters": { },
      "fieldMappings": (optional) Maps fields in the underlying data source to fields in an index,
      "outputFieldMappings" : (required) Maps skill outputs to fields in an index,
    }
    
  3. スキルセットに indexProjections を追加します。

    インデクサーがコンテンツのインデックス作成に使用するデータ パスを指定するインデックス プロジェクション定義の例を次に示します。 インデックス プロジェクション定義で子インデックス名を指定し、すべての子またはチャンク レベルのフィールドのマッピングを指定します。 これは、子インデックス名が指定されている唯一の場所です。

    parametersは null であり、既定のincludeIndexingParentDocumentsを使用していることに注意してください。 インデクサーによって親インデックスが埋め込まれます。 selectors配列は、チャンク ドキュメントを子インデックスに投影するために使用されます。

    "indexProjections": {
        "selectors": [
            {
                "targetIndexName": "my-child-index",
                "parentKeyFieldName": "parent_id",
                "sourceContext": "/document/pages/*",
                "mappings": [
                    {
                        "name": "chunk",
                        "source": "/document/pages/*",
                        "sourceContext": null,
                        "inputs": []
                    },
                    {
                        "name": "chunk_vector",
                        "source": "/document/pages/*/chunk_vector",
                        "sourceContext": null,
                        "inputs": []
                    }
                ]
            }
        ],
        "parameters": {}
    }
    
  4. インデクサーを実行します。 インデクサーを以前に実行した場合は、最初にリセットすることを忘れないでください。

    2 つのインデックスに適切なコンテンツが設定されている必要があります。 Search Explorer でインデックスにクエリを実行して、それぞれが正しいコンテンツを持っているかどうかを確認します。

次の手順

データ チャンクと一対多インデックス作成は、Azure AI 検索の従来の RAG パターンの一部です。 詳細については、次のチュートリアルとコード サンプルに進んでください。

Azure AI 検索