Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Amazon Kendra と ChatGPT で RAG を実現する

こんにちは、機械学習チーム YAMALEX の駿です。
YAMALEX は Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。
(詳細はリンク先をご覧ください。)

今回は Amazon KendraOpenAI ChatGPT を組み合わせてRAGシステムを構築してみます。

RAG とは Retrieval Augmented Generation (検索拡張生成) の略で、 ChatGPT に代表される LLM (大規模言語モデル)でユーザの質問への回答を生成する際に必要な情報(コンテキスト)を事前に検索などを通して取得してから、 コンテキストを踏まえた回答を生成する手法のことです。
言い換えると、RAG は検索そのものの処理ではなく、検索結果を解析し、その内容を分かりやすく要約するものです。

RAG を使用することで、企業内情報やドメイン知識が必要な質問応答において、分かりやすい回答を生成できます。

1. Amazon Kendraとは

Amazon Kendra (以下、 Kendra )は Amazon が提供する、機械学習を利用したインテリジェント検索サービスです。
どのような検索ができるのかは、こちらの記事で紹介しているので、参照してください。

acro-engineer.hatenablog.com

RAG の検索部分に Kendra を使う利点が大きく2つあります。

  1. 自前の実装はゼロで、データ投入、検索を実現できる

    • ドキュメントの取り込み、検索の実行がいかに簡単かは、前回の記事で示した通りです。
  2. LLM が扱うのに最適なチャンクサイズで結果を返してくれる

    • 他の検索サービスを使う場合、 LLM のトークン上限に抵触しないように、検索前後で扱いやすいサイズのチャンクに切り分ける必要がありましたが、 Kendra は LLM で利用されることを想定して、より高い精度で回答できるようなチャンクで検索結果を返します。

    チャンクとは RAG において、検索結果の塊のことを指します。
    一つ一つのチャンクを大きくする/利用するチャンク数を増やすと必要な情報がチャンクに含まれる確率は上がりますが、 その分トークン数が増えてしまうため、金額やトークン上限に響いてきます。
    逆にチャンクを小さくしすぎる/少なすぎると LLM の回答に必要な情報が含まれず、適切な回答ができなくなります。

    • チャンクを作る際には文脈の途中で区切れないようにしないと、など工夫しながらやっていましたが、もうその必要はありません。

2. 構成

AWS Lambda を利用して、 Kendra と ChatGPT を呼び出し、 RAG を実現します。

# 項目 概要
1 Kendra データソース S3: IPA が公開している EC サイト構築・運用セキュリティガイドライン
WebCrawler: Kendra の公式ドキュメント
2 ChatGPT モデル gpt-3.5-turbo

Kendra と ChatGPT を使った構成図

Kendra のインデックスとデータソースの作り方については AWS 公式のドキュメントを参照してください。

docs.aws.amazon.com

3. 実装

順番に実装していきます。

  1. Kendra のインデックスを検索する(上の構成図の 2. 相当)
  2. ChatGPT で回答を生成する(上の構成図の 3. 相当)
  3. 組み合わせて Lambda を作る(上の構成図の 1. 、 4. 相当)

3.1. Kendra のインデックスを検索する

def _retrieve_contexts(question: str, page_size=2) -> dict:
    """Retrieve contexts from Kendra."""
    response = kendra.retrieve(
        IndexId=KENDRA_INDEX,
        QueryText=question,
        PageSize=page_size,
        AttributeFilter={
            "EqualsTo": {
                "Key": "_language_code",
                "Value": {"StringValue": "ja"},
            },
        },
    )

    contexts = [{
        "DocumentTitle": item["DocumentTitle"],
        "Content": item["Content"],
    } for item in response.get("ResultItems", [])]
    return contexts

3.2. ChatGPT で回答を生成する

def _generate_answer(question: str, contexts: dict, max_tokens=512) -> str:
    """Generate answer using ChatGPT."""
    system_prompt = dedent("""\
        You are a Bot that answers User's question.
        Be sure to answer using information in Contexts.

        ## Contexts ##
        {contexts}
    """).format(contexts=json.dumps(contexts, ensure_ascii=False))
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": question},
    ]

    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        temperature=0.0,
        messages=messages,
        max_tokens=max_tokens,
        stop=None,
    )

    answer = response.choices[0]["message"]["content"]
    return answer

3.3. 組み合わせて Lambda を作る

def retrieve_and_generate(question: str) -> tuple[dict, str]:
    """Run RAG using User question."""
    contexts = _retrieve_contexts(question)
    answer = _generate_answer(question, contexts)
    return contexts, answer


def lambda_handler(event, context):
    """Handler for RAG Lambda."""
    question = event.get("question")
    contexts, answer = retrieve_and_generate(question)
    return contexts, answer

インポート文やコメント行も含めて、 100 行足らずで RAG システムを実装できてしまいました。

4. 最終結

次に実際に実行してみて、検索結果と回答を確認します。
※見やすさのため、実際の出力を整形して記載しています。

Lambda を実行する際は、 event に {"question": "質問を入力する"} を入れて実行してください。

4.1. Kendra について質問

【質問】
Kendraの特徴を3つ教えて

【回答】
Kendraの特徴を以下に示します:

1. インデックス作成の柔軟性:Kendraは、最大5つのインデックスを作成することができます。
  それぞれのインデックスは、最大50のデータソースを持つことができます。
  これにより、エンタープライズドキュメントライブラリ全体を効率的にインデックス化することができます。
2. 高いクエリ処理能力:Kendraは、1日あたり約8,000のクエリを処理することができます。
  また、1秒あたり0.1のクエリを処理することも可能です。
  これにより、大量のユーザーからの同時アクセスにも対応することができます。
3. 高い可用性と耐久性:Kendraは、3つのアベイラビリティーゾーン(AZ)で実行されます。
  これにより、データセンターの障害やネットワークの問題に対しても高い可用性と耐久性を提供します。
  また、AWSリージョン内のデータセンターを参照しているため、データのセキュリティも確保されています。

【使用したコンテキスト】
[
  {
    "DocumentTitle": "kendra-dg",
    "Content": "• 10,000 件のドキュメントまたは 3 GB の抽出テ キスト • 1 日あたり約 4,000 クエリ、または 1 秒あたり 0.05 クエリ • 1 つのアベイラビリティーゾーン (AZ) で実行 — 「アベイラビリティーゾーン (AWSリージョン内 のデータセンター)」を参照 機能制限 • プロダクションアプリケーションには適してい ません • レイテンシーや可用性の保証なし Amazon KendraEnterprise Edition は、Amazon Kendraプロダクション環境向けに設計されてお り、すべての機能を備えています。 理想的な使用事例 • エンタープライズドキュメントライブラリ全体 のインデックス作成 • 本番環境へのアプリケーションのデプロイ 特徴 • 最大 5 つのインデックス、それぞれ最大 50 の データソース • 100,000 件のドキュメントまたは 30 GB の抽出 テキスト • 1 日あたり約 8,000 クエリ、または 1 秒あたり 0.1 クエリ • 3 つのアベイラビリティーゾーン (AZ) で実行 — 「アベイラビリティーゾーン (AWSリージョン内 のデータセンター)」を参照 Note Service Quotas コンソールを使用して IAM クォータの増加をリクエストできま"
  },
  {
    "DocumentTitle": "kendra-dg",
    "Content": "権に基づいてフィルタリングできます。 ユーザーアクセスの認証と承認はお客様の責任となります。 Amazon Kendraエディション Amazon Kendraには、開発者版とエンタープライズ版の 2 つのバージョンがあります。 次の表は、それぞ れの機能と 2 つの相違点をまとめたものです。 Amazon Kendra開発者版 Amazon Kendraエンタープライズエディション Amazon Kendraデベロッパーエディション は、Amazon Kendraのすべての機能を低コストで 提供します。 理想的な使用事例 • Amazon Kendraドキュメントのインデックス作 成方法を調べる • 機能を試してみる • を使用するアプリケーションの開発 Amazon Kendra 特徴 • 750 時間の使用を含む無料利用枠 • 最大 5 つのインデックス、それぞれ最大 5 つの データソース • 10,000 件のドキュメントまたは 3 GB の抽出テ キスト • 1 日あたり約 4,000 クエリ、または 1 秒あたり 0.05 クエリ • 1 つのアベイラビリティーゾーン (AZ) で実行 — 「アベイラビリティーゾーン (AWSリージョン内 のデータセンター)」を参照"
  }
]

4.2. セキュリティガイドラインについて質問

【質問】
サイバー保険とは何ですか?

【回答】
サイバー保険は、企業や個人がサイバー攻撃やデータ漏洩などのサイバー事件によって生じる損害や費用を補償する保険のことです。  
具体的には、被害者への損害賠償や法的費用、被害復旧やデータ復旧の費用、プライバシー侵害によるリスク管理費用などが補償されます。  
サイバー保険は、企業や組織が顧客情報や機密情報を保有している場合に特に重要であり、被害発生時の経済的なリスクを軽減するために加入されることが推奨されています。

【使用したコンテキスト】
[
  {
    "DocumentTitle": "000109337",
    "Content": "対策をするまでの期 間内にサイバー攻撃を受けることがないよう、応急処置として、WAF を導入することを推 奨しています。 (「コラム 5:(ご紹介)WAF(Web Application Firewall)の活用方法」も参 考にしてください。) 36 要件7 サイバー保険に加入する。 万が一、ECサイトまたは、自社システムがサイバー攻撃による被害を受けた場合に備え て、サイバー保険に加入することを推奨しています。 考にしてください。) 36 要件7 サイバー保険に加入する。 万が一、ECサイトまたは、自社システムがサイバー攻撃による被害を受けた場合に備え て、サイバー保険に加入することを推奨しています。 サイバー保険については、IPA調査でも顧客情報の漏えい事故を発生させてしまった EC サイトの多くが、被害後に加入していますが、損害賠償や事故対応費用の 負担、収益の減 少を補う効果が認められることから、被害が発生していない場合でも被害発生に備えて加 入することを推奨しています。 コ ラ ム 2 (ご紹介)ECサイト向けホスティング付きセキュリティ保守運用サービス セキュリティ対策について、自社または構築事業 者による対応で賄えない部分を補う際に採りうる選択肢"
  },
  {
    "DocumentTitle": "000109337",
    "Content": "要件6 WAFを導入する。 既に 見つかっている脆弱性に対して対応するまでに期間が必要な場合や、必要となるセ キュリティ対策を実装するまでに期間が必要な場合が 想定されます。 対策をするまでの期 間内にサイバー攻撃を受けることがないよう、応急処置として、WAF を導入することを推 奨してい ます。 (「コラム 5:(ご紹介)WAF(Web Application Firewall)の活用方法」も参 考にしてください。) 36 要件7 サイバー保険に 加入する。 万が一、ECサイトまたは、自社システムがサイバー攻撃による被害を受けた場合に備え て、サイバー保険に加入することを推奨しています。 考にしてください。) 36 要件7 サイバー保険に加入する。 万が一、ECサイトまたは、自社システムがサイバー攻撃による被害を受けた場合に備え て、サイバー保険に加入することを推奨しています。 サイバー保険については、IPA調査でも顧客情報の漏え い事故を発生させてしまった EC サイトの多くが、被害後に加入していますが、損害賠償や事故対応費用の負担、収益の減 少を補う効果 が認められることから、被害が発生していない場合でも被害発生に備えて加"
  }
]

いずれも検索結果に回答に必要な情報が含まれており、かつ検索結果の情報を使って回答できていることが分かります。

しかし、今回取得したチャンクが、質問ごとにそれぞれほぼ同じ部分の検索結果が引っ掛かっているのが気になりました。
特にサイバー保険についての質問では7割方内容が一致しており、ChatGPTへの入力トークンの無駄遣い感は否めません。

範囲がかぶっているチャンクを削除するなど、トークン数を節約するための対策はしておいた方が良さそうです。

5. まとめ

Kendra と ChatGPT を使った RAG システムを Lambda で実装し、いくつか質問と回答のやり取りをしてみました。

実際のシステムとして運用する場合はここに、 APIトークン数計算、会話履歴、ロギングなど、様々な要素を追加する必要がありますが、 最小限の RAG の構成を 100 行以下で実装できる、というのは衝撃でした。

今回は質問文をそのまま Kendra に入力してセマンティック検索を行いましたが、固有表現を抽出してキーワード検索を行うと検索精度が上がるのか、 ChatGPT に入力しているプロンプトを改善して生成精度が上がるか、など検証してみるのも面白いかもしれません。

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com