Amazon BedrockをBoto3から使ってみた

こんにちは。サービス開発室の武田です。AWSの生成AIサービスであるAmazon BedrockがGAとなりましたね。さっそくBoto3を使ってアクセスしてみました。
2023.09.29

こんにちは。サービス開発室の武田です。

AWSの生成AIサービスであるAmazon BedrockがGAとなりましたね。

このビッグウェーブに乗って行こうぜってことで、今回はBoto3を使ってPythonのコードでサービスを利用する手順を確認してみました。

ライブラリのバージョン

さてGAしたばかりのサービスですので、Boto3も最新にする必要があります。具体的にサポートされたバージョンは 1.28.57 です。このエントリを書いている9時間ほど前にリリースされていました。

そんなわけでライブラリをアップグレードしましょう。

$ pip list | grep boto3
boto3                1.28.53

$ pip3 install boto3 --upgrade
$ pip list | grep boto3
boto3                1.28.57

準備OK!

モデルの有効化

さて、Bedrockを試していくわけですが、実はデフォルトでは提供されているモデルを使用できません。明示的に有効化が必要で、これはマネジメントコンソールから行います。手順は前述したsuzuki.ryoのエントリで解説しているため参考にしてください。

Boto3でアクセスしてみた

Boto3でサービスにアクセスする場合、対応したクライアントを作成してメソッドを呼び出すことでアクセスできます。Bedrockは次の2種類のクライアントが提供され、それぞれできることが違います。

  • bedrock
  • bedrock-runtime

実際に生成AIを利用するためにはbedrock-runtimeを利用する必要があります。

それではさっそくREPLを使用して試してみましょう。

>>> import boto3
>>> bedrock = boto3.client("bedrock", region_name="us-east-1")
>>> bedrock.
bedrock.can_paginate(
bedrock.close(
bedrock.create_model_customization_job(
bedrock.delete_custom_model(
bedrock.delete_model_invocation_logging_configuration(
bedrock.exceptions
bedrock.generate_presigned_url(
bedrock.get_custom_model(
bedrock.get_foundation_model(
bedrock.get_model_customization_job(
bedrock.get_model_invocation_logging_configuration(
bedrock.get_paginator(
bedrock.get_waiter(
bedrock.list_custom_models(
bedrock.list_foundation_models(
bedrock.list_model_customization_jobs(
bedrock.list_tags_for_resource(
bedrock.meta
bedrock.put_model_invocation_logging_configuration(
bedrock.stop_model_customization_job(
bedrock.tag_resource(
bedrock.untag_resource(
bedrock.waiter_names

まずはbedrockから。東京リージョンではまだ利用できないため、明示的にus-east-1を指定しています。こちらはモデルの一覧取得や、カスタムモデルの管理などができるようです。

試しにモデルの一覧を取得してみましょう。

>>> import pprint
>>> pprint.pprint(bedrock.list_foundation_models()["modelSummaries"])
[{'customizationsSupported': ['FINE_TUNING'],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-tg1-large',
  'modelId': 'amazon.titan-tg1-large',
  'modelName': 'Titan Text Large',
  'outputModalities': ['TEXT'],
  'providerName': 'Amazon',
  'responseStreamingSupported': True},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-e1t-medium',
  'modelId': 'amazon.titan-e1t-medium',
  'modelName': 'Titan Text Embeddings',
  'outputModalities': ['EMBEDDING'],
  'providerName': 'Amazon'},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-g1-text-02',
  'modelId': 'amazon.titan-embed-g1-text-02',
  'modelName': 'Titan Text Embeddings v2',
  'outputModalities': ['EMBEDDING'],
  'providerName': 'Amazon'},
 {'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',
  'modelName': 'Titan Text G1 - Express',
  'outputModalities': ['TEXT'],
  'providerName': 'Amazon',
  'responseStreamingSupported': True},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-text-v1',
  'modelId': 'amazon.titan-embed-text-v1',
  'modelName': 'Titan Embeddings G1 - Text',
  'outputModalities': ['EMBEDDING'],
  'providerName': 'Amazon',
  'responseStreamingSupported': True},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT', 'IMAGE'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/stability.stable-diffusion-xl',
  'modelId': 'stability.stable-diffusion-xl',
  'modelName': 'Stable Diffusion XL',
  'outputModalities': ['IMAGE'],
  'providerName': 'Stability AI'},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT', 'IMAGE'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/stability.stable-diffusion-xl-v0',
  'modelId': 'stability.stable-diffusion-xl-v0',
  'modelName': 'Stable Diffusion XL',
  'outputModalities': ['IMAGE'],
  'providerName': 'Stability AI'},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/ai21.j2-grande-instruct',
  'modelId': 'ai21.j2-grande-instruct',
  'modelName': 'J2 Grande Instruct',
  'outputModalities': ['TEXT'],
  'providerName': 'AI21 Labs',
  'responseStreamingSupported': False},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/ai21.j2-jumbo-instruct',
  'modelId': 'ai21.j2-jumbo-instruct',
  'modelName': 'J2 Jumbo Instruct',
  'outputModalities': ['TEXT'],
  'providerName': 'AI21 Labs',
  'responseStreamingSupported': False},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/ai21.j2-mid',
  'modelId': 'ai21.j2-mid',
  'modelName': 'Jurassic-2 Mid',
  'outputModalities': ['TEXT'],
  'providerName': 'AI21 Labs',
  'responseStreamingSupported': False},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/ai21.j2-mid-v1',
  'modelId': 'ai21.j2-mid-v1',
  'modelName': 'Jurassic-2 Mid',
  'outputModalities': ['TEXT'],
  'providerName': 'AI21 Labs',
  'responseStreamingSupported': False},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/ai21.j2-ultra',
  'modelId': 'ai21.j2-ultra',
  'modelName': 'Jurassic-2 Ultra',
  'outputModalities': ['TEXT'],
  'providerName': 'AI21 Labs',
  'responseStreamingSupported': False},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/ai21.j2-ultra-v1',
  'modelId': 'ai21.j2-ultra-v1',
  'modelName': 'Jurassic-2 Ultra',
  'outputModalities': ['TEXT'],
  'providerName': 'AI21 Labs',
  'responseStreamingSupported': False},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v1',
  'modelId': 'anthropic.claude-instant-v1',
  'modelName': 'Claude Instant',
  'outputModalities': ['TEXT'],
  'providerName': 'Anthropic',
  'responseStreamingSupported': True},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v1',
  'modelId': 'anthropic.claude-v1',
  'modelName': 'Claude',
  'outputModalities': ['TEXT'],
  'providerName': 'Anthropic',
  'responseStreamingSupported': True},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2',
  'modelId': 'anthropic.claude-v2',
  'modelName': 'Claude',
  'outputModalities': ['TEXT'],
  'providerName': 'Anthropic',
  'responseStreamingSupported': True},
 {'customizationsSupported': [],
  'inferenceTypesSupported': ['ON_DEMAND'],
  'inputModalities': ['TEXT'],
  'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/cohere.command-text-v14',
  'modelId': 'cohere.command-text-v14',
  'modelName': 'Command',
  'outputModalities': ['TEXT'],
  'providerName': 'Cohere',
  'responseStreamingSupported': True}]

それでは続いてbedrock-runtimeです。

>>> bedrock_runtime = boto3.client("bedrock-runtime", region_name="us-east-1")
>>> bedrock_runtime.
bedrock_runtime.can_paginate(                       bedrock_runtime.get_waiter(
bedrock_runtime.close(                              bedrock_runtime.invoke_model(
bedrock_runtime.exceptions                          bedrock_runtime.invoke_model_with_response_stream(
bedrock_runtime.generate_presigned_url(             bedrock_runtime.meta
bedrock_runtime.get_paginator(                      bedrock_runtime.waiter_names

おそらく一番使っていくのがinvoke_modelになるのでしょうか。またinvoke_model_with_response_streamも覚えておくと良さそうですね。

今回はJurassic-2 Midのモデルを使ってみましょう。invoke_modelはモデル名ではなく モデルID を指定します。先ほどの一覧で確認してみると、IDはai21.j2-mid-v1のようです。

なんとなく日本の初代総理大臣を知りたくなったので聞いてみました。

>>> resp = bedrock_runtime.invoke_model(modelId="ai21.j2-mid-v1", body=json.dumps({"prompt":"日本の初代総理大臣は誰ですか。"}))
>>> json.loads(resp["body"].read())["completions"]
[{'data': {'text': '\n日本の初代総理大臣は清治四年', 'tokens': [{'generatedToken': {'token': '<|newline|>', 'logprob': -0.001190906623378396, 'raw_logprob': -0.001190906623378396}, 'topTokens': None, 'textRange': {'start': 0, 'end': 1}}, {'generatedToken': {'token': '▁', 'logprob': -0.33202117681503296, 'raw_logprob': -0.33202117681503296}, 'topTokens': None, 'textRange': {'start': 1, 'end': 1}}, {'generatedToken': {'token': '日', 'logprob': -0.11673896014690399, 'raw_logprob': -0.11673896014690399}, 'topTokens': None, 'textRange': {'start': 1, 'end': 2}}, {'generatedToken': {'token': '本', 'logprob': -0.0002172949316445738, 'raw_logprob': -0.0002172949316445738}, 'topTokens': None, 'textRange': {'start': 2, 'end': 3}}, {'generatedToken': {'token': 'の', 'logprob': -0.30525633692741394, 'raw_logprob': -0.30525633692741394}, 'topTokens': None, 'textRange': {'start': 3, 'end': 4}}, {'generatedToken': {'token': '初', 'logprob': -0.0029284947086125612, 'raw_logprob': -0.0029284947086125612}, 'topTokens': None, 'textRange': {'start': 4, 'end': 5}}, {'generatedToken': {'token': '代', 'logprob': -2.7656173188006505e-05, 'raw_logprob': -2.7656173188006505e-05}, 'topTokens': None, 'textRange': {'start': 5, 'end': 6}}, {'generatedToken': {'token': '総', 'logprob': -0.0014261561445891857, 'raw_logprob': -0.0014261561445891857}, 'topTokens': None, 'textRange': {'start': 6, 'end': 7}}, {'generatedToken': {'token': '理', 'logprob': -1.6927575416048057e-05, 'raw_logprob': -1.6927575416048057e-05}, 'topTokens': None, 'textRange': {'start': 7, 'end': 8}}, {'generatedToken': {'token': '大', 'logprob': -0.00010752100206445903, 'raw_logprob': -0.00010752100206445903}, 'topTokens': None, 'textRange': {'start': 8, 'end': 9}}, {'generatedToken': {'token': '臣', 'logprob': -0.022740887477993965, 'raw_logprob': -0.022740887477993965}, 'topTokens': None, 'textRange': {'start': 9, 'end': 10}}, {'generatedToken': {'token': 'は', 'logprob': -0.0009258274803869426, 'raw_logprob': -0.0009258274803869426}, 'topTokens': None, 'textRange': {'start': 10, 'end': 11}}, {'generatedToken': {'token': '清', 'logprob': -1.1000699996948242, 'raw_logprob': -1.1000699996948242}, 'topTokens': None, 'textRange': {'start': 11, 'end': 12}}, {'generatedToken': {'token': '治', 'logprob': -0.8374251127243042, 'raw_logprob': -0.8374251127243042}, 'topTokens': None, 'textRange': {'start': 12, 'end': 13}}, {'generatedToken': {'token': '四', 'logprob': -6.17655611038208, 'raw_logprob': -6.17655611038208}, 'topTokens': None, 'textRange': {'start': 13, 'end': 14}}, {'generatedToken': {'token': '年', 'logprob': -0.6831862330436707, 'raw_logprob': -0.6831862330436707}, 'topTokens': None, 'textRange': {'start': 14, 'end': 15}}]}, 'finishReason': {'reason': 'length', 'length': 16}}]

なるほど。なるほど?まぁレスポンスの内容は気にせず、ちゃんと返って来たことに安心しましょう。

ちなみにモデルを有効化していない場合、次のようなエラーが表示され失敗します。このメッセージが出た場合は、有効化をしてあげてください。

botocore.errorfactory.AccessDeniedException: An error occurred (AccessDeniedException) when calling the InvokeModel operation: Your account is not authorized to invoke this API operation.

まとめ

ひとまず一覧の取得と、モデルの実行をBoto3から試してみました。いろいろと試していきましょう!

参考URL