Amazon KendraとAWS Lambdaを使い、RAGのRetrievalフェーズを試してみた

2023.10.10

はじめに

Amazon KendraとAWS Lambdaを使って、Retrieval Augmented Generation(RAG)のうち、Retrievalフェーズを試してみました。

Amazon Kendraは、様々なデータソースから特定の情報を迅速に見つけ出すことができるエンタープライズ向けの検索サービスです。

ユーザーが質問やキーワードを入力すると、Kendraは自身のインデックスから関連性の高い情報を検索(Retrieval)してユーザーに提供します。このプロセスは自然言語処理(NLP)技術を利用しており、質問の内容を理解し、最も関連性のある回答を提供することが可能です。

最近、社内の業務効率化などの目的で、AIの言語モデル(以降、LLM)を用いて社内情報を活用するための手法として、RAGがよく話題になっています。

RAGは、ユーザーのプロンプトに基づいて、外部データを参照し関連性のあるドキュメントを検索後、その情報とユーザーのプロンプトをLLMに渡すことで、質問への回答が生成されます。

これをAWSサービスで実現する場合、AWSドキュメントにも記載されていますが、検索(Retrieval)フェーズは、Kendraで実現が可能です。

Amazon Kendraは、最適化されたKendra Retriever APIを提供し、RAG(Retrieval Augmented Generation)ワークフローのエンタープライズリトリーバーとしてAmazon Kendraの高精度セマンティックランカーを使用できるようにします。Kendra Retriever APIは、ユーザーの質問に最もセマンティックに関連し、RAGペイロードの品質を最大化するために最適化された粒度を持つ、エンタープライズコンテンツからの文章を検索して取得します。

今回は、いきなりRAGでのシステムを構築するのは、ハードルが高いので、検索(Retrieval)のフェーズのみを試してみます。

構成

構成図は、下記のとおりです。

Kendraのデータソース(取り込み元)としては、ウェブサイトやS3バケットに保存したドキュメントなどが利用できます。今回は、ウェブサイトとしてAWS公式ドキュメントをデータソースとして使用し、その中から情報を検索します。

Kendraのインデックスを作成

インデックス名は、testにします。

他は、アクセスコントロール設定は、デフォルトのままにします

検証用のDeveloper editionを選択し、インデックスを作成します。30分程度かかりました。

データソースとして、S3バケットやウェブクローラーなどが選択できます。

今回は、ウェブクローラーv2.0にしました。

データソースは、日本語ですので、日本語を選択します。

今回は、KendraのAWSドキュメントを取り込みます。

  • https://docs.aws.amazon.com/ja_jp/kendra/latest/dg/what-is-kendra.html

IAMロールは、新規作成します

他は、デフォルトのまま進めます。

Sync domains onlyを選択します。

同期の頻度は、オンデマンドにします。後で、Sync nowで同期することを忘れないようにしましょう。

フィールドのマッピングは、デフォルトのまま進めます。

Sync nowで手動で同期します

同期が完了すれば、kendra側の設定は完了です。

Document countは、539でした。

左画面の[Search indexed content]から、[Settings]を日本語にしてから、検索してみます。

検索ワードをもとに、関連度順に検索結果できています。

Lambdaを作成

Lambdaを作成します。以下を参考に、IAMロールやBedrockを利用するためのBoto3ライブラリをアップロードしてください。

さらに、Kendraにリクエストを送信できるようにするため、LambdaのIAMロールには、AmazonKendraFullAccessポリシーをアタッチします。

コードは下記のとおりです。

import boto3
import json
kendra = boto3.client('kendra')

def lambda_handler(event, context):

    # クエリのテキストをハードコーディング(検索内容を記載)
    query_text = 's3バケット'

    # Kendra インデックス ID に置き換え
    index_id = '<IndexID>'  

    response = kendra.retrieve(
        QueryText=query_text,
        IndexId=index_id,
        AttributeFilter={
            "EqualsTo": {
                "Key": "_language_code",
                "Value": {"StringValue": "ja"},
            },
        },
    )

    # Kendra の応答から最初の3つの結果を抽出
    results = response['ResultItems'][:3] if response['ResultItems'] else []

    extracted_results = []
    for item in results:
        content = item.get('Content')
        document_uri = item.get('DocumentURI')

        extracted_results.append({
            'Content': content,
            'DocumentURI': document_uri,
        })
    print("Kendra extracted_results:" + json.dumps(extracted_results, ensure_ascii=False))
    return extracted_results

KendraのインデックスIDは、Kendraのコンソール画面から確認できます。

また、検索内容から、関連性の高い上位3つのドキュメントを抽出しました。(下記の3つです)

"翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。nS3 バケットからドキュメントを追加できます。nPDFnRSSnドキュメントは、次の場所からインデックスに直接追加場合に指定します。Amazon S3バケット。同じコールで最大 10 個のドキュメントを追加できます。S3 バケットを使用する場合は、IAMドキュメントを含むバケットにアクセスする権限を持つロール。RoleArn パラメータでロールを指定します。nを使用するBatchPutDocumentAPI を使用してからドキュメントを追加するAmazon S3バケットは削除できます。インデックスをバケットの内容と同期させるには、Amazon S3データソース。詳細については、次を参照してください。"

"サンプルデータフォルダをダウンロードして抽出したら、Amazon S3 バケットに保存します。n重要nAmazon S3 バケットの名前はすべての AWS 全体で一意である必要があります。nS3 バケットを作成するには (コンソール)nS3 バケットを作成するには (AWS CLI)nS3 バケットにデータフォルダとメタデータフォルダを作成するnS3 バケットを作成した後、その中のフォルダにデータフォルダとメタデータフォルダを作成します。"

"前提条件n使用する前にAmazon KendraS3 データソースにインデックスを付けるには、S3 で以下の変更を行い、AWSアカウント。nS3 では、次ののコストを追跡したりできます。nあなたの名前をコピーしましたAmazon S3バケット名n注記nバケットは、のコストを追跡したりできます。Amazon Kendraインデックスとインデックスには、ドキュメントを含むバケットにアクセスする権限が必要です。nS3 内および同じインデックスに使用する予定の他のデータソースでも、各ドキュメントが固有であることを確認しました。"

コンソールと同じ内容が抽出されていることを確認しました。

最後に

今回は、RAGのうちRetrievalのフェーズを、Kendraを使用して実装してみました

Lambdaのコードも思ったよりも少なく簡単に実装することができました、

また、次回では、Kendraで抽出した3つのドキュメントをもとに、次のGenerationフェーズ(LLMにプロンプトを渡し、質問に対する回答を生成する)を実装したいと思います。

参考