Open16

Amazon Bedrockの状況整理

Kento.YamadaKento.Yamada

冒頭

サービスは2023年9月28日にGAされた。偶然にも30歳の誕生日前にGAするというなんとも数奇な運命

Kento.YamadaKento.Yamada

現在発生している問題

2023年10月3日現在、Amazon BedrockをAWS Lambdaで実装しようとすると下記のような問題が発生します。

  • boto3のバージョンが古いため、bedrock-runtimeが呼び出せない

→Public ECRから持ってきているPythonイメージのバージョンが古いだけかもしれない。

Kento.YamadaKento.Yamada

解決方法

2023年10月3日現在、Amazon BedrockをAWS Lambdaで実装する場合
Lambdaのレイヤーをうまく活用します。

docker run --entrypoint "" -v "$PWD":/var/task "public.ecr.aws/lambda/python:3.11.2023.09.27.10" /bin/sh -c "mkdir -p /tmp/python && pip3 install boto3 -t /tmp/python && cd /tmp && yum install -y zip && zip -r /var/task/boto3-mylayer.zip ."

具体的にはzipでライブラリを固めてLambdaに配置します。

Kento.YamadaKento.Yamada

Q. コンテナ Lambdaで実装できないのか?
A. できない。理由としてはLambdaのレイヤーを実装しないといけないため

つまり、CI/CD コンテナLambda※を実践するためにはzipアップロードを前提にしないといけない。

※CI/CD コンテナLambdaの構成

Kento.YamadaKento.Yamada

LINE botを作る時のDockerfile

AWS Lambdaでレイヤーを組まなくても良くなり、Lambda上のboto3からAmazon Bedrockを実行できるようになった場合は以下のDockerfileを作ると良い。

FROM public.ecr.aws/lambda/python:3.8-x86_64 AS x86_64
COPY ./app.py   ./
RUN pip3 install LINE-bot-sdk
CMD ["app.lambda_handler"]
Kento.YamadaKento.Yamada

LINE botを作る時の使うPythonスクリプト

上記のDockerfileと併用して利用する。ファイル名はapp.py

app.py
import json
import os
import boto3
from boto3.session import Session

from linebot import LineBotApi
from linebot.models import TextSendMessage

channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)

line_bot_api = LineBotApi(channel_access_token)

# Headerの生成
HEADER = {
    'Content-type':
    'application/json',
    'Authorization':
    'Bearer ' + channel_access_token
}


def lambda_handler(event, context):

    print(f"boto3 version: {boto3.__version__}")
    body = json.loads(event['body'])

    # Webhookの接続確認用
    if len(body['events']) == 0:
        return {
            'statusCode': 200,
            'body': ''
        }

    # bodyからevent を取得する
    body_event = body['events'][0]
    reply_token = body_event['replyToken']

    message_text = body_event["message"].get("text")

    message_type = body_event['message']['type']
    event_type = body_event['type']

    if event_type == 'message':
        if message_type == 'text':
            session = boto3.Session(region_name='us-east-1')
            bedrock = session.client('bedrock-runtime')

            body = json.dumps({
                "prompt": "\n\nHuman:" + message_text + "\n\nAssistant:",
                "max_tokens_to_sample": 300,
                "temperature": 0.1,
                "top_p": 0.9,
            })

            modelId = 'anthropic.claude-v2'
            accept = 'application/json'
            contentType = 'application/json'

            response = bedrock.invoke_model(
                body=body, modelId=modelId, accept=accept, contentType=contentType)
            response_body = json.loads(response.get('body').read())

            # text
            reply_message_text = response_body.get('completion')

            line_bot_api.reply_message(
                reply_token,
                TextSendMessage(text=reply_message_text))

        elif message_type == 'image':
            line_bot_api.reply_message(
                reply_token,
                TextSendMessage(text='画像には対応しておりません'))

        elif message_type == 'sticker':
            line_bot_api.reply_message(
                reply_token,
                TextSendMessage(text='スタンプを受信しました。'))

        else:
            line_bot_api.reply_message(
                reply_token,
                TextSendMessage(text='メッセージタイプ' + message_type))

    elif event_type == 'postback':
        line_bot_api.reply_message(
            reply_token,
            TextSendMessage(text='タイプ:' + event_type))

上記のスクリプトでWebhookが通るところを確認済です。

Kento.YamadaKento.Yamada

LINE bot SDKのLambda レイヤーを作成する

docker run --entrypoint "" -v "$PWD":/var/task "public.ecr.aws/lambda/python:3.11.2023.09.27.10" /bin/sh -c "mkdir -p /tmp/python && pip3 install LINE-bot-sdk -t /tmp/python && cd /tmp && yum install -y zip && zip -r /var/task/LINE-bot-sdk.zip ."
Kento.YamadaKento.Yamada

boto3のバージョンを確認したい

import json
import boto3


def lambda_handler(event, context):
    return f"boto3 version: {boto3.__version__}"

Kento.YamadaKento.Yamada

LINE bot作成中に出たエラー

以下のエラーはIAMから出るものなのでLambdaにIAMロールを入れてあげればいい。

[ERROR] AccessDeniedException: An error occurred (AccessDeniedException) when calling the InvokeModel operation: User: arn:aws:sts::{アカウントID}:assumed-role/amz_bedrock-role-3vrxzgee/amz_bedrock is not authorized to perform: bedrock:InvokeModel on resource: arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2 because no identity-based policy allows the bedrock:InvokeModel action
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 59, in lambda_handler
    response = bedrock.invoke_model(
  File "/opt/python/botocore/client.py", line 535, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/opt/python/botocore/client.py", line 980, in _make_api_call
    raise error_class(parsed_response, operation_name)

対応するIAMポリシー

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "bedrock:InvokeModel",
            "Resource": [
                "arn:aws:bedrock:*:{アカウントID}:custom-model/*",
                "arn:aws:bedrock:*:{アカウントID}:provisioned-model/*",
                "arn:aws:bedrock:us-east-1::foundation-model/arn:aws:lambda:{リージョン}:{アカウントID}:function:amz_bedrock"
            ]
        }
    ]
}

「Youのアカウント、APIオペレーションを許可していない的なエラー」

[ERROR] AccessDeniedException: An error occurred (AccessDeniedException) when calling the InvokeModel operation: Your account is not authorized to invoke this API operation.
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 59, in lambda_handler
    response = bedrock.invoke_model(
  File "/opt/python/botocore/client.py", line 535, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/opt/python/botocore/client.py", line 980, in _make_api_call
    raise error_class(parsed_response, operation_name)

モデルのアクセスを許可しましょう。

Kento.YamadaKento.Yamada
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": "logs:CreateLogGroup",
			"Resource": "arn:aws:logs:us-east-1:{アカウントID}:*"
		},
		{
			"Effect": "Allow",
			"Action": [
				"logs:CreateLogStream",
				"logs:PutLogEvents"
			],
			"Resource": [
				"arn:aws:logs:us-east-1:{アカウントID}:log-group:/aws/lambda/amz_bedrock:*"
			]
		},
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "bedrock:InvokeModel",
            "Resource": [
                "arn:aws:bedrock:*:{アカウントID}:custom-model/*",
                "arn:aws:bedrock:*:{アカウントID}:provisioned-model/*",
                "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2"
            ]
        }
	]
}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:us-east-1:{アカウントID}:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:us-east-1:{アカウントID}:log-group:/aws/lambda/amz_bedrock:*"
            ]
        },
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "bedrock:InvokeModel",
            "Resource": [
                "arn:aws:bedrock:*:{アカウントID}:custom-model/*",
                "arn:aws:bedrock:*:{アカウントID}:provisioned-model/*",
                "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2"
            ]
        }
    ]
}
Kento.YamadaKento.Yamada

Bedrockの履歴

Use model invocation logging to collect metadata, requests, and responses for all model invocations in your account.

Kento.YamadaKento.Yamada

東京リージョンのboto3バージョンは1.27.1
Bedrockは1.28.58で対応していることを確認済み。
作成済みのLINE botoは 1.28.57で動作する。