🗿

今更Amazon Bedrockに入門してみた

2024/05/21に公開

はじめに

こんにちはへたれです。
株式会社アイデミーでエンジニアとして、材料開発のためのデータ活用プラットフォームLab Bankを開発、運用しています。

きっかけ

普段書く記事はインフラ系の記事が多いのですが、業務ではアプリケーション開発も行っています。
迅速な機能開発、バリュー提供のためにはLLMを使ったアプリケーション開発の必要性を感じつつもなかなかキャッチアップができておらず焦りを感じていました。
最近Amazon Bedrockが話題に上がることが多く、AWSをメイン領域としている自分としては丁度良い機会だと思い入門することにしました。

Amazon Bedrockとは?

Amazon BedrockはフルマネージドなLLM基盤を提供してくれるサービスです。
もちろんインスタンスの管理などは必要ありません。

魅力的な点としては、以下のようなものを感じました。

  • AWS基盤上に存在するので、IAMロールでの権限制御やCloudTrailでの証跡などを統合的にできる
  • Claude3やLlamaなどいくつかの企業が作ったモデルを1つの基盤で利用することが可能
  • トークンごとの従量課金性なので、気軽に始めやすい

ただ一方で、今回試したClaude 3 Sonnetを含めて多くのモデルが日本のリージョンでは使えません。
またAmazon Titan以外のモデルに関しては最初から使えるわけではなく、予めモデルの使用リクエストをしておく必要があります。
自分の場合は許可までに数日かかっていたので、何か使いたい予定がある場合はすぐにでもリクエストした方がいいかもしれません。
ここは少し使いづらかったかな?という印象でした。
(2024/05/21現在)

今回は贅沢にも画像読み取りも扱えるClaude 3 Sonnetを試してみました。

サンプルのシナリオ

商品のカタログデータやイベントの開催データ、官公庁が発表しているPDFデータなど世の中には綺麗にまとめられたデータが沢山あります。
ただ人間にとっての可読性とプログラムにとっての可読性は異なります。
プログラムにとって扱いやすいのは、RDBやJSONの構造化されたデータです。
これらを変換するのに手動はあまりに面倒なので、LLMに投げちゃおうと思います。

ここにサンプルとしてスキー板のスペックと試乗をまとめた架空の表を用意しました。
表のデータをDBに落としていきます。

boto3からの実行

ChatGPTのようなGUI上で実行するPlaygroundもあるのですが、今回は直接boto3で実行してみました。
サンプルコードはこちらです。
コードを読むとわかると思うのですが、非常にシンプルなコードでプロンプトを実行し、結果を得ることができました。

Bedrockへのリクエストは至ってシンプルで、JSON形式でクエリを組み立てていきます。

  • 画像はbase64エンコード
  • テキストはプレインテキスト

で構造化された状態になっています。

リクエストボディ
body = json.dumps({
    "anthropic_version": "bedrock-2023-05-31",
    "max_tokens": 2048,
    "messages": [
      {
        "role": "user",
        "content": [
          {
            "type": "image",
            "source": {
              "type": "base64",
              "media_type": "image/png",
              "data": encoded_img
            }
          },
          {
            "type": "text",
            "text": prompt,
          }
        ]
      }
    ]
})

ここはプロンプトを定義しています。
よくあるLLMのプロンプトですね。

プロンプト
# 画像はbase64エンコードして送信する
encoded_img = ''
with open('./sample_table.png', 'rb') as f:
    encoded_img = base64.b64encode(f.read()).decode('utf-8')

prompt = ''''
あなたはベテランのデータベースエンジニアです。
画像の表を正規化し、テーブル定義に落とし込んでください。
適切なリレーションや制約条件の付与も行ってください。
結果はSQLのみで、解説は不要です。
'''

最後にBedrockのAPIにリクエストを投げて応答を取得すれば完了です。
本記事の執筆時点では、Claude 3 Sonnetは日本のリージョンで利用できないため、バージニアを指定しています。

リクエストの実行
# boto3からbedrockへリクエスト
# Claude3 Sonnetはバージニアリージョンのみ対応
bedrock = boto3.client(service_name='bedrock-runtime', region_name='us-east-1')
res = bedrock.invoke_model(
    body=body,
    modelId='anthropic.claude-3-sonnet-20240229-v1:0',
    accept='application/json',
    contentType='application/json',
)
res_body = json.loads(res.get('body').read())
print(res_body.get('content')[0].get('text'))

実行結果

こちらが実行結果です。
パッと見良さそうですが、実際にそのまま投入できるレベルのものではないですね。
とはいえ、議論のベースを作るショートカットとしてはとても便利です!

$ python3 main.py

-- テーブル定義
CREATE TABLE 機種 (
    機種ID INT PRIMARY KEY,
    機種名 VARCHAR(20),
    形状 VARCHAR(20),
    素材 VARCHAR(20),
    ラディウス DECIMAL(5,2)
);

CREATE TABLE 製品 (
    製品ID INT PRIMARY KEY,
    機種ID INT FOREIGN KEY REFERENCES 機種(機種ID),
    トップ INT,
    ボックス INT, 
    テール INT,
    長さ INT,
    価格 INT
);

CREATE TABLE 評価項目 (
    評価項目ID INT PRIMARY KEY,
    項目名 VARCHAR(20)
);

CREATE TABLE 評価値 (
    製品ID INT FOREIGN KEY REFERENCES 製品(製品ID),
    評価項目ID INT FOREIGN KEY REFERENCES 評価項目(評価項目ID),
    安定性 INT CHECK (安定性 BETWEEN 1 AND 5),
    スキッディング INT CHECK (スキッディング BETWEEN 1 AND 5),
    カービング INT CHECK (カービング BETWEEN 1 AND 5)
);

-- データ入力
INSERT INTO 機種 (機種ID, 機種名, 形状, 素材, ラディウス) VALUES
(1, 'キャンバー', '形状1', 'ウッドコア', 14.00),
(2, 'ロッカー', 'サンドイッチキャップ', 'ウッドコア', 12.00),
(3, 'フラット', 'サンドイッチキャップ', '樹脂コア', 11.80),
(4, 'キャンバー', 'サンドイッチキャップ', 'ウッドコア', 30.00),
(5, 'ロッカー', 'サンドイッチキャップ', 'ウッドコア', 28.00),
(6, 'フラット', 'サンドイッチキャップ', 'ウッドコア', 26.00);

INSERT INTO 製品 (製品ID, 機種ID, トップ, ボックス, テール, 長さ, 価格) VALUES
(1, 1, 68, 60, 65, 165, 148000),
(2, 2, 80, 70, 76, 160, 192000),
(3, 3, 77, 70, 74, 160, 125000), 
(4, 4, 72, 60, 68, 180, 190000),
(5, 5, 120, 110, 116, 175, 170000),
(6, 6, 112, 60, 108, 180, 168000);

INSERT INTO 評価項目 (評価項目ID, 項目名) VALUES
(1, '安定性'),
(2, 'スキッディング'), 
(3, 'カービング');

INSERT INTO 評価値 (製品ID, 評価項目ID, 安定性, スキッディング, カービング) VALUES
(1, 1, 5, 3, 5),
(2, 1, 4, 4, 4),
(2, 2, NULL, 5, 3),
(2, 3, NULL, NULL, 4),
(3, 1, 4, 3, 3), 
(3, 2, NULL, 4, 4),
(3, 3, NULL, 3, 4),
(4, 1, 4, NULL, 5),
(4, 2, NULL, 2, NULL),
(4, 3, NULL, 5, 2),
(5, 1, 5, 1, 3),
(5, 2, NULL, 3, NULL),
(5, 3, NULL, 4, 1),
(6, 1, 3, NULL, 3),
(6, 2, NULL, NULL, NULL), 
(6, 3, NULL, 4, 4);

ちなみにこちら(うろ覚えですが)inputが2000, outputが1000トークン程度でしたので、実行コストは$0.021でした。

おわりに

最近話題になっているAmazon Bedrockに入門してみました。
テキストと画像を組み合わせたプロンプトをかなり手軽に投げられました。
本格的にサービスに組み込むとなるとプロンプトエンジニアリングの要素やファインチューニングで精度を上げていく必要があると思います。
ただ1分程度で考えたプロンプトでも人が考える叩き台としては十分ですね。
今回は単純にプロンプトを投げて結果を返すだけのサンプルを試してみましたが、結果を使ってLambdaを起動できるAgents for Amazon Bedrockも気になるところです。

またアイデミーでは一緒に働く仲間を募集中です!
今回の記事を読んで興味が湧いた方、まずはカジュアル面談からでもお待ちしております!

Aidemy Tech Blog

Discussion