はじめに
Amazon Bedrock は AWS から各種生成 AI を呼び出し、テキスト生成や画像生成を実行するサービスです
ブラウザからお手軽に試す場合、以下の記事を参考にしてください
本記事では Elixir の Livebook から Bedrock を使い、生成AIによるチャット、画像生成を実装します
実装したノートブックはこちら
Livebook とは
Livebook は Elixir のコードをブラウザから実行し、結果を表示してくれるツールです
Python における Jupyter のようなもので、 Elixir 入門や Elixir を使ったデータ分析、データの視覚化などに適しています
はじめ方は以下の記事を参考にしてください
セットアップ
Livebook で新しいノートブックを開いたら、先頭のセットアップセルに以下のコードを入力し、実行してください
Mix.install([
{:aws, git: "https://github.com/aws-beam/aws-elixir/"},
{:hackney, "~> 1.20"},
{:kino, "~> 0.11"}
])
AWS Elixir というモジュールをインストールしています
まだリリースされているバージョンには Bedrock 用のモジュールが含まれていないため、 GitHub から最新版をインストールしています
クライアントの準備
AWS の認証情報を入力するためのテキストボックスを準備します
access_key_id_input = Kino.Input.password("ACCESS_KEY_ID")
secret_access_key_input = Kino.Input.password("SECRET_ACCESS_KEY")
region_input = Kino.Input.text("REGION")
[
access_key_id_input,
secret_access_key_input,
region_input
]
|> Kino.Layout.grid(columns: 3)
セルを実行して表示されたテキストボックスにそれぞれ入力してください
リージョンは Bedrock を使用するリージョンなので、東京リージョン(ap-northeast-1)で使えないモデルを使いたい場合、バージニア北部(us-east-1)などを指定してください
AWS の API と通信するためのクライアントを用意します
この際、先ほどの認証情報を受け渡します
client =
AWS.Client.create(
Kino.Input.read(access_key_id_input),
Kino.Input.read(secret_access_key_input),
Kino.Input.read(region_input)
)
基盤モデル一覧の取得
入力したリージョンで使用可能な基盤モデルの一覧を表示します
models =
client
|> AWS.Bedrock.list_foundation_models()
|> elem(1)
|> Map.get("modelSummaries")
Kino.DataTable.new(models)
Kino.DataTable
を使うことで、結果が検索やソートも可能なテーブル表示になります
テーブル表示した情報を参考にしながら、使いたい基盤モデルの情報を取得します
例えば Titan Text を使う場合、以下のように providerName
が Amazon
かつ modelName
が Titan Text G1 - Express
かつ
inferenceTypesSupported
が ["ON_DEMAND"]
の条件を指定して検索できます
同じモデルでも課金体系によって ON_DEMAND
と PROVISIONED
が存在する点に注意してください
考え方は DynamoDB と同じように、完全に呼ばれる毎に課金されるのが ON_DEMAND
で、一定の性能を確保して時間毎固定料金になる PROVISIONED
です
本記事では数回しか呼ばないため、 ON_DEMAND
を使用します
titan_express_model =
models
|> Enum.find(fn model ->
model["providerName"] == "Amazon" and
model["modelName"] == "Titan Text G1 - Express" and
model["inferenceTypesSupported"] == ["ON_DEMAND"]
end)
実行結果
%{
"customizationsSupported" => [],
"inferenceTypesSupported" => ["ON_DEMAND"],
"inputModalities" => ["TEXT"],
"modelArn" => "arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-text-express-v1",
"modelId" => "amazon.titan-text-express-v1",
"modelLifecycle" => %{"status" => "ACTIVE"},
"modelName" => "Titan Text G1 - Express",
"outputModalities" => ["TEXT"],
"providerName" => "Amazon",
"responseStreamingSupported" => true
}
この情報のうち、基盤モデルを API 経由で呼び出す場合には modelId
の値が必要になるため、取得しておきます
titan_express_model_id = titan_express_model["modelId"]
実行結果
"amazon.titan-text-express-v1"
日本語対応テキスト生成の Claude も使いたいのでモデル ID を取得しておきます
claude_model_id =
models
|> Enum.find(fn model ->
model["modelName"] == "Claude" and
model["inferenceTypesSupported"] == ["ON_DEMAND"]
end)
|> Map.get("modelId")
実行結果
"anthropic.claude-v1"
画像生成用のモデル ID も取得します
titan_image_id =
models
|> Enum.find(fn model ->
model["modelName"] == "Titan Image Generator G1" and
model["inferenceTypesSupported"] == ["ON_DEMAND"]
end)
|> Map.get("modelId")
実行結果
"amazon.titan-image-generator-v1"
テキスト生成(チャット)
テキスト生成によるチャットを実装してみます
まず、生成 AI に投げる質問を用意します
Titan は日本語非対応なので、英語で「Elixir での Hello, World のコードを教えて」と聞いてみましょう
input = "Please show me the code to display \"Hello, World\" in Elixir."
以下のようにして Bedrock の基盤モデルを呼び出します
results =
client
|> AWS.BedrockRuntime.invoke_model(
titan_express_model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"inputText" => input
}
)
|> elem(1)
|> Map.get("results")
第1引数が用意しておいたクライアント、第2引数がモデル ID 、第3引数が生成 AI に渡す入力です
第3引数の "accept" => "application/json"
と "contentType" => "application/json"
は API リクエスト時のヘッダーとして指定するため、固定で必ず必要です
それ以外の項目と、出力形式については基盤モデル毎にバラバラな仕様になっているため、個別に仕様を確認してください
Titan の場合、以下のような形式で結果が返ってきます
実行結果
[
%{
"completionReason" => "LENGTH",
"outputText" => "\nTo display \"Hello, World\" in Elixir, you can use the \"IO.puts\" function. Here is an example:\n```Elixir\nIO.puts \"Hello, World\"\n```\nIn Elixir, the \"IO.puts\" function is used to print messages to the console. In this case, it will output \"Hello, World\" to the console.\nYou can also use formatting options to customize the output. For example, you can use \"IO.inspect\" to pretty-print the message:\n```Elixir\nIO.inspect \"Hello, World\"",
"tokenCount" => 128
}
]
生成 AI の出力は Markdown になっているため、ノートブック上で Markdown として表示してみましょう
results
|> Enum.at(0)
|> Map.get("outputText")
|> Kino.Markdown.new()
ちゃんと Hello, World のコードを解説付きで生成してくれました
日本語チャット
次に Claude による日本語チャットを実装します
今度は少し難しい問題として、 Elixir で 5 の階乗を計算するコードを生成してもらいます
input = "Elixirで5の階乗を計算するコードを教えてください。"
result =
client
|> AWS.BedrockRuntime.invoke_model(
claude_model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"prompt" => "\n\nHuman: #{input}\n\nAssistant:",
"max_tokens_to_sample" => 300
},
recv_timeout: 60_000
)
|> elem(1)
|> Map.get("completion")
Claude の場合、入力テキストを prompt
に \n\nHuman: <入力テキスト>\n\nAssistant:
という形式で入れなければならない、という点に注意してください
\n\nHuman:
で始まり、 \n\nAssistant:
で終わる、という条件を満たしていないとエラーが返ります
max_tokens_to_sample
も必須項目なので指定してください
処理に少し時間がかかるため、第4引数のオプションに recv_timeout: 60_000
を指定し、タイムアウトを 1 分まで延長しています
これを指定しないとタイムアウトエラーが発生します
Titan の場合、結果は results
に入っていましたが、 Claude の場合は completion
に入っている点も要注意です
結果を表示してみましょう
result
|> Kino.Markdown.new()
しっかり Elixir らしい実装を日本語の解説とともに返してくれました
画像生成
最後に画像生成です
Titan に Elixir の初心者向けコミュニティ piyopiyo.ex の画像を生成してもらいましょう
さすがに名前だけだと厳しいと思うので、「Elixir の赤ちゃんが卵から生まれている」「Elixir はかわいい顔をした紫色の雫」とヒントを与えます。
input = "piyopiyo.ex Elixir babies are being born from eggs. Elixir is a purple drop with a pretty face."
results =
client
|> AWS.BedrockRuntime.invoke_model(
titan_image_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"taskType" => "TEXT_IMAGE",
"textToImageParams" => %{
"text" => input
},
"imageGenerationConfig" => %{
"numberOfImages" => 1,
"quality" => "standard",
"height" => 512,
"width" => 512,
"cfgScale" => 2.0,
"seed" => 999
}
},
recv_timeout: 60_000
)
|> elem(1)
|> Map.get("images")
実行結果
["iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOt...
結果を見て「何のこっちゃ」と思うかもしれませんが、これはバイナリデータを文字列で表現した Base64 という形式です
Base64 を PNG 画像として表示してみます
results
|> Enum.at(0)
|> then(&"""
<img src="data:image/png;base64,#{&1}" />
""")
|> Kino.HTML.new()
当たらずも遠からず、といったところでしょうか
まとめ
Elixir から Amazon Bedrock を呼び出し、生成 AI によるチャット、画像生成が実装できました
Bedrock は今後もモデルが増えるでしょうし、しかも自分のデータでモデルをカスタマイズすることも可能です
当然 AWS 上の Lambda などから呼び出したりなど、 AWS の各種サービスとの連携も容易になっています
今後、 Bedrock を使ったサービスがどんどん増えそうですね