COLUMN

Agents for Amazon Bedrockを用いたアプリケーション実装例をご紹介!【Amazon Connect × 生成AI】

こんにちは。エンジニアの森です。

今回は、現在NTT東日本が開発に力を入れているContact Center as a Service(CCaaS)プロダクトで使用されている技術を解説していきます。

※CCaaSについては、こちらの記事で詳しくご紹介していますのでこちらもぜひご覧ください。

CCaaSプロダクトの開発には生成AIを使用することも多くなってきており、AWSのAIエージェントを簡単に作成することのできるAgents for Amazon Bedrockを用いることも増えてきました。

そこで本記事では、Amazon Connect × Lex × Agents for Amazon Bedrockを使用した簡単なアプリ作成方法を画像付きでご紹介します。

Amazon Connect×生成AIを活用したコンタクトセンターについてNTT東日本のクラウドエンジニアにお悩みや課題についてのご相談などありましたらぜひお気軽にご相談ください!

1. Agents for Amazon Bedrockの「Agents」とは?

今回使用するメインサービスであるAgents for Amazon BedrockのAgentsについて簡単にご紹介します。

Agents for Amazon Bedrockは、冒頭でもご紹介した通り、AIエージェントをマネジメントコンソール上で操作するだけで簡単に作成できるサービスです。

では、AIエージェントとはどんなものでしょう?

AWS公式サイトによるとAIエージェントは「環境と対話し、データを収集し、そのデータを使用して自己決定タスクを実行して、事前に決められた目標を達成するためのソフトウェアプログラムです。」とあります。

つまり、生成AI(LLM)が持っている知識だけでなく、与えられたタスクを解決するために必要だと判断すれば外部のデータを自ら取得して解決するものです。

例えば、生成AIに「2024年の日本の祝日を教えて」と入力しても生成AIには最新の情報を持っていないため、答えることができないか昔の情報を(嘘の情報・ハルシネーション)を答えてしまいます。

もし、AIエージェントを使わずにこれを実現するアプリケーションを作ろうと思ったら生成AIの前段にチャットの仕組みや日本の祝日情報を取得するコードが必要となってしまい、毎回Lambdaを介す必要が出てきます。

しかし、AIエージェントを使用することでエージェントが自ら考え、祝日の情報が必要だと判断したらデータを取得した上で答えてくれるといった自律的な行動ができるようになります。

自分の知識で答えられる場合は余計な処理は行わずに答えることができます。

Agents for Amazon Bedrockは、このようなAIエージェントを簡単に作成できるサービスです。

Amazon Connect×生成AIを活用したコンタクトセンターについてNTT東日本のクラウドエンジニアにお悩みや課題についてのご相談などありましたらぜひお気軽にご相談ください!

2. 構築するアーキテクチャ

前置きが長くなってしまいましたが、今回構築するアーキテクチャはこちらです。

Amazon Connectで受けた電話をLexに取り込み、文字起こしを行います。

文字起こししたデータをLambda関数経由でAgents for Amazon Bedrock(Agents)に渡します。

Agentsは、必要な場合に祝日情報を取得するLambda関数を呼び出してデータを取得し、人間が分かりやすい形に整形して返却します。

返却されたデータは再度Lex経由でAmazon Connectに渡され、読み上げられます。

2024年8月9日現在、東京リージョンにおいてAgentsではClaude 3 Haikuが使用できないため、上記アーキテクチャ図の通り、Amazon ConnectからAgentsを起動するLambda関数までを東京リージョン、Agents以降を米国東部リージョンで作成します。

記事公開時点では東京リージョンでもClaude 3 Haikuが使えるようになっていたのですべて東京リージョンで作成いただいても構いません。

Amazon Connect×生成AIを活用したコンタクトセンターについてNTT東日本のクラウドエンジニアにお悩みや課題についてのご相談などありましたらぜひお気軽にご相談ください!

3. Amazon Connect、Lex、Agents for Amazon Bedrockを用いたアプリケーション実装方法解説

3-1. 日本の祝日情報を取得するLambda関数を作成(us-east-1リージョン)

まず、日本の祝日情報を取得するLambda関数をus-east-1リージョンに作成していきます。

Lambdaコンソールを開き、米国東部(バージニア北部)になっていることを確認し、「関数の作成」をクリックします。

オプションの「一から作成」を選択し、関数名を「getJapanHoliday」、ランタイムを「Python 3.12」として「関数の作成」をクリックします。

関数のコードは以下とします。

import json
import boto3
import requests
 
def get_japan_holidays():
    url = "https://holidays-jp.github.io/api/v1/date.json"
    response = requests.get(url)
    if response.status_code == 200:
        return response.json()
    else:
        print(f"Failed to fetch holidays: {response.status_code}")
        return {}
 
def lambda_handler(event, context):
    print('Received event:' + json.dumps(event, ensure_ascii=False))
 
    # 日本の祝日を取得
    japan_holidays = get_japan_holidays()
 
    agent = event['agent']
    actionGroup = event['actionGroup']
    function = event['function']
    parameters = event.get('parameters', [])
 
    response_body = {
        'TEXT': {
            'body': json.dumps(japan_holidays, ensure_ascii=False)
        }
    }

    function_response = {
        'actionGroup': event['actionGroup'],
        'function': event['function'],
        'functionResponse': {
            'responseBody': response_body
        }
    }

    session_attributes = event['sessionAttributes']
    prompt_session_attributes = event['promptSessionAttributes']

    action_response = {
        'messageVersion': '1.0', 
        'response': function_response,
        'sessionAttributes': session_attributes,
        'promptSessionAttributes': prompt_session_attributes
    }
 
    return action_response

コード内でimportしているrequestsはレイヤーとして設定しますのであらかじめレイヤーにrequestsを含め、アップロードしておきます。(今回はrequestsLayerという名前でアップロードしておきました)

getJapanHoliday関数の詳細を開き、「コード」タブの下部にある「レイヤー」セクションから「レイヤーの追加」を選択します。

カスタムレイヤーを選択し、requestsLayerのバージョン1を選択して追加します。

「設定」タブの一般設定を編集し、「タイムアウト」値を10秒に変更します。

3-2. Agents for Amazon Bedrockの作成(us-east-1リージョン)

Agentsを作成します。

今回作成するAgentsは、アクショングループを1つ作成し、そのアクショングループから呼び出されるLambda関数が日本の祝日情報を取得するというものです。

Bedrockのコンソール画面からオーケストレーション内のエージェントを選択し、「エージェントの作成」をクリックします。

今回は、エージェント名をsample-agentとします。

Agent detailsでは、以下を設定します。

  • モデル:Anthropic Claude 3 Haiku
  • エージェント向けの指示
あなたは、日本の祝日情報や曜日を答えることのできる親切なエージェントです。

タスク:
・日付から何曜日か計算を行って回答します。
・祝日情報は最新情報を取得し、回答を作成します。

その他はデフォルトのままです。

アクショングループの追加をクリックします。

以下を設定します。

  • アクショングループ名:sample-action-group
  • Action group invocation:Select an existing Lambda function
  • Lambda関数を選択:getJapanHoliday
  • Action group function 1内のName:getJapanHoliday
  • 説明:今年、去年、来年の3年間分の日本の祝日一覧を取得するAPI

画面上部から保存したのち、準備をクリックします。

画面右のテスト用チャットで「2024年の日本の祝日一覧は?」と質問すると回答が返ってきたらOKです。

「保存して終了」をクリックします。

画面上部の「エイリアスを作成」をクリックします。

任意のエイリアス名(ここでは001)を入力し、「エイリアスを作成」をクリックします。

これでAgentの完成ですが、エージェントIDとエージェントエイリアスIDは後程使用するため、控えておきます。

3-3. Agentsを呼び出すLambda関数の作成(ap-northeast-1リージョン)

Lexから呼び出され、Agentsから結果を取得するLambda関数を作成します。

Lambdaコンソールから関数の作成をクリックし、オプションの「一から作成」を選択、関数名を「sample-InvokeAgents」、ランタイムを「Python 3.12」として「関数の作成」をクリックします。

関数のコードは以下とします。

※エージェントIDとエージェントエイリアスIDは先ほど控えたものに置き換えてください。

import json
import boto3
from uuid import uuid4
 
def elicit_intent():
    return {
        'messages': [{'contentType': 'PlainText', 'content': ','}],
        'sessionState': {
            'dialogAction': {
                'type': 'ElicitIntent',

            },
            'intent': {
                'name': 'FallbackIntent',
                'state': 'Fulfilled'
            }
        },
    }
def close(answer):
    return {
        'messages': [{'contentType': 'PlainText', 'content': ','}],
        "sessionState": {
            "sessionAttributes": {
                "answer": answer
            },  
            'dialogAction': {
                'type': 'Close',
            },
            'intent': {
                'name': 'FallbackIntent',
                'slots': {},
                'state': "Fulfilled"
            }
        }
    }
 
def lambda_handler(event, context):
    print('Received event:' + json.dumps(event, ensure_ascii=False))
    if not event['inputTranscript']:
        return elicit_intent()
    input_text = event['inputTranscript']
    print('Received input_text:' + json.dumps(input_text, ensure_ascii=False))
 
    client = boto3.client('bedrock-agent-runtime', region_name='us-east-1')
    agent_id = 'エージェントID'
    agent_alias_id = 'エージェントエイリアスID'
    session_id = str(uuid4())
 
    response = client.invoke_agent(
        agentId=agent_id,
        agentAliasId=agent_alias_id,
        sessionId=session_id,
        inputText=input_text,
    )
 
    completion = ""
    for res in response.get("completion"):
        chunk = res["chunk"]
        completion += chunk["bytes"].decode("utf-8")
 
    print('Received completion:' + json.dumps(completion, ensure_ascii=False))
 
 
    return close(completion)

「設定」タブの一般設定を編集し、「タイムアウト」値を1分に変更します。

「設定」タブのアクセス権限から実行ロールを選択し、「AmazonBedrockFullAccess」ポリシーを付与します。

3-4. Amazon Lexボットの作成(ap-northeast-1リージョン)

Amazon Connectから起動され、音声データの文字起こしを行い、3-3で作成したLambda関数を呼び出すLexボットを作成します。

LexコンソールからBotsを選択し、「Create bot」をクリックします。

Step 1では、Bot nameをsample-botと設定し、次に進みます。

Step 2では、Select languageをJapanese(JP)とし、「Done」をクリックします。

作成したbotの詳細画面に遷移するのでIntentsのNewIntentを作成します。

Sample utterancesに「ダミー」と入力し、「Add utterance」をクリックします。

Code hooksのチェックボックスにチェックを入れ、「Save intent」で保存します。

※FallbackIntentのCode hooksにも同様にチェックを入れておきます。

作成したBot詳細のDeployment→Aliasesを選択し、TestBotAliasをクリックします。

Languages内のJapanese(Japan)をクリックします。

Sourceに先ほど作成した「sample-invokeAgents」を選択、Lambda function version or aliasは「$LATEST」を選択し、「Save」をクリックします。

Bot詳細のIntents一覧画面右上の「Build」からビルドを行います。

AliasesからTestBotAliasを選択し、Resource-based policy内のResourceのARNは後程使用するため、控えておきます。

3-5. Amazon Connectの設定(ap-northeast-1リージョン)

最後に入力された音声を3-4で作成したLexボットに渡し、返却されたデータを再生するAmazon Connectコンタクトフローを作成します。

ここでは、以下はすでに実施済である想定で進めていきます。

Amazon Connectコンソールから設定したいインスタンスを選択→問い合わせフロー→先ほど作成したボットを選択して、「Amazon Lexボットを追加」をクリックします。

以下のコードをコピーし、jsonファイルとして保存します。

125行目botAliasArnは、3-4で控えておいたLexボットのARNに置き換えます。

{
 "Version": "2019-10-30",
 "StartAction": "7fb43c40-fee6-4e3d-87f1-d956529b9032",
 "Metadata": {
   "entryPointPosition": {
     "x": -464.8,
     "y": 46.4
   },
   "ActionMetadata": {
     "7fb43c40-fee6-4e3d-87f1-d956529b9032": {
       "position": {
         "x": -328,
         "y": 17.6
       },
       "children": [
         "3321c6e6-abc6-4a26-9811-b4cb1bdec88d"
       ],
       "overrideConsoleVoice": true,
       "fragments": {
         "SetContactData": "3321c6e6-abc6-4a26-9811-b4cb1bdec88d"
       },
       "overrideLanguageAttribute": true
     },
     "3321c6e6-abc6-4a26-9811-b4cb1bdec88d": {
       "position": {
         "x": -328,
         "y": 17.6
       },
       "dynamicParams": []
     },
     "d1a2eed0-afb1-4340-a72d-08cfc78065c7": {
       "position": {
         "x": -80.8,
         "y": 14.4
       },
       "parameters": {
         "LexV2Bot": {
           "AliasArn": {
             "displayName": "TestBotAlias",
             "useLexBotDropdown": true,
             "lexV2BotName": "sample-bot"
           }
         }
       },
       "useLexBotDropdown": true,
       "lexV2BotName": "sample-bot",
       "lexV2BotAliasName": "TestBotAlias",
       "conditionMetadata": []
     },
     "52c238f2-44db-47de-bb64-cff7d433ff8d": {
       "position": {
         "x": 826.4,
         "y": 51.2
       }
     },
     "3e501011-e8ef-4623-bc8b-8a9d8a8040df": {
       "position": {
         "x": 604.8,
         "y": 216
       }
     },
     "ac1178f8-a26a-4a95-b40b-22f9b6d0c662": {
       "position": {
         "x": 158.4,
         "y": 16
       },
       "parameters": {
         "Attributes": {
           "answer": {
             "useDynamic": true
           }
         }
       },
       "dynamicParams": [
         "answer"
       ]
     },
     "1f924c53-4bdf-4cd6-8395-2dc33923d487": {
       "position": {
         "x": 399.2,
         "y": 8
       }
     }
   },
   "Annotations": [],
   "name": "sample-contact-flow",
   "description": "",
   "type": "contactFlow",
   "status": "PUBLISHED",
   "hash": {}
 },
 "Actions": [
   {
     "Parameters": {
       "TextToSpeechEngine": "Neural",
       "TextToSpeechStyle": "None",
       "TextToSpeechVoice": "Kazuha"
     },
     "Identifier": "7fb43c40-fee6-4e3d-87f1-d956529b9032",
     "Type": "UpdateContactTextToSpeechVoice",
     "Transitions": {
       "NextAction": "3321c6e6-abc6-4a26-9811-b4cb1bdec88d"
     }
   },
   {
     "Parameters": {
       "LanguageCode": "ja-JP"
     },
     "Identifier": "3321c6e6-abc6-4a26-9811-b4cb1bdec88d",
     "Type": "UpdateContactData",
     "Transitions": {
       "NextAction": "d1a2eed0-afb1-4340-a72d-08cfc78065c7",
       "Errors": [
         {
           "NextAction": "d1a2eed0-afb1-4340-a72d-08cfc78065c7",
           "ErrorType": "NoMatchingError"
         }
       ]
     }
   },
   {
     "Parameters": {
       "Text": "要件をお話しください。",
       "LexV2Bot": {
         "AliasArn": "botAliasArn"
       }
     },
     "Identifier": "d1a2eed0-afb1-4340-a72d-08cfc78065c7",
     "Type": "ConnectParticipantWithLexBot",
     "Transitions": {
       "NextAction": "3e501011-e8ef-4623-bc8b-8a9d8a8040df",
       "Errors": [
         {
           "NextAction": "ac1178f8-a26a-4a95-b40b-22f9b6d0c662",
           "ErrorType": "NoMatchingCondition"
         },
         {
           "NextAction": "3e501011-e8ef-4623-bc8b-8a9d8a8040df",
           "ErrorType": "NoMatchingError"
         }
       ]
     }
   },
   {
     "Parameters": {},
     "Identifier": "52c238f2-44db-47de-bb64-cff7d433ff8d",
     "Type": "DisconnectParticipant",
     "Transitions": {}
   },
   {
     "Parameters": {
       "Text": "エラーになりました。通話を終了します。"
     },
     "Identifier": "3e501011-e8ef-4623-bc8b-8a9d8a8040df",
     "Type": "MessageParticipant",
     "Transitions": {
       "NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
       "Errors": [
         {
           "NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
           "ErrorType": "NoMatchingError"
         }
       ]
     }
   },
   {
     "Parameters": {
       "Attributes": {
         "answer": "$.Lex.SessionAttributes.answer"
       },
       "TargetContact": "Current"
     },
     "Identifier": "ac1178f8-a26a-4a95-b40b-22f9b6d0c662",
     "Type": "UpdateContactAttributes",
     "Transitions": {
       "NextAction": "1f924c53-4bdf-4cd6-8395-2dc33923d487",
       "Errors": [
         {
           "NextAction": "3e501011-e8ef-4623-bc8b-8a9d8a8040df",
           "ErrorType": "NoMatchingError"
         }
       ]
     }
   },
   {
     "Parameters": {
       "Text": "エージェントからの回答です。\n$.Attributes.answer"
     },
     "Identifier": "1f924c53-4bdf-4cd6-8395-2dc33923d487",
     "Type": "MessageParticipant",
     "Transitions": {
       "NextAction": "52c238f2-44db-47de-bb64-cff7d433ff8d",
       "Errors": [
         {
           "NextAction": "3e501011-e8ef-4623-bc8b-8a9d8a8040df",
           "ErrorType": "NoMatchingError"
         }
       ]
     }
   }
 ]
}

Amazon Connectインスタンス詳細のAccess URLもしくはEmergency accessからログインします。

フローを選択します。

「フローを作成」をクリックします。

右上の▼からインポート(ベータ)を選択し、先ほど保存したjsonファイルを選択して取り込み、コンタクトフローを作成します。

以下のようなコンタクトフローができたはずです。

右上の「保存」を選択し、「公開」します。

以下のように正常発行されたことを確認します。

※エラーになってしまった場合、「顧客の入力を取得する」ブロックを選択し、Botを再度選択してみてください。

電話番号を選択します。

インポートしたコンタクトフローを紐づけたい電話番号を選択し、コンタクトフロー/IVRから該当コンタクトフローを選択し、保存します。

Amazon Connect×生成AIを活用したコンタクトセンターについてNTT東日本のクラウドエンジニアにお悩みや課題についてのご相談などありましたらぜひお気軽にご相談ください!

4. 動作確認

実際に電話をかけて動作確認を行います。

まずは、「2025年の日本の祝日を教えて」と聞いてみます。

【回答】

エージェントからの回答です。

2025年の日本の祝日は以下の通りです

1月1日 - 元日

1月13日 - 成人の日

2月11日 - 建国記念の日

2月23日 - 天皇誕生日

3月20日 - 春分の日

4月29日 - 昭和の日

5月3日 - 憲法記念日

5月4日 - みどりの日

5月5日 - こどもの日

7月21日 - 海の日

8月11日 - 山の日

9月15日 - 敬老の日

9月23日 - 秋分の日

10月13日 - スポーツの日

11月3日 - 文化の日

11月23日 - 勤労感謝の日

ハイフンを「から」と呼んでしまうなど課題はありますが、正確に祝日を答えてくれます。

少し聞き方を変えて「2025年9月15日は祝日ですか?祝日なら何の祝日ですか?」と聞いてみます。

【回答】

エージェントからの回答です。

2025年9月15日は祝日で、「敬老の日」です。

敬老の日で祝日であると正常な答えを返してくれます。

では祝日は関係ない話題として「2025年9月27日は何曜日ですか?」と聞いてみます。

【回答】

エージェントからの回答です。

2025年9月27日は土曜日です。

計算をして土曜日であると正確な答えとなりました。

Amazon Connect×生成AIを活用したコンタクトセンターについてNTT東日本のクラウドエンジニアにお悩みや課題についてのご相談などありましたらぜひお気軽にご相談ください!

5. まとめ

今回は、Amazon Connect × Lex × Agents for Amazon Bedrockを使用した簡単なアプリ作成方法をご紹介しました。

このようにCCaaSプロダクトはAWSの様々なサービスを利用して開発を行っています。

今回は、CCaaSの文脈に沿ってAmazon Connectを使用しましたが、Amazon Connectを使用しなくても様々なプロダクトに使用できる可能性があると感じていますのでぜひ参考にしてください。

NTT東日本では、今回ご紹介した生成AIやAIエージェントを使用したCCaaSプロダクト開発に力を入れています。

コンタクトセンターでお困りのことがある方はぜひお問い合わせください。

コンタクトセンターではないが、クラウド導入でお困りという方ももちろん大歓迎です!

ページ上部へ戻る

相談無料!プロが中立的にアドバイスいたします

クラウド・AWS・Azureでお困りの方はお気軽にご相談ください。