3
2

Bedrockを使って、S3に置いた独自データに応答してくれるチャットボットを作ってみた。

Last updated at Posted at 2024-01-16

はじめに

AWS re:Invent 2023 でGAとなったKnowledge Bases for Amazon BedrockとAgents for Amazon Bedrockを使用するとS3バケットに配置したオブジェクトを対象としたRAGが簡単に構築できるということで、試してみました。Bedrockのコンソールで試すだけでなく、Agents for Amazon Bedrock(以下、Agents)をチャットボットで呼び出すSlack Appも作ってみました。

本記事では、Vector storeにPinecone(Free Plan)を使用します。Knowledge Bases for Amazon Bedrock(以下、knowledge base)のVectore database作成画面ではデフォルトでOpenSearch ServerlessにVector storeが構築される流れになっていますが、時間単位の課金で思わぬ費用が発生しないようPineconeを選択しました。

今回は、FIAとJAFモータースポーツのサイトで公開されているF1世界選手権の技術規則書と競技規則書をもとにRAGを作成し、F1のレギュレーションについて応答するチャットボットを作成します。

デモ画面

Agentsのレスポンスに参照ファイルのパスと参照箇所が含まれていれば、それらも出力できます。もちろん、会話の履歴を踏まえた回答が得られます。
参照元に同じ内容が2回出力されていますが、おそらく参照した2箇所が含まれる広い範囲をレスポンスしているためこのようになっているのだと思います。

(ボットの名前にbacklogと入っていますが、今回はbacklogは関係ありません。)

Agentsとの会話を行うSlackボット

構成図

構成図

  1. PDFなどのファイルをS3バケットにアップロード
  2. knowledge baseが埋め込みを作成し、Pineconeに登録/更新
  3. SlackからのクエリをAgentsが受け取る
  4. Agentsはknowledge baseと対話し、クエリを送る。
  5. knowledge baseはクエリに応じて意味的に最も関連性の高いコンテンツをPineconeから取得しAgentsに返す
  6. クエリの結果をSlackに返す

参考情報

前提条件

  • 以下のリソースを作成・削除・変更できる権限をもつAWSユーザーを利用すること
    • AWS IAM
    • AWS Lambda
    • AWS CloudFormation
    • AWS Secrets Manager
    • Amazon API Gateway
    • Amazon S3
    • Amazon CloudWatch Logs
    • Amazon Bedrock
  • 使用するAWSリージョンは、us-east-1 (記事執筆時点でknowledge baseとAgentsが利用可能なリージョンがus-east-1,us-west-2のみのため)
  • Slack Appを作成するためのアカウントや権限を持っている
  • Pineconeにサインイン可能なアカウント(Google, Github, Microsoftのいずれか)を持っている

環境構築

以降の手順は、Pinecone as a Knowledge Base for Amazon Bedrockの記事を参考にしました。記事に書かれている手順に従って進めていきます。

PineconeのサインアップとIndexの作成

Pineconeのサイトにアクセスし、Get Started (もしくは画面右上の Sign Up Free)からアカウント作成画面に進みます。

www.pinecone.io Pineconeトップページ

Google, Github, Microsoftのいずれかのアカウントでログインします。もしくは、メールアドレスを入力しアカウントを作成します。

Pineconeログイン

質問に回答しサインアップを完了させます。どれを選択してもよいと思いますが、今回は以下のとおり選択しました。

質問 回答
Waht is your goal? Personal Lerning and development
What are you making? Chatbot application
What is your preferred coding launguage? Pyhon

Pineconeサインアップ時のアンケート

Create IndexをクリックしてIndexを作成します。
Pinecone サインアップ後の画面

入力/選択項目 内容
Name 任意のIndex名。 使用可能な文字は小文字、数字、ハイフンのみ
Dimensions 1536
Metric cosine
Pod Type starter

後ほど作成するknowledge baseの埋め込みモデルTitan Embeddings G1 - Text v1.2が1536次元のため、Indexを1536次元で作成します。Pod Typeは、Free Planではstarterのみ選択可能です。Free Planは作成可能なIndex数が1個なので、別のIndexを作成する場合は既存のものを削除するか上位プランの契約が必要です。

Pinecone インデックス作成画面

Indexの作成が完了すると、このようにIndexes画面にHOSTアドレスが、API Keys画面にAPIキーが表示されます。Environmentがawsではなくgcpとなっていますが、Free Planではこの選択しかできません。HOSTアドレスとAPIキーがあればknowledge baseから接続できるので、Environmentは気にしなくてよさそうです。
HOSTアドレスは後ほど作成するknowledge baseの設定で使用します。

Pinecone インデックスのHOSTアドレス

Pinecone APIキー

PineconeのAPI KeyをSecrets Managerに登録

AWS Secrets Managerを開き、シークレットの登録を行います。

入力/選択項目 内容
シークレットのタイプ その他のシークレットのタイプ
キー apiKey
PineconeのAPIキー

PineconeのAPI KeyをSecrets Managerに登録

シークレットの名前と説明を入力します。

シークレットの名前と説明を入力

ローテーションの設定はすべてデフォルトのままにします。

ローテーションの設定はすべてデフォルトのままにします

シークレットの作成が完了すると、Secret ARNが発行されます。
Secret ARNは後ほど作成するknowledge baseの設定で使用します。

シークレットの作成が完了すると、Secret ARNが発行されます

Bedrock環境構築

Knowledge Bases for Amazon Bedrock

ファイルをS3バケットにアップロード

任意のS3バケットにファイルをアップロードします。knowledge baseをus-east-1に作成するためリージョン間の通信費用を考慮し、S3バケットのリージョンもus-east-1としたほうが良いと思います。
knowledge baseがサポートするファイルフォーマットは、Set up your data for ingestionに記載されています。
引用すると以下のとおりです。また、1ファイルあたりの最大ファイルサイズは50MBです。

  • Plain text (.txt)
  • Markdown (.md)
  • HyperText Markup Language (.html)
  • Microsoft Word document (.doc/.docx)
  • Comma-separated values (.csv)
  • Microsoft Excel spreadsheet (.xls/.xlsx)
  • Portable Document Format (.pdf)

ここでは、JAFモータースポーツのサイト内の国際モータースポーツ諸規則などで公開されているFIAレース世界選手権等規則書のなかから2023年競技規則_日本語版_202304252023年技術規則_日本語版_20230425の2つのPDFファイルを、FIAのサイト内のREGULATIONSで公開されているFIA FORMULA ONE WORLD CHAMPIONSHIPのドキュメントのなかからFIA 2023 Formula 1 Sporting Regulations - Issue 8 - 2023-12-06FIA 2023 Formula 1 Technical Regulations - Issue 7 - 2023-08-31をそれぞれS3バケットにアップロードしています。

knowledge baseを作成

knowledge baseを開き、knowledge baseを作成します。

入力/選択項目 内容
Knowledge base name 任意のknowledge base名
Runtime role Create and use a new service role

ロールは新規に作成します。以前に作成したknowledge baseのロールが存在していても、Use an existing service roleで既存のロールが選択肢に表示されませんでした。

knowledge base名の入力

データソースとして、ファイルをアップロードしたS3バケットを指定します。

入力/選択項目 内容
Data source name 任意のデータソース名
S3 URI S3バケットのURI
Chunking strategy Default chunking

S3バケットのURIは、直接URIを入力するか、Browse S3をクリックしてバケット一覧からバケットを選択することで入力できます。
Chunking strategyは、チャンク分割の設定を選択します。knowledge base作成後に設定を変更することができないため、この時点で選択が必要です。それぞれどのように分割するのかはプルダウンメニュー内に記載されています。また、Set up your data for ingestionにも記載があります。

データソースとして、ファイルをアップロードしたS3バケットを指定

Vectore storeの設定

つづいて、Vector storeを設定します。
埋め込みモデルは、Titan Embeddings G1 - Text v1.2のみ利用可能です。PineconeのIndexを作成する際に設定した Dimensions: 1536 はここに明記されています。

埋め込みモデルの選択

入力/選択項目 内容
Select how you want to create your vector store Choose a vector store you have created
Select an existing database Pinecone
By selecting "Pinecone"...
Connection String PineconeのIndexのHOSTアドレス
Credentials secret ARN PineconeのAPIキーを登録したSecretsのARN
Text field Vector databaseのテキストフィールドの任意の名前。 textなど
Bedrock-managed metadata field Vector databaseのメタデータフィールドの任意の名前。 metadataなど

By selecting "Pinecone"... の項目は、AWSがユーザーに代わってサードバーティーのソースにアクセスすることを許可することに同意する旨が書かれています。

Vectore storeの設定

knowledge baseは作成されましたが、まだS3バケットのデータはPineconeに登録されていません。作成したknowledge baseのData Sourceの項目に移動し、Syncボタンをクリックします。S3バケット内のデータに追加/更新があった場合も同様にSyncを実行します。

PineconeとSync

Statusが Ready となれば完了です。PineconeのIndex画面にアクセスすると、このようにデータが登録されていることが分かります。

Pineconeに構築されたインデックス

Agents for Amazon Bedrock

Agentsの設定

Agentsを開き、Agentを作成します。

入力/選択項目 内容
Agent name 任意のAgents名
Agent description Agentsの説明文
User Input Yes
IAM Permissins 新規にロールを作成する場合は、Create and use a new service role。既存のロールを利用する場合は Use an existing service role
Idle session timeout 30 Minutes

Agent作成

"User Input" は、Agentsが回答するための十分な情報がない場合に、ユーザーに追加の情報を求めることができるかを選択します。Yes,Noのどちらを選択しても良いのですが、Yesのほうが会話らしくなると思います。
"Idle session timeout"は、入力が何もない状態でセッションを維持する時間。セッションが有効な間であれば、以前の会話履歴を引き継いで会話を続けることができます。

Agentsが使用する言語モデルを選択します。記事執筆時点では、Anthropic社の"Claude instant V1","Claude instant V2", "Claude instant V2.1"のみが選択可能です。
"Amazon Titan Express"というモデルが近々公開されるようです。

Agentsの言語モデルを選択

Amazon Titan Expressは選択不可

"Instructions for the Agent"には、エージェントが実行するタスクについて明確かつ具体的な指示を入力します。スタイルやトーンを指示することも可能なので、特定の口調や語尾で会話するように指示することができるようです。

Add Action groupsの設定はスキップします。APIスキーマとそれを処理するLambda関数を指定することでOpenAI APIのFunction Callingと同じようなことが実現できるようです。

Action groupsの設定はスキップ

Agentsが連携するknowledge baseを選択し、それをいつ使用するのかどういった情報が含まれているのかをAgentsに伝えるための指示を入力します。

入力/選択項目 内容
Select knowledge base 先ほど作成したknowledge baseを選択
knowledge base instructions for Agent Agentsがknowledge baseと対話する際の指示を定義

今回の例では使用するknowledge baseはひとつだけですが、Add another knowledge baseを操作することで複数のknowledge baseをひとつのAgentsに紐付けることができます。

knowledge baseをいつ使用するのかどういった情報が含まれているのかをAgentsに伝えるための指示を入力

Agentsの設定が完了すると、概要がこのように表示されます。APIからAgentsを呼び出す際にIDを使用します。 AgentsのIDは後述のSecret Managerのシークレット登録に使用します。
右上のEditからAgentsの設定を変更することができます。

Agentsの設定概要

Agentsの動作確認

画面右にあるTest playgroundを使用すると、Agentsとのチャットを試す事ができます。この例では、S3バケットに置いたPDFドキュメントの内容について質問し、Agentsが応答しています。"もう少し詳細に説明してください。"という問い合わせに対して、会話履歴を踏まえた応答が得られました。

Agentsの動作確認

Agents エイリアスの作成

AgentsをAPIから呼び出す際は、Agents本体ではなくエイリアスを使用します。そこで、Agentsに対するエイリアスを作成します。
右上のCreateから新規作成画面に進みます。

Agents エイリアスの作

エイリアスの情報を入力します。

入力/選択項目 内容
Alias name 任意のエイリアス名
Description エイリアスの説明文
Create a new version and associate it to this alias. / Use an existing version to associate this alias. エイリアスを新しいバージョンと既存バージョンのどちらに紐付けるか

エイリアスの情報

エイリアスを作成すると、バージョンとともに以下のようにリストが表示されます。APIからAgentsを呼び出す際にAlias IDを使用します。 Alias IDは後述のSecret Managerのシークレット登録に使用します。

エイリアス一覧

knowledge baseの設定変更は、Working draftの項目から行えます。なかでもAdvanced promptsでは、言語モデルに対して前処理や後処理の詳細な指示テンプレートを上書き/拡張できます。
設定を変更した際はplaygroundで動作確認が行ったあと、エイリアスを作成します。

Agentsのドラフト

AgentsのAdvanced prompts

開発環境構築

作業環境のOSバージョン

Windows 11上のWSLでUbuntu 23.04を動かしています。

$ cat /etc/os-release | grep PRETTY_NAME
PRETTY_NAME="Ubuntu 23.04"

Python環境

$ python3 --version
Python 3.11.6
$ python3 -m venv .venv
source .venv/bin/activate
$ pip3 install --upgrade pip
$ pip3 --version
pip 23.3.1 from /home/xxx/.venv/lib/python3.11/site-packages/pip (python 3.11)

AWS環境構築

aws configureコマンドでデフォルトのリージョンやクレデンシャルを設定するか、もしくは~/.aws/configや~/.aws/credentialsを用意します。

AWS SAM CLIインストール

AWS上でサーバーレスアプリケーションを構築、実行するAWS SAMを使用します。

Installing the AWS SAM CLI の手順に従い、AWS SAM CLIをインストールします。今回はx86_64環境でLinux OSを使用するため、x86_64 - command line installerの手順を実行します。

$ sam --version
SAM CLI, version 1.107.0

version 1.104.0 ~ 1.106.0ではビルド時に不必要なWarningメッセージが出力されるため修正パッチが適用された1.107.0を使用します。

Slack Appの作成

Slac APIを開き、From scratchからSlack Appを作成します。ここでは、App Nameをaws-sam-bedrock-slack-ragとします。

Basic Information画面のApp Credentialsに表示されているクレデンシャルはSlackSigningSecretとして次のSecret Managerのシークレット登録に使用します。

App Credentials

OAuth & Permissions画面のOAuth Tokens for Your WorkspaceにあるBot User OAuth Tokenは、SlackBotTokenとして次のSecret Managerのシークレット登録に使用します。

OAuth Tokens for Your Workspace

OAuth & Permissions画面のBot Token Scopesにapp_mentions:readとchat:writeを追加します。

Bot Token Scopes

シークレット情報をSecret Managerに登録

あらたにシークレットを作成し、ここまでの手順で作成した以下のシークレット情報を登録します。

シークレットキー
SlackSigningSecret 前項のSlackのSigning Secret
SlackBotToken 前項のSlackのBot User OAuth Token
BedrockAgentId AgentsのID
BedrockAgentAliasId AgentsのエイリアスID

シークレット作成

アプリケーションの構築

ディレクトリ構造は以下のとおりです。

.
├── bedrock_slack_rag_app
│   ├── __init__.py
│   ├── app.py
│   └── requirements.txt
├── samconfig.toml
└── template.yaml

__init__.pyは空のファイルです。

requirements.txtは以下のとおりです。

slack-bolt
slack-sdk
boto3
template.yaml (長いので折りたたんでいます。クリックして展開)
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Slack Bedrock Assitant.

Resources:
  BedrockAssitantFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: bedrock_slack_rag_app/
      Handler: app.lambda_handler
      Runtime: python3.11
      Role: !GetAtt LambdaRole.Arn
      Timeout: 300
      MemorySize: 384
      Architectures:
        - x86_64
      Environment:
        Variables:
          SECRET_NAME: 'Bedrock-sam-secrets' # Name of the secret in Secrets Manager
          REGION_NAME: 'us-east-1' # Region of the secret in Secrets Manager
      Events:
        Slack:
          Type: Api
          Properties:
            Method: POST
            Path: /slack/events
      Layers:
        # https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html#ps-integration-lambda-extensions-add
        - arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11

  LambdaRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: bedrock-slack-rag-app-lambda-role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: allow-lambda-invocation
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                  - lambda:InvokeAsync
                Resource: "*"
        - PolicyName: SecretsManagerPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action: 'secretsmanager:GetSecretValue' # Required for Lambda to retrieve the secret
                Resource: "*"
        - PolicyName: allow-bedrock-access
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:InvokeModel
                  - bedrock:InvokeModelWithResponseStream
                Resource: "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2"
        - PolicyName: allow-bedrock-agent-access
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:InvokeAgent
                Resource: "*"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  BacklogAssitantLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${BedrockAssitantFunction}
      RetentionInDays: 14 # Optional. Default retention is 30 days.

Outputs:
  BedrockAssitantApi:
    Description: "The URL of Slack Event Subscriptions"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/slack/events"
  BedrockAssitantFunction:
    Description: "Bedrock Assistant Lambda Function ARN"
    Value: !GetAtt BedrockAssitantFunction.Arn
  BedrockAssitantFunctionIamRole:
    Description: "Implicit IAM Role created for Bedrock Assistant function"
    Value: !GetAtt LambdaRole.Arn

AWS SAM テンプレートファイル(template.yaml)に、作成するAWSリソースを定義します。Lambda関数用ロールやLambdaの環境変数、API Gatewayの設定が含まれます。
Lambda関数からSecrets ManagerにアクセスするためにはAWSレイヤーが必要で、手動で追加する場合は以下の画面でAWS-Parameters-and-Secrets-Lambda-Extensionを選択し、バージョンは11とします(11以外に選択肢がない)。

AWSレイヤーの設定

template.ymlで定義する場合は、LayersにAWS Parameters and Secrets Lambda ExtensionのARNを設定します。ARNは、以下のドキュメントに記載されています。

ほかにも、LambdaからBedrockやSecrets Managerを扱うためのポリシー設定を行っています。EnvironmentにあるSECRET_NAMEREGION_NAMEにはそれぞれ先ほど作成したSecrets Managerのシークレットの名前とリージョンを設定します。IAMポリシーの定義においてResource: "*"としていますが、本番環境などでは許可するリソースを特定したほうがよいでしょう。例えば、AgentsのResourceを特定する場合は以下のように指定します。

Resource: "arn:aws:bedrock:{Region}:{AWS Account ID}:agent-alias/{Agents ID}/{Agents Alias}"
samconfig.toml (長いので折りたたんでいます。クリックして展開)
# More information about the configuration file can be found here:
# https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-config.html
version = 0.1

[default]
[default.global.parameters]
stack_name = "bedrock-slack-rag-app"

[default.build.parameters]
cached = true
parallel = true

[default.validate.parameters]
lint = true

[default.deploy.parameters]
capabilities = "CAPABILITY_NAMED_IAM"
confirm_changeset = true
resolve_s3 = true
region = "us-east-1"

[default.package.parameters]
resolve_s3 = true

[default.sync.parameters]
watch = true

[default.local_start_api.parameters]
warm_containers = "EAGER"

[default.local_start_lambda.parameters]
warm_containers = "EAGER"

SAM CLIの実行設定ファイル(samconfig.toml)に、SAM CLIを実行する際の設定を定義します。AWS SAMのチュートリアル: Hello World アプリケーションのデプロイを実行した際に作成されるsamconfig.tomlをもとにしています。今回の例では、以下の点を変更しています。

  • [default.global.parameters]セクションのstack_nameを"sam-app"から"bedrock-slack-rag-appt"に変更
  • [default.deploy.parameters]セクションにregion指定を追加
  • [default.deploy.parameters]セクションのcapabilitiesを"CAPABILITY_IAM"から"CAPABILITY_NAMED_IAM"に変更
app.py (長いので折りたたんでいます。クリックして展開)
import ast
import json
import logging
import os
import re

import boto3
from botocore.config import Config
from botocore.exceptions import ClientError
from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler

SlackRequestHandler.clear_all_log_handlers()
logging.basicConfig(
    format="%(asctime)s [%(levelname)s] %(message)s",
    level=logging.INFO
)

logger = logging.getLogger(__name__)


class SecretsManager:
    def __init__(self, secret_name, region_name):
        self.secret_name = secret_name
        self.region_name = region_name
        self.client = boto3.client(
            service_name='secretsmanager',
            region_name=region_name
        )

    def get_secret(self, key):
        try:
            get_secret_value_response = self.client.get_secret_value(
                SecretId=self.secret_name
            )
        except ClientError as e:
            raise e

        secret_data = get_secret_value_response['SecretString']
        secret = ast.literal_eval(secret_data)

        return secret[key]


secrets_manager = SecretsManager(
    secret_name=os.environ.get("SECRET_NAME"),
    region_name=os.environ.get("REGION_NAME")
)

app = App(
    signing_secret=secrets_manager.get_secret("SlackSigningSecret"),
    token=secrets_manager.get_secret("SlackBotToken"),
    process_before_response=True,
)


def agent_client(message, session_id):
    agetn_id = secrets_manager.get_secret("BedrockAgentId")
    agent_alias_id = secrets_manager.get_secret("BedrockAgentAliasId")
    enable_trace = False

    client = boto3.client(
            service_name='bedrock-agent-runtime',
            region_name="us-east-1",
            config=Config(
                read_timeout=180,
            )
        )

    response = client.invoke_agent(
            inputText=message,
            agentId=agetn_id,
            agentAliasId=agent_alias_id,
            sessionId=session_id,
            enableTrace=enable_trace,
    )

    event_stream = response['completion']
    data, uri, text, references = "", "", "", ""
    try:
        for event in event_stream:
            if 'chunk' in event:
                data = event['chunk']['bytes'].decode("utf-8")

            # Check if 'attribution' and 'citations' exist in 'chunk'
            if 'attribution' in event['chunk']:
                for citation in event['chunk']['attribution']['citations']:
                    # Check if 'retrievedReferences' exist in 'citation'
                    if 'retrievedReferences' in citation:
                        for refs in citation['retrievedReferences']:
                            if 'location' in refs:
                                uri = refs['location']['s3Location']['uri']
                            if 'content' in refs:
                                text = refs['content']['text']
                            references += uri + "\n" + text[:100].replace('\n', '') + "...\n\n"
            if 'trace' in event:
                logger.info(json.dumps(event['trace'], indent=2))
    except Exception as e:
        raise Exception("Error in processing response: ", e) from e

    return data + "\n\n" + "参照元:\n" + references if references else data


def handle_app_mentions(event, say):
    thread_ts = event["ts"]
    userid = event["user"]
    message = re.sub("<@.*>", "", event["text"])

    say("\n\nお待ちください...", thread_ts=thread_ts)
    if "thread_ts" in event:
        thread_ts = event["thread_ts"]

    response = agent_client(message, thread_ts)
    say(f"<@{userid}> {response}", thread_ts=thread_ts)


def respond_to_slack_within_3_seconds(ack):
    ack()


app.event("app_mention")(
    ack=respond_to_slack_within_3_seconds,
    lazy=[handle_app_mentions]
)


def lambda_handler(event, context):
    logging.info(json.dumps(event))

    retry_counts = event.get("multiValueHeaders", {}).get("X-Slack-Retry-Num", [0])

    if retry_counts[0] != 0:
        logging.info("Skip slack retrying(%s).", retry_counts)
        return {}

    slack_handler = SlackRequestHandler(app=app)
    return slack_handler.handle(event, context)

ボットプログラムの本体です。Bolt for Python を使ったアプリをAWS Lambda上で動かしやすくするためのLazy Listenersを利用しています。SlackのクレデンシャルやAgentsのIDなどはSecrets Managerから取得しています。

ビルド

template.yamlがあるディレクトリで、ビルドコマンドを実行します。

$ sam build

ビルドに成功すると、以下のようなメッセージが表示されます。

Starting Build use cache
Manifest is not changed for (BedrockAssitantFunction), running incremental build
Building codeuri: /home/xxx/aws-sam-bedrock-slack-rag-app/bedrock_slack_rag_app runtime: python3.11 metadata: {} architecture:
x86_64 functions: BedrockAssitantFunction
 Running PythonPipBuilder:CopySource
 Running PythonPipBuilder:CopySource

Build Succeeded

Built Artifacts  : .aws-sam/build
Built Template   : .aws-sam/build/template.yaml

Commands you can use next
=========================
[*] Validate SAM template: sam validate
[*] Invoke Function: sam local invoke
[*] Test Function in the Cloud: sam sync --stack-name {{stack-name}} --watch
[*] Deploy: sam deploy --guided

デプロイ

ビルドでエラーがなければsam deployコマンドを実行し、デプロイを行います。

$ sam deploy

デプロイが成功すると、以下のような情報がコンソールに出力されます。

CloudFormation outputs from deployed stack
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Outputs
-------------------------------------------------------------------------------------------------------------------------------------------------------------
Key                 BedrockAssitantApi
Description         The URL of Slack Event Subscriptions
Value               https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/slack/events

Key                 BedrockAssitantFunction
Description         Bedrock Assistant Lambda Function ARN
Value               arn:aws:lambda:us-east-1:xxxxxxxxxxxx:function:bedrock-slack-rag-app-BedrockAssitantFunction-xxxxxxxxxxxx

Key                 BedrockAssitantFunctionIamRole
Description         Implicit IAM Role created for Bedrock Assistant function
Value               arn:aws:iam::xxxxxxxxxxxx:role/bedrock-slack-rag-app-lambda-role
-------------------------------------------------------------------------------------------------------------------------------------------------------------

Slack Appの設定

メンションイベントに応答するために、Event Subscriptions画面のSubscribe to bot eventsにapp_mentionを追加します。

Event Subscriptions

Event Subscriptions画面のEnable EventsをOnにし、Request URLにさきほど出力されたURL https://xxxxxxxxxx.execute-api.us-east-1.amazonaws.com/Prod/slack/events を入力します。
Verified✓ と表示されれば、正しいURLが入力されたことになります。レスポンスが得られるようになるまで時間がかかる場合があります。正しいURLを入力しているにもかかわらずVerifiedとならない場合は、時間をおいて再試行します。

Enable_events.png

設定を追加後、画面最下部にあるSave Changesをクリックし内容を保存します。

Slack Workspaseにアプリをインストール

Install App画面のInstall to Workspaceをクリックし、Slack AppをWorkspaceにインストールします。
インストールに成功すると、Thank you!画面が表示されます。Slackアプリをインストールしている場合はclick hereのリンク、Webブラウザを使用している場合は、this linkをクリックしてSlackを開きます。

動作確認

任意のチャンネンルに、@aws-sam-bedrock-slack-rag (Slack Appの作成時に設定した名前)を招待し、メンション形式で依頼をポストします。動作しない場合のログやOpenAI APIが返すレスポンスは CloudWatch Logs に出力されているログが参考になります。

作成したリソースの削除

最後に、作成したアプリケーションを削除する手順です。リソースを削除するには sam delete コマンドを実行します。

$ sam delete

まとめ

Knowledge Bases for Amazon BedrockとAgents for Amazon bedrock、Pineconeを使うことでRAGを手軽に構築できました。Vector Databaseを構築しAgentsで応答を試す工程まではノーコードで構築できるため、手元のPDFやWordドキュメントに対して生成AIがどのような応答を示すのか容易に試すことができます。knowledge baseにはあらかじめ前処理や後処理、応答のプロンプトが設定済みなので、複雑なプロンプトを意識しなくてよいのも利点です。もちろん、カスタマイズもできるため、より高度な利用も可能です。

少し手を加えて、S3オブジェクトの更新をトリガーにしてknowledge baseのSyncを実行するような仕組みを入れると、最新の情報に対してAgentsが応答するようなこともできそうです。
また、Agentsに対してboto3クライアントで接続できるため、既存のアプリケーションから呼び出したり、今回のようにチャットボットを作ったり用途はいろいろとありそうです。

記事執筆時点では、knowledge baseの埋め込みモデルやAgentsの言語モデルが限定されているため、日本語のドキュメントに対する応答が不自然に感じるところもありました。この点は、今後AWS側で改善されると期待しています。

3
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2