REST를 사용하여 지식 저장소 만들기

Azure AI 검색에서 지식 저장소는 검색 외의 시나리오에 사용되는 AI 생성 콘텐츠의 리포지토리 입니다. 인덱서 및 기술 세트를 사용하여 지식 저장소를 만들고 출력을 저장할 Azure Storage를 지정합니다. 지식 저장소가 채워지면 Storage Explorer 또는 Power BI와 같은 도구를 사용하여 콘텐츠를 검색합니다.

이 문서에서는 REST API를 사용하여 지식 저장소에 있는 호텔 숙박에 대한 일단의 고객 리뷰를 수집, 보강 및 탐색합니다. 지식 저장소에는 원본에서 끌어온 원본 텍스트 콘텐츠와 영어가 아닌 고객 의견의 감정 점수, 핵심 구 추출, 언어 감지 및 텍스트 번역이 포함된 AI 생성 콘텐츠가 포함되어 있습니다.

초기 데이터 세트를 사용할 수 있도록 호텔 리뷰를 먼저 Azure Blob Storage에 가져옵니다. 처리 후에는 결과가 Azure Table Storage에 지식 저장소로 저장됩니다.

이 문서에서는 REST를 사용하여 각 단계에 대한 자세한 설명을 제공합니다. 명령을 실행하려는 경우 REST 파일을 다운로드합니다. 또는 Azure Portal에서 지식 저장소를 만들 수도 있습니다.

필수 조건

이 예제의 기술 세트는 보강을 위해 Azure AI Services를 사용합니다. 워크로드가 너무 작으므로 Azure AI 서비스는 매일 최대 20개의 트랜잭션을 무료로 제공하기 위해 백그라운드에 탭으로 처리됩니다. 워크로드가 작기 때문에 Azure AI 다중 서비스 리소스 만들기 또는 연결을 건너뛸 수 있습니다.

Azure Storage에 데이터 업로드 및 연결 문자열 가져오기

  1. HotelReviews_Free.csv를 다운로드합니다. 이 CSV에는 단일 호텔에 대한 19개의 고객 피드백이 포함되어 있습니다(Kaggle.com에서 가져옴).

  2. Azure Portal에서 스토리지 계정을 찾고 Storage Browser를 사용하여 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;"
}

참고 항목

연결 문자열 중요한 데이터를 제공하지 않으려면 관리 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 Search에서 데이터 원본 연결을 만듭니다.

  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. 요청 보내기를 선택합니다.

기술 세트 만들기

기술 세트는 보강(기술) 및 지식 저장소를 정의합니다. Skillset 만들기는 검색 서비스에 개체를 만듭니다.

  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": []
                    }
                ]
            }
        }
    

주요 정보:

  • 쉐이퍼 기술은 지식 저장소 정의에 중요합니다. 데이터가 지식 저장소의 테이블로 흐르는 방식을 지정합니다. 입력은 저장하려는 보강된 문서의 일부입니다. 출력은 노드를 단일 구조로 통합한 것입니다.

  • 프로젝션은 지식 저장소의 테이블, 개체 및 Blob을 지정합니다. 각 프로젝션 항목은 "name" Azure Storage에서 만들 열 또는 필드의 값을 지정합니다. "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 계정으로 전환하고, Storage Browser를 사용하여 새 테이블을 봅니다. 기술 세트에 정의된 각 프로젝션에 대해 하나씩 6개의 테이블이 표시됩니다.

각 테이블은 쿼리에서 테이블을 교차 연결하는 데 필요한 ID를 사용하여 생성됩니다. 테이블을 열 때 이러한 필드를 지나면서 스크롤하여 파이프라인에서 추가한 콘텐츠 필드를 봅니다.

Screenshot of the knowledge store tables in Storage Browser

이 연습에서 지식 저장소는 테이블을 셰이핑하고 구조화하는 다양한 방법을 보여주는 다양한 테이블로 구성됩니다. 1~3 테이블은 Shaper 기술의 출력을 사용하여 열과 행을 결정합니다. 4~6 테이블은 프로젝션 자체 내에 포함된 인라인 모양 지정 명령에서 생성됩니다. 두 방법 중 하나를 사용하여 동일한 결과를 얻을 수 있습니다.

테이블 설명
hotelReviews1Document CSV에서 전달되는 필드(예: review_date 및 review_text)를 포함합니다.
hotelReviews2Pages 기술 세트에서 만든 보강 필드(예: 감정 점수 및 번역된 텍스트)를 포함합니다.
hotelReviews3KeyPhrases 핵심 구의 긴 목록을 포함합니다.
hotelReviews4InlineProjectionDocument 첫 번째 테이블의 대안이며, Shaper 기술 대신 인라인 셰이핑을 사용하여 프로젝션에 대한 데이터를 셰이핑합니다.
hotelReviews5InlineProjectionPages 두 번째 테이블의 대안이며, 인라인 셰이핑을 사용합니다.
hotelreviews6InlineProjectionKeyPhrases 세 번째 테이블의 대안이며, 인라인 셰이핑을 사용합니다.

정리

본인 소유의 구독으로 이 모듈을 진행하고 있는 경우에는 프로젝트가 끝날 때 여기에서 만든 리소스가 계속 필요한지 확인하는 것이 좋습니다. 계속 실행되는 리소스에는 요금이 부과될 수 있습니다. 리소스를 개별적으로 삭제하거나 리소스 그룹을 삭제하여 전체 리소스 세트를 삭제할 수 있습니다.

왼쪽 탐색 창의 모든 리소스 또는 리소스 그룹 링크를 사용하여 포털에서 리소스를 찾고 관리할 수 있습니다.

다음 단계

이제 Azure AI 서비스를 사용하여 데이터를 보강하고 결과를 지식 저장소에 프로젝션했으므로 Storage Explorer 또는 다른 앱을 사용하여 보강된 데이터 세트를 검색할 수 있습니다.