REST を使用してナレッジ ストアを作成する

Azure AI Search では、 ナレッジ ストア は、検索以外のシナリオで使用される AI によって生成されたコンテンツ のリポジトリです。 インデクサーとスキルセットを使用してナレッジ ストアを作成し、出力を保存する Azure Storage を指定します。 ナレッジ ストアにデータが取り込まれた後は、Storage ExplorerPower BI などのツールを使用してコンテンツを探索します。

この記事では、REST API を使用して、ナレッジ ストアにホテル宿泊の一連の顧客レビューを取り込み、エンリッチし、探索します。 ナレッジ ストアは、ソースから取得された元のテキスト コンテンツと AI によって生成されたコンテンツ (センチメント スコア、キー フレーズ抽出、言語検出、および非英語圏の顧客によるコメントのテキスト翻訳が含まれている) を含んでいます。

初期データ セットを使用できるようにするため、最初にホテルのレビューが Azure Blob Storage にインポートされます。 後処理で、結果が Azure Table Storage にナレッジ ストアとして保存されます。

ヒント

この記事では、REST を使用して各手順について詳しく説明します。 コマンドを実行するだけの場合は、REST ファイルをダウンロードします。 または、Azure portal でナレッジ ストアを作成することもできます。

前提条件

この例のスキルセットでは、エンリッチメントに Azure AI サービスを使用します。 ワークロードは非常に小さいので、1 日に最大 20 トランザクションの処理を無料で使うことができる Azure AI サービスを内部で利用しています。 ワークロードが小さいということは、Azure AI マルチサービス リソースの作成またはアタッチをスキップできることを意味します。

Azure Storage にデータをアップロードして接続文字列を取得する

  1. HotelReviews_Free.csv をダウンロードします。 この CSV には、1 つのホテルに関する 19 個のお客様のフィードバックが含まれています (ソースは Kaggle.com)。

  2. Azure portal でお使いのストレージ アカウントを探し、ストレージ ブラウザーを使用して hotel-reviews という名前の BLOB コンテナーを作成します。

  3. ページの上部にある [アップロード] を選択して、前の手順でダウンロードした HotelReviews-Free.csv ファイルを読み込みます。

    Screenshot of Storage Browser with uploaded file and left nav pane

  4. 左側で、[アクセス キー] を選択し、[キーの表示] を選択し、key1 または key2 の接続文字列をコピーします。 フル アクセス接続文字列は次のような形式です。

"knowledgeStore": {
    "storageConnectionString": "DefaultEndpointsProtocol=https;AccountName=<YOUR-ACCOUNT-NAME>;AccountKey=<YOUR-ACCOUNT-KEY>;EndpointSuffix=core.windows.net;"
}

Note

接続文字列の機密データを提供したくない場合は、「マネージド ID を使用して接続する」を参照してください。

キーと URL をコピーする

この例では、REST 呼び出しが検索サービス エンドポイントを必要とし、すべての要求で API キーを使用します。 これらの値は Azure portal から取得できます。

  1. Azure portal にサインインし、[概要] ページに移動して URL をコピーします。 たとえば、エンドポイントは https://mydemo.search.windows.net のようになります。

  2. [設定]>[キー] で管理者キーをコピーします。 管理者キーは、オブジェクトの追加、変更、削除で使用します。 2 つの交換可能な管理者キーがあります。 どちらかをコピーします。

    Screenshot of the URL and API keys in the Azure portal.

有効な API キーにより、要求を送信するアプリケーションとそれを処理する検索サービスとの間で、要求ごとに信頼が確立されます。

インデックスを作成する

[インデックスを作成する (REST)]: 検索サービスに検索インデックスを作成します。 検索インデックスはナレッジ ストアとは無関係ですが、これはインデクサーに必要です。 検索インデックスにはナレッジ ストアと同じコンテンツが含まれており、これは、クエリ要求を送信して調べることができます。

  1. Visual Studio Code で新しいテキスト ファイルを開きます。

  2. 事前に収集した検索エンドポイントと API キーに変数を設定します。

    @baseUrl = PUT-YOUR-SEARCH-SERVICE-URL-HERE
    @apiKey = PUT-YOUR-ADMIN-API-KEY-HERE
    @storageConnection = PUT-YOUR-STORAGE-CONNECTION-STRING-HERE
    @blobContainer = PUT-YOUR-CONTAINER-NAME-HERE (hotel-reviews)
    
  3. .rest ファイル拡張子でファイルを保存します。

  4. 次の例を貼り付けて、インデックス要求を作成します。

    ### Create a new index
    POST {{baseUrl}}/indexes?api-version=2023-11-01  HTTP/1.1
        Content-Type: application/json
        api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-idx",  
            "fields": [
                { "name": "name", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_date", "type": "Edm.DateTimeOffset", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_rating", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_text", "type": "Edm.String", "filterable": false,  "sortable": false, "facetable": false },
                { "name": "reviews_title", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "reviews_username", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false },
                { "name": "AzureSearch_DocumentKey", "type": "Edm.String", "searchable": false, "filterable": false, "sortable": false, "facetable": false, "key": true },
                { "name": "language", "type": "Edm.String", "filterable": true, "sortable": false, "facetable": true },
                { "name": "translated_text", "type": "Edm.String", "filterable": false, "sortable": false, "facetable": false },
                { "name": "sentiment", "type": "Collection(Edm.String)", "searchable": false, "filterable": true, "retrievable": true, "sortable": false, "facetable": true },
                { "name": "keyphrases", "type": "Collection(Edm.String)", "filterable": true, "sortable": false, "facetable": true }
            ]
        }
    
  5. [要求の送信] をクリックします。 HTTP/1.1 201 Created 応答が返され、その応答本文にはインデックス スキーマの JSON 表現が含まれているはずです。

データ ソースを作成する

[データ ソースの作成]。Azure AI 検索にデータ ソース接続を作成します。

  1. 次の例を貼り付けて、データ ソースを作成します。

    ### Create a data source
    POST {{baseUrl}}/datasources?api-version=2023-11-01  HTTP/1.1
      Content-Type: application/json
      api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-ds",
            "description": null,
            "type": "azureblob",
            "subtype": null,
            "credentials": {
                "connectionString": "{{storageConnectionString}}"
            },
            "container": {
                "name": "{{blobContainer}}",
                "query": null
            },
            "dataChangeDetectionPolicy": null,
            "dataDeletionDetectionPolicy": null
        }
    
  2. [要求の送信] をクリックします。

スキルセットを作成する

スキルセットにより、エンリッチメント (スキル) とナレッジ ストアが定義されます。 [スキルセットの作成]: 検索サービスにオブジェクトを作成します。

  1. 次の例を貼り付けて、スキルセットを作成します。

    ### Create a skillset
    POST {{baseUrl}}/skillsets?api-version=2023-11-01  HTTP/1.1
        Content-Type: application/json
        api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-ss",
            "description": "Skillset to detect language, translate text, extract key phrases, and score sentiment",
            "skills": [ 
                {
                    "@odata.type": "#Microsoft.Skills.Text.SplitSkill", 
                    "context": "/document/reviews_text", "textSplitMode": "pages", "maximumPageLength": 5000,
                    "inputs": [ 
                        { "name": "text", "source": "/document/reviews_text" }
                    ],
                    "outputs": [
                        { "name": "textItems", "targetName": "pages" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.V3.SentimentSkill",
                    "context": "/document/reviews_text/pages/*",
                    "inputs": [
                        { "name": "text", "source": "/document/reviews_text/pages/*" },
                        { "name": "languageCode", "source": "/document/language" }
                    ],
                    "outputs": [
                        { "name": "sentiment", "targetName": "sentiment" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.LanguageDetectionSkill",
                    "context": "/document",
                    "inputs": [
                        { "name": "text", "source": "/document/reviews_text" }
                    ],
                    "outputs": [
                        { "name": "languageCode", "targetName": "language" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.TranslationSkill",
                    "context": "/document/reviews_text/pages/*",
                    "defaultFromLanguageCode": null,
                    "defaultToLanguageCode": "en",
                    "inputs": [
                        { "name": "text", "source": "/document/reviews_text/pages/*" }
                    ],
                    "outputs": [
                        { "name": "translatedText", "targetName": "translated_text" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Text.KeyPhraseExtractionSkill",
                    "context": "/document/reviews_text/pages/*",
                    "inputs": [
                        { "name": "text",  "source": "/document/reviews_text/pages/*" },
                        { "name": "languageCode",  "source": "/document/language" }
                    ],
                    "outputs": [
                        { "name": "keyPhrases" , "targetName": "keyphrases" }
                    ]
                },
                {
                    "@odata.type": "#Microsoft.Skills.Util.ShaperSkill",
                    "context": "/document",
                    "inputs": [
                        { "name": "name",  "source": "/document/name" },
                        { "name": "reviews_date",  "source": "/document/reviews_date" },
                        { "name": "reviews_rating",  "source": "/document/reviews_rating" },
                        { "name": "reviews_text",  "source": "/document/reviews_text" },
                        { "name": "reviews_title",  "source": "/document/reviews_title" },
                        { "name": "reviews_username",  "source": "/document/reviews_username" },
                        { "name": "AzureSearch_DocumentKey",  "source": "/document/AzureSearch_DocumentKey" },
                        {
                        "name": "pages",
                        "sourceContext": "/document/reviews_text/pages/*",
                        "inputs": [
                            {
                            "name": "languageCode",
                            "source": "/document/language"
                            },
                            {
                            "name": "translatedText",
                            "source": "/document/reviews_text/pages/*/translated_text"
                            },
                            { 
                            "name": "sentiment",
                            "source": "/document/reviews_text/pages/*/sentiment"
                            },
                            {
                            "name": "keyPhrases",
                            "source": "/document/reviews_text/pages/*/keyphrases/*"
                            },
                            {
                            "name": "Page",
                            "source": "/document/reviews_text/pages/*"
                            }
                        ]
                        }
                    ],
                    "outputs": [
                        { "name": "output" , "targetName": "tableprojection" }
                    ]
                }
            ],
            "knowledgeStore": {
                "storageConnectionString": "{{storageConnectionString}}",
                "projections": [
                    {
                        "tables": [
                            { "tableName": "hotelReviews1Document", "generatedKeyName": "Documentid", "source": "/document/tableprojection" },
                            { "tableName": "hotelReviews2Pages", "generatedKeyName": "Pagesid", "source": "/document/tableprojection/pages/*" },
                            { "tableName": "hotelReviews3KeyPhrases", "generatedKeyName": "KeyPhrasesid", "source": "/document/tableprojection/pages/*/keyPhrases/*" }
                        ],
                        "objects": []
                    },
                    {
                        "tables": [
                            { 
                                "tableName": "hotelReviews4InlineProjectionDocument", "generatedKeyName": "Documentid", "sourceContext": "/document",
                                "inputs": [
                                    { "name": "name", "source": "/document/name"},
                                    { "name": "reviews_date", "source": "/document/reviews_date"},
                                    { "name": "reviews_rating", "source": "/document/reviews_rating"},
                                    { "name": "reviews_username", "source": "/document/reviews_username"},
                                    { "name": "reviews_title", "source": "/document/reviews_title"},
                                    { "name": "reviews_text", "source": "/document/reviews_text"},
                                    { "name": "AzureSearch_DocumentKey", "source": "/document/AzureSearch_DocumentKey" }
                                ]
                            },
                            { 
                                "tableName": "hotelReviews5InlineProjectionPages", "generatedKeyName": "Pagesid", "sourceContext": "/document/reviews_text/pages/*",
                                "inputs": [
                                    { "name": "Sentiment", "source": "/document/reviews_text/pages/*/sentiment"},
                                    { "name": "LanguageCode", "source": "/document/language"},
                                    { "name": "Keyphrases", "source": "/document/reviews_text/pages/*/keyphrases"},
                                    { "name": "TranslatedText", "source": "/document/reviews_text/pages/*/translated_text"},
                                    { "name": "Page", "source": "/document/reviews_text/pages/*" }
                                ]
                            },
                            { 
                                "tableName": "hotelReviews6InlineProjectionKeyPhrases", "generatedKeyName": "kpidv2", "sourceContext": "/document/reviews_text/pages/*/keyphrases/*",
                                "inputs": [
                                    { "name": "Keyphrases", "source": "/document/reviews_text/pages/*/keyphrases/*" }
                                ]
                            }
                        ],
                        "objects": []
                    }
                ]
            }
        }
    

重要なポイント:

  • Shaper スキルは、ナレッジ ストアの定義にとって重要です。 ナレッジ ストアのテーブルにデータがどのように流れるかを指定します。 入力は、格納するエンリッチされたドキュメントの部分です。 出力は、ノードを 1 つの構造に統合したものです。

  • プロジェクションは、ナレッジ ストアのテーブル、オブジェクト、BLOB を指定します。 各プロジェクション項目は、Azure Storage で作成する列またはフィールドの "name" を指定します。 "source" は、シェーパー出力のどの部分が、そのフィールドまたは列に割り当てられるかを指定します。

インデクサーの作成

[インデクサーの作成]: インデクサーを作成して実行します。 インデクサーの実行は、まず、ドキュメントを解読し、テキストと画像を抽出し、スキルセットを初期化して開始します。 インデクサーは、作成された他のオブジェクト (データソース、インデックス、スキルセット) をチェックします。

  1. 次の例を貼り付けて、インデクサーを作成します。

    ### Create indexer
    POST {{baseUrl}}/indexers?api-version=2023-11-01  HTTP/1.1
        Content-Type: application/json
        api-key: {{apiKey}}
    
        {
            "name": "hotel-reviews-kstore-idxr",
            "dataSourceName": "hotel-reviews-kstore-ds",
            "skillsetName": "hotel-reviews-kstore-ss",
            "targetIndexName": "hotel-reviews-kstore-idx",
            "parameters": {
                "configuration": {
                    "dataToExtract": "contentAndMetadata",
                    "parsingMode": "delimitedText",
                    "firstLineContainsHeaders": true,
                    "delimitedTextDelimiter": ","
        }
    },
    "fieldMappings": [
        {
            "sourceFieldName": "AzureSearch_DocumentKey",
            "targetFieldName": "AzureSearch_DocumentKey",
            "mappingFunction": { "name": "base64Encode" }
        }
    ],
    "outputFieldMappings": [
        { "sourceFieldName": "/document/reviews_text/pages/*/Keyphrases/*", "targetFieldName": "Keyphrases" },
        { "sourceFieldName": "/document/Language", "targetFieldName": "Language" },
        { "sourceFieldName": "/document/reviews_text/pages/*/Sentiment", "targetFieldName": "Sentiment" }
        ]
    }
    
  2. [要求の送信] を選択して、インデクサーを実行します。 この手順は、完了するまでに数分かかります。

重要なポイント:

  • parameters/configuration オブジェクトでは、インデクサーがデータを取り込む方法を制御します。 ここでは、入力データは、ヘッダー行とコンマ区切り値がある単一の CSV ファイルに含まれています。

  • "AzureSearch_DocumentKey" は、(メタデータ ストレージ パスに基づいて) BLOB インデクサーによって生成される各ドキュメントの一意の識別子です。

  • 出力フィールド マッピングでは、エンリッチされたフィールドが、検索インデックス内のフィールドにマップされる方法を指定します。 出力フィールド マッピングは、ナレッジ ストアでは使用されません (ナレッジ ストアでは、物理的なデータ構造を表すためにシェイプとプロジェクションが使用されます)。

状態の確認

各要求を送信すると、検索サービスは 201 成功メッセージで応答します。

### Get Indexer Status (wait several minutes for the indexer to complete)
GET {{baseUrl}}/indexers/hotel-reviews-kstore-idxr/status?api-version=2023-11-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}}

数分後に、インデックスに対してクエリを実行して内容を検査できます。 インデックスを使用していない場合でも、この手順は、スキルセットが想定される出力を生成したことを確認するのに便利な方法です。

### Query the index (indexer status must be "success" before querying the index)
POST {{baseUrl}}/indexes/hotel-reviews-kstore-idxr/docs/search?api-version=2023-11-01  HTTP/1.1
  Content-Type: application/json
  api-key: {{apiKey}}
  
  {
    "search": "*",
    "select": "reviews_title, reviews_username, language, translated_text, sentiment",
    "count": true
  }

Azure portal でテーブルを確認する

Azure portal で Azure Storage アカウントに切り替え、ストレージ ブラウザーを使用して新しいテーブルを表示します。 スキルセットに定義されているプロジェクションごとに 1 つずつ、6 つのテーブルが表示されます。

各テーブルは、クエリ内のテーブルをクロスリンクするために必要な ID を使用して生成されます。 テーブルを開いたときに、これらのフィールドの下までスクロールして、パイプラインによって追加されたコンテンツ フィールドを表示します。

Screenshot of the knowledge store tables in Storage Browser

このチュートリアルでは、ナレッジ ストアは、テーブルのさまざまな整形と構造化方法を示すさまざまなテーブルで構成されています。 テーブル 1 から 3 は、Shaper スキルの出力を使用して列と行を決定します。 テーブル 4 から 6 は、プロジェクション自体に埋め込まれたインライン整形命令から作成されます。 どちらの方法を使用しても、同じ結果を得られます。

テーブル 説明
hotelReviews1Document CSV から引き継がれたフィールド (reviews_date や reviews_text など) が含まれます。
hotelReviews2Pages スキルセットによって作成された、エンリッチされたフィールド (センチメント スコアや翻訳されたテキストなど) が含まれます。
hotelReviews3KeyPhrases キー フレーズのみの長い一覧が含まれます。
hotelReviews4InlineProjectionDocument 最初のテーブルの代わりに、Shaper スキルではなくインライン整形を使用して、プロジェクションのデータを整形します。
hotelReviews5InlineProjectionPages 2 番目のテーブルの代わりに、インライン整形を使用します。
hotelreviews6InlineProjectionKeyPhrases 3 番目のテーブルの代わりに、インライン整形を使用します。

クリーンアップ

独自のサブスクリプションを使用している場合は、プロジェクトの最後に、作成したリソースがまだ必要かどうかを確認してください。 リソースを実行したままにすると、お金がかかる場合があります。 リソースは個別に削除することも、リソース グループを削除してリソースのセット全体を削除することもできます。

ポータルの左側のナビゲーション ウィンドウにある [すべてのリソース] または [リソース グループ] リンクを使って、リソースを検索および管理できます。

次のステップ

Azure AI サービスを使用してデータをエンリッチし、その結果をナレッジ ストアに投影したので、Storage Explorer や他のアプリを使用して、エンリッチしたデータ セットを探索できます。