AWS Bedrock(Claude)に読み取らせる情報によるハルシネーションの発生状況を確認してみた

    AWS Bedrock(Claude)に読み取らせる情報によるハルシネーションの発生状況を確認してみた

     

    目次

      はじめに

      クラウドインテグレーション部の渡邊です。
      今回は以下3パターンの出力を検証します。
      とくに、ハルシネーションに絞った検証を行います。

      • AWS Bedrockのみ
      • AWS Kendraと連携
      • Agents for Amazon Bedrock(Google Cloud APIs)

      こちらは、Jr. Champion限定のイベントの内容を参考にしています。

      Japan AWS Jr. Championsとは?

      AWS Partner Network (APN) 参加企業に所属し、現在社会人歴 1 ~ 3 年目で AWS を積極的に学び、アクションを起こし、周囲に影響を与えている APN 若手エンジニア

      引用元:2023 Japan AWS Jr. Champions の発表

      Japan AWS Jr. Championsのメンバーと、勉強会やLT会など定期的に活動しています。

      202402_bedrock_01
      2023 Japan AWS Jr. Champions 表彰時の写真

      いきなりまとめ

      • Kendraと連携する場合
        • 検索する対象が決まっており、正しい回答を得たい場合、適している
        • 検索する対象が定まっていない場合、Kendra以外を使用するほうが望ましそう
      • Agent(Google Cloud APIs)
        • インターネットの情報を参照しているからと言って、正確な回答が得られるとは限らない

      環境準備

      今回は、us-west-2(米国西部 (オレゴン))リージョンで環境作成する想定なので注意してください。

       

      スタック作成

      まずは、以下のCloudFormationテンプレートをもとにスタックを作成してください。

      ##This CloudFormation template creates an Amazon Kendra index. It adds a webcrawler datasource
      ##to the index and crawls the online AWS Documentation for Amazon Kendra, Amazon Lex and Amazon SageMaker
      ##After the datasource is configured, it triggers a datasource sync, i.e. the process to crawl the sitemaps
      ##and index the crawled documents.
      ##The output of the CloudFormation template shows the Kendra index id and the AWS region it was created in.
      ##It takes about 30 minutes to create an Amazon Kendra index and about 15 minutes more to crawl and index
      ##the content of these webpages to the index. Hence you might need to wait for about 45 minutes after
      ##launching the CloudFormation stack
      Resources:
      ##Create the Role needed to create a Kendra Index
      KendraIndexRole:
      Type: 'AWS::IAM::Role'
      Properties:
      AssumeRolePolicyDocument:
      Version: 2012-10-17
      Statement:
      - Sid: ''
      Effect: Allow
      Principal:
      Service: kendra.amazonaws.com
      Action: 'sts:AssumeRole'
      Policies:
      - PolicyDocument:
      Version: 2012-10-17
      Statement:
      - Effect: Allow
      Resource: '*'
      Condition:
      StringEquals:
      'cloudwatch:namespace': 'Kendra'
      Action:
      - 'cloudwatch:PutMetricData'
      - Effect: Allow
      Resource: '*'
      Action: 'logs:DescribeLogGroups'
      - Effect: Allow
      Resource: !Sub
      - 'arn:aws:logs:${region}:${account}:log-group:/aws/kendra/*'
      - region: !Ref 'AWS::Region'
      account: !Ref 'AWS::AccountId'
      Action: 'logs:CreateLogGroup'
      - Effect: Allow
      Resource: !Sub
      - 'arn:aws:logs:${region}:${account}:log-group:/aws/kendra/*:log-stream:*'
      - region: !Ref 'AWS::Region'
      account: !Ref 'AWS::AccountId'
      Action:
      - 'logs:DescribeLogStreams'
      - 'logs:CreateLogStream'
      - 'logs:PutLogEvents'
      PolicyName: !Join
      - ''
      - - !Ref 'AWS::StackName'
      - '-DocsKendraIndexPolicy'
      RoleName: !Join
      - ''
      - - !Ref 'AWS::StackName'
      - '-DocsKendraIndexRole'

      ##Create the Kendra Index
      DocsKendraIndex:
      Type: 'AWS::Kendra::Index'
      Properties:
      Name: !Join
      - ''
      - - !Ref 'AWS::StackName'
      - '-Index'
      Edition: 'DEVELOPER_EDITION'
      RoleArn: !GetAtt KendraIndexRole.Arn

      ##Create the Role needed to attach the Webcrawler Data Source
      KendraDSRole:
      Type: 'AWS::IAM::Role'
      Properties:
      AssumeRolePolicyDocument:
      Version: 2012-10-17
      Statement:
      - Sid: ''
      Effect: Allow
      Principal:
      Service: kendra.amazonaws.com
      Action: 'sts:AssumeRole'
      Policies:
      - PolicyDocument:
      Version: 2012-10-17
      Statement:
      - Effect: Allow
      Resource: !Sub
      - 'arn:aws:kendra:${region}:${account}:index/${index}'
      - region: !Ref 'AWS::Region'
      account: !Ref 'AWS::AccountId'
      index: !GetAtt DocsKendraIndex.Id
      Action:
      - 'kendra:BatchPutDocument'
      - 'kendra:BatchDeleteDocument'
      PolicyName: !Join
      - ''
      - - !Ref 'AWS::StackName'
      - '-DocsDSPolicy'
      RoleName: !Join
      - ''
      - - !Ref 'AWS::StackName'
      - '-DocsDSRole'

      #Docs Data Source
      KendraDocsDS:
      Type: 'AWS::Kendra::DataSource'
      Properties:
      DataSourceConfiguration:
      WebCrawlerConfiguration:
      UrlInclusionPatterns:
      - '.*https://docs.aws.amazon.com/ja_jp/lex/.*'
      - '.*https://docs.aws.amazon.com/ja_jp/kendra/.*'
      - '.*https://docs.aws.amazon.com/ja_jp/sagemaker/.*'
      Urls:
      SiteMapsConfiguration:
      SiteMaps:
      - 'https://docs.aws.amazon.com/ja_jp/lex/latest/dg/sitemap.xml'
      - 'https://docs.aws.amazon.com/ja_jp/kendra/latest/dg/sitemap.xml'
      - 'https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/sitemap.xml'
      IndexId: !GetAtt DocsKendraIndex.Id
      Name: 'KendraDocsDS'
      RoleArn: !GetAtt KendraDSRole.Arn
      Type: 'WEBCRAWLER'
      LanguageCode: ja

      DataSourceSyncLambdaRole:
      Type: AWS::IAM::Role
      Properties:
      AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
      - Effect: Allow
      Principal:
      Service: lambda.amazonaws.com
      Action: sts:AssumeRole
      ManagedPolicyArns:
      - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
      - PolicyDocument:
      Version: 2012-10-17
      Statement:
      - Effect: Allow
      Resource: !Sub
      - 'arn:aws:kendra:${region}:${account}:index/${index}*'
      - region: !Ref 'AWS::Region'
      account: !Ref 'AWS::AccountId'
      index: !GetAtt DocsKendraIndex.Id
      Action:
      - 'kendra:StartDataSourceSyncJob'
      PolicyName: DataSourceSyncLambdaPolicy

      DataSourceSyncLambda:
      Type: AWS::Lambda::Function
      Properties:
      Handler: index.lambda_handler
      Runtime: python3.8
      Role: !GetAtt 'DataSourceSyncLambdaRole.Arn'
      Timeout: 900
      MemorySize: 1024
      Code:
      ZipFile: |

      # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
      # SPDX-License-Identifier: MIT-0

      import json
      import logging
      import boto3
      import cfnresponse
      import random
      import os

      logger = logging.getLogger()
      logger.setLevel(logging.INFO)

      INDEX_ID = os.environ['INDEX_ID']
      DS_ID = os.environ['DS_ID']
      AWS_REGION = os.environ['AWS_REGION']
      KENDRA = boto3.client('kendra')

      def start_data_source_sync(dsId, indexId):
      logger.info(f"start_data_source_sync(dsId={dsId}, indexId={indexId})")
      resp = KENDRA.start_data_source_sync_job(Id=dsId, IndexId=indexId)
      logger.info(f"response:" + json.dumps(resp))

      def lambda_handler(event, context):
      logger.info("Received event: %s" % json.dumps(event))
      start_data_source_sync(DS_ID, INDEX_ID)
      status = cfnresponse.SUCCESS
      cfnresponse.send(event, context, status, {}, None)
      return status

      Environment:
      Variables:
      INDEX_ID: !GetAtt DocsKendraIndex.Id
      DS_ID: !GetAtt KendraDocsDS.Id

      DataSourceSync:
      Type: Custom::DataSourceSync
      DependsOn:
      - DocsKendraIndex
      - KendraDocsDS
      Properties:
      ServiceToken: !GetAtt DataSourceSyncLambda.Arn

      Outputs:
      KendraIndexID:
      Value: !GetAtt DocsKendraIndex.Id
      AWSRegion:
      Value: !Ref 'AWS::Region'

       

      Bedrockのモデルへのアクセス許可設定

      Amazon Bedrockのコンソール画面で特定のモデルを利用する際に、モデルを利用するためにリクエストが必要です。

      202402_bedrock_02

      [Get started] > [Request model access]をクリックします。

      202402_bedrock_03

      すると、「Base models」が出るため、[Manage Models]をクリック後、「Anthropic」を選択します。
      しばらくすると、選択したモデルへのアクセス許可がされます。

      202402_bedrock_04

       

      GCPでAPI KEYとCSE IDの用意

      検証でAgent(外部アプリケーション(Google Cloud APIs)を利用するために、回答を行います。
      CGPアカウントにログインして、API_KEYとCSE_IDを用意します。
      GOOGLE_API_KEY:https://console.cloud.google.com/apis/credentials
      GOOGLE_CSE_ID:https://programmablesearchengine.google.com/controlpanel/create

      Custom Search API有効化する点に注意しましょう。

      202402_bedrock_05

       

      SageMakerノートブックの用意 & IAMロールの権限変更

      SageMakerノートブックのコンソール画面からインスタンスを作成します。
      また、あとで権限を変更しますが、新しいIAMロールを作成します。

      202402_bedrock_06

      IAMロールの作成に成功するのでインスタンス名を設定して、作成します。

      202402_bedrock_07

      ここで、先ほど作成したIAMロールの権限を変更します。
      検証で、BedrockとKendraに対するアクセス権限が必要だからです。

      • IAMロール作成時にすでにあるポリシー
        • AmazonSageMaker-ExecutionPolicy-XXXXXXXXXXXXXX
        • AmazonSageMakerFullAccess
      • 追加するポリシー
        • AmazonBedrockFullAccess
        • AmazonKendraFullAccess

      202402_bedrock_08

      202402_bedrock_09

      信頼ポリシーは以下のように設定してください。

      {
      "Version": "2012-10-17",
      "Statement": [
      {
      "Effect": "Allow",
      "Principal": {
      "Service": [
      "sagemaker.amazonaws.com",
      "kendra.amazonaws.com",
      "bedrock.amazonaws.com"
      ]
      },
      "Action": "sts:AssumeRole"
      }
      ]
      }

      SageMaker ロールを設定するを参考に、信頼ポリシーも修正します。

      ノートブックを開きます。

      202402_bedrock_10

      ノートブックのアップロード

      以下のノートブックを保存してアップロードします。
      拡張子はipynbです。

      {
      "cells": [
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "dfec2db5-c61a-40f4-9a65-6de2621cc390",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "!pip install langchain==0.0.335"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "0a0ceaf5-3899-42ed-b223-a7f9c58da83b",
      "metadata": {},
      "outputs": [],
      "source": [
      "# AWS Bedrockのみ(Claude)"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "92312d13-5cb0-4962-8d1e-16367151ec5c",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "from langchain.llms import Bedrock\n",
      "from langchain.chains import ConversationChain\n",
      "from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler\n",
      "\n",
      "llmst = Bedrock(\n",
      " model_id = \"anthropic.claude-v2\",\n",
      " streaming=True,\n",
      " callbacks=[StreamingStdOutCallbackHandler()],\n",
      ")\n",
      "\n",
      "conversation = ConversationChain(\n",
      " llm=llmst, verbose=True\n",
      ")"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "deced5af-5684-4921-8624-3bf8d368ad7b",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "conversation.predict(input=\"Amazon Lexにアクセスするためにクライアントが満たすべきTLSの要件を日本語で教えてください\")"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "e4593ef2-3cf2-4f8d-8cd6-f961de82d80c",
      "metadata": {},
      "outputs": [],
      "source": [
      "# AWS Kendraと連携"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": 7,
      "id": "f16b04d0-496b-478f-85b2-bbe9d0d8490a",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "import boto3\n",
      "from langchain.retrievers import AmazonKendraRetriever\n",
      "from langchain.chains import RetrievalQA\n",
      "from langchain.llms import Bedrock\n",
      "\n",
      "language_code = \"ja\"\n",
      "retriever = AmazonKendraRetriever(\n",
      " index_id=\"YOUR_KENDRA_INDEX_ID\",\n",
      " attribute_filter={\n",
      " \"EqualsTo\": {\n",
      " \"Key\": \"_language_code\",\n",
      " \"Value\": {\"StringValue\": language_code},\n",
      " }\n",
      " }\n",
      " )\n",
      " \n",
      "qa = RetrievalQA.from_chain_type(\n",
      " llm = Bedrock(model_id = \"anthropic.claude-v2\"),\n",
      " chain_type=\"stuff\",\n",
      " retriever=retriever,\n",
      ")"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "df4892d9-1ffe-4e74-b7e6-1b53951ad26f",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "qa.run(\"Amazon Lexにアクセスするためにクライアントが満たすべきTLSの要件を日本語で教えてください\")"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "05869d8e-5db6-4877-a61e-0b9db185f3d8",
      "metadata": {},
      "outputs": [],
      "source": [
      "# Agents for Amazon Bedrock(Google Cloud APIs)"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": 9,
      "id": "c01de778-5107-4ab7-9a5d-b6dd1f1a1d6f",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "!pip install google-api-python-client>=2.100.0"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": 27,
      "id": "d2fb223f-3bf7-48c2-b692-ef569686507c",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "import os\n",
      "from langchain.agents import Tool\n",
      "from langchain.utilities import GoogleSearchAPIWrapper\n",
      "\n",
      "\n",
      "\n",
      "\n",
      "search = GoogleSearchAPIWrapper( google_api_key = \"YOUR_API_KEY\", google_cse_id=\"YOUR_CSE_ID\")\n",
      "\n",
      "tools = [\n",
      " Tool(\n",
      " name = \"現在の検索\",\n",
      " func=search.run,\n",
      " description=\"現在の出来事や世界の現状に関する質問に答える必要がある場合に役立ちます\"\n",
      " )\n",
      "]"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": 35,
      "id": "936be6b3-11b8-4a95-9d1a-edb26e7d1509",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "from langchain.llms import Bedrock\n",
      "from langchain.agents import initialize_agent\n",
      "from langchain.agents import AgentType\n",
      "\n",
      "\n",
      "llm = Bedrock(model_id=\"anthropic.claude-v2\", model_kwargs={\"temperature\":0.1, \"max_tokens_to_sample\": 20000 })\n",
      "\n",
      "agent_chain = initialize_agent(\n",
      " tools,\n",
      " llm,\n",
      " agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,\n",
      " verbose=True,\n",
      " handle_parsing_errors=True,\n",
      ")"
      ]
      },
      {
      "cell_type": "code",
      "execution_count": null,
      "id": "645d010f-8a31-4790-8a43-cedfe242c747",
      "metadata": {
      "tags": []
      },
      "outputs": [],
      "source": [
      "agent_chain.run(input=\"Amazon Lexにアクセスするためにクライアントが満たすべきTLSの要件を日本語で教えてください\")"
      ]
      }
      ],
      "metadata": {
      "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
      },
      "language_info": {
      "codemirror_mode": {
      "name": "ipython",
      "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.1"
      }
      },
      "nbformat": 4,
      "nbformat_minor": 5
      }

      ハルシネーションの性能検証やってみた

      実施内容

      • AWS Bedrockのみ
        • LangChainでClaudeを利用
      • AWS Kendraと連携
      • Agents for Amazon Bedrock(Google Cloud APIs)

      カーネルとして、conda_pytorch_р310を利用しています。
      ノートブックにあるLangChainはLLMに接続してクエリを実行するために使用します。

      今回は、以下質問に対してハルシネーションが発生するかを検証します。
      質問:Amazon Lexにアクセスするためにクライアントが満たすべきTLSの要件を日本語で教えてください
      期待する回答:クライアントは TLS(Transport Layer Security)1.0 をサポートしている必要がある

      参考:Amazon Lex のインフラストラクチャセキュリティ

       

      AWS Bedrockのみ(Claude)

      Bedrockで選択可能なモデルClaudeを利用してハルシネーションの発生を確認します。

       Amazon Lex にアクセスするクライアントは、以下のTLS要件を満たす必要があります:

      - TLS 1.2 以上のバージョンを使用すること
      - Perfect Forward Secrecy (前方秘密) をサポートした暗号スイート (ECDHE-ECDSA-AES128-GCM-SHA256 など) を使用すること
      <略>

      ハルシネーションが発生しています。

       

      AWS Kendraと連携

      Kendra側で事前に用意しているAWSドキュメントをLLMに渡してRAGをします。
      公式ドキュメントのAmazon Lex、Amazon Kendra、Amazon SageMakerをデータソースに設定しています。

      ' Amazon Lexにアクセスするために、クライアントは少なくともTLS 1.0以上をサポートしている必要があります。TLS 1.2以降が推奨されています。また、PFSを使用した暗号スイート(DHEやECDHEなど)もクライアントでサポートされている必要があります。'

      含めて正しい回答ができています(ハルシネーションが発生していません)。
      なお、暗号スイートの関連情報も出ておりますが、そちらも正しい回答です。
      検索する対象が決まっており、正しい回答を得たい場合、今回比べた3つのパターンではこちらが適していると思われます。

      余談: ドキュメントに記載のない内容を質問すると、以下のように情報がない旨が返答されます。

      '鎌倉ぽっぽとはなんですか'

      ' すみませんが、「鎌倉ぽっぽ」についての情報が十分ではありません。答えられるほどの情報がないので、「鎌倉ぽっぽとはなんですか」という質問に答えることができません。ご質問の意味がよく分からないので、もっと情報を教えていただけるとありがたいです。'

       

      Agents for Amazon Bedrock(Google Cloud APIs)

      Agentを利用して、回答を行います。
      外部アプリケーションとしてGoogle Cloud APIsを利用します。

      Amazon Lexは、クライアントとの通信にTLS 1.2以上を必須としています。LexコンソールやAPIはHTTPSを使用し、サーバ証明書はAmazonが発行するものが使われます。クライアント証明書の検証は任意ですが推奨されています。暗号スイートはPCI DSSに準拠しています。  クライアントもこれらの要件を満たす必要があります。 

      ハルシネーションが発生しています。
      インターネットの情報を参照しているからといって、正確な回答がくるとは限らないことがわかりました。

      【参考】

      LangChain とは?