昨年出版した「Amazon Bedrock 生成AIアプリ開発入門 [AWS深掘りガイド]」が、皆様にご支援いただき、なんと
ITエンジニア本大賞2025 技術書部門ベスト10 に選出いただきました!!!!!
50音順ではありますが、位置が良いですね!
執筆段階から、「移り変わりがはやいテーマなので、出版後もできるだけアップデート情報を反映させていきたいね」と話していたのですが、、
申し訳ございません!!
ちょっとサボってました🙇♀️🙇♂️🙇
Amazon Novaで動かないという声を聞いたので、大急ぎで検証しました!
書籍サポートのGitHubにも更新版のコードを格納しておりますのでこちらも参照ください。
InvokeModel APIの場合
InvokeModel APIを使用たNovaモデルの呼び出しは、書籍執筆時点の1.34.87
でも動作します。
pip install boto3==1.34.87 botocore==1.34.87
boto3にbedrock-runtime
が追加されたバージョン1.28.57
でも、Novaモデルを呼び出すことは可能です。(ただし、ガードレールはクロスリージョン推論などの機能には対応していないので、可能であれば最新バージョンを使用したほうがいいと思います。
通常呼び出し
書籍の「2.11.2 AWS SDKを用いて各モデルのAPIへリクエストを行う方法」のP.92に記載のコードを元に解説します。
InvokeMode APIはリクエストの形式がモデルごとに異なるのでAmazon Novaの形式に合わせる必要があります。
# リクエストボディを定義
body = json.dumps(
{
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1000,
"messages": [
{
"role": "user",
"content": "Bedrockってどういう意味?",
}
],
}
)
# モデルを定義(Claude 3 Sonnet)
modelId = "anthropic.claude-3-sonnet-20240229-v1:0"
# リクエストボディを定義
body = json.dumps(
{
"messages": [
{
"role": "user",
"content": [{"text": "Bedrockってどういう意味?"}],
}
],
"inferenceConfig": {"max_new_tokens": 1000},
}
)
# モデルを定義(Amazon Nova Pro)
modelId = "amazon.nova-pro-v1:0"
レスポンスの形式もモデルごとに異なります。
answer = response_body["content"][0]["text"]
answer = response_body["output"]["message"]["content"][0]["text"]
実行方法は変わりません。
python 2_invoke-model.py
ストリーム呼び出し
書籍の「2.11.2 AWS SDKを用いて各モデルのAPIへリクエストを行う方法」のP.97に記載のコードを元に解説します。
ストリームの場合、リクエストの形式は先ほどで解説したものと変わりませんが、レスポンスの形式が以下のように変わります。
# ストリーミング出力
for event in response.get("body"):
chunk = json.loads(event["chunk"]["bytes"])
if (
chunk["type"] == "content_block_delta"
and chunk["delta"]["type"] == "text_delta"
):
print(chunk["delta"]["text"], end="")
# ストリーミング出力
for event in response.get("body"):
chunk = json.loads(event["chunk"]["bytes"])
if "contentBlockDelta" in chunk:
print(chunk["contentBlockDelta"]["delta"]["text"], end="")
Converse APIの場合
Converse APIが校了ギリギリのタイミングでリリースだったので、実行例を書籍に含めることができませんでした。以下にコード例を用意していますので、こちらをご確認ください。
https://github.com/minorun365/bedrock-book/blob/main/chapter2/3-1_streaming-converse.py
Converse APIはBoto3の1.34.116
で追加されました。このバージョン以降であれば、Converse APIを使用してNovaモデルを呼び出し可能です。
pip install boto3==1.34.116 botocore==1.34.116
Converse APIの特徴として、 「モデルが変わってもリクエストとレスポンスの形式が同一」 というものがあるので、モデル名を変更するだけでNovaモデルを利用できます。
# モデルを定義(Claude 3 Sonnet)
model_id = "anthropic.claude-3-sonnet-20240229-v1:0"
# モデルを定義(Amazon Nova Pro)
model_id = "amazon.nova-pro-v1:0"
実行方法
python 2-1_invoke-converse.py
LangChainの場合
LangChainを使用する場合は、Boto3だけでなくlangchain-awsが対応している必要があります。langchain-awsのNova対応は0.2.9
のリリースに含まれています。
langchain-awsのバージョン0.2.9では依存ライブラリーとして、boto3の1.35.74
以降が必要ですので同時にインストールされます。
今回調べて発覚したのですが、Amazon Novaが発表されたその日のうちにlangchain-awsのバージョン0.2.9がリリースされていました!!はやい!!!
pip install langchain-aws==0.2.9 boto3==1.35.74 botocore==1.35.74
Invoke呼び出し
書籍の「3.5.2 【ステップ1】 LangChainの実装」のP.144に記載のコードを元に解説します。
ライブラリーのバージョンが対応していれば、モデル名を変更するだけNovaモデルを呼び出し可能です。
# ChatBedrockを生成
chat = ChatBedrock(
model_id="anthropic.claude-3-sonnet-20240229-v1:0",
model_kwargs={"max_tokens": 1000},
)
# ChatBedrockを生成
chat = ChatBedrock(
model_id="amazon.nova-pro-v1:0",
model_kwargs={"max_tokens": 1000},
)
実行方法
python 1_langchain.py
Stream呼び出し
書籍の「3.5.3 【ステップ2】ストリーム出力」のP.150に記載のコードを元に解説します。
stream呼び出しの場合、同じコードで動作はするのですが、出力部分の動作が異なります。
以下のコードを用いてClaudeとNovaの出力を比較します。
# Stream形式でモデル呼び出し
for chunk in chat.stream(messages):
print(chunk.content, end="", flush=True)
print("")
空が青く見える主な理由は、太陽の光が地球の大気中の分子と相互作用するためです。
具体的には以下の理由があげられます。
1. 空気中の窒素や酸素の分子が、太陽光の短い波長(青色)の光を mehr散乱させるため。この散乱を"レーリー散乱"と呼びます。
2. 長い波長(赤色)の光は空気分子にあまり散乱されないため、太陽からの光は青白く見えます。
3. 太陽光は大気圏の上部で最も強く散乱され、そのため空頂上付近は深い青色に見えます。地平線に近づくにつれ、散乱される光の経路が長くなり、青みが薄れていきます。
4. 大気中の水蒸気や浮遊粒子なども一部光を散乱させますが、レーリー散乱が空の青色の主な原因です。
このように、太陽光と大気中の分子との相互作用により、青色光が優先的に散乱され、それが空の青色の印象を与えているのです。
[][{'type': 'text', 'text': '', 'index': 0}][{'type': 'text', 'text': '空', 'index': 0}][{'type': 'text', 'text': 'が', 'index': 0}][{'type': 'text', 'text': '青', 'index': 0}][{'type': 'text', 'text': 'く', 'index': 0}][{'type': 'text', 'text': '見', 'index': 0}][{'type': 'text', 'text': 'える', 'index': 0}][{'type': 'text', 'text': '理由', 'index': 0}][{'type': 'text', 'text': 'は', 'index': 0}][{'type': 'text', 'text': '、', 'index': 0}][{'type': 'text', 'text': 'レイ', 'index': 0}][{'type': 'text', 'text': 'リー', 'index': 0}][{'type': 'text', 'text': '散', 'index': 0}][{'type': 'text', 'text': '乱', 'index': 0}][{'type': 'text', 'text': 'と呼ばれる', 'index': 0}][{'type': 'text', 'text': '現', 'index': 0}][{'type': 'text', 'text': '象', 'index': 0}][{'type': 'text', 'text': 'による', 'index': 0}][{'type': 'text', 'text': 'ものです', 'index': 0}][{'type': 'text', 'text': '。', 'index': 0}]
...以下略
Novaモデルを使用すると、JSONで出力されました。
この出力はChatBedrock
ではなくChatBedrockConverse
を使用した場合の動作と同じです。
# Pyhton外部モジュールのインポート
from langchain_aws import ChatBedrockConverse
from langchain_core.messages import HumanMessage, SystemMessage
# ChatBedrockを生成
chat = ChatBedrockConverse(
model="anthropic.claude-3-sonnet-20240229-v1:0",
max_tokens=1000,
)
# メッセージを定義
messages = [
SystemMessage(content="あなたのタスクはユーザーの質問に明確に答えることです。"),
HumanMessage(content="空が青いのはなぜですか?"),
]
# Stream形式でモデル呼び出し
for chunk in chat.stream(messages):
print(chunk.content, end="", flush=True)
print("")
[][{'type': 'text', 'text': '空', 'index': 0}][{'type': 'text', 'text': 'が', 'index': 0}][{'type': 'text', 'text': '青', 'index': 0}][{'type': 'text', 'text': 'く', 'index': 0}][{'type': 'text', 'text': '見', 'index': 0}][{'type': 'text', 'text': 'える', 'index': 0}][{'type': 'text', 'text': '主', 'index': 0}][{'type': 'text', 'text': 'な', 'index': 0}][{'type': 'text', 'text': '理', 'index': 0}][{'type': 'text', 'text': '由', 'index': 0}][{'type': 'text', 'text': 'は', 'index': 0}][{'type': 'text', 'text': '、', 'index': 0}][{'type': 'text', 'text': '太', 'index': 0}][{'type': 'text', 'text': '陽', 'index': 0}][{'type': 'text', 'text': '光', 'index': 0}][{'type': 'text', 'text': 'の', 'index': 0}][{'type': 'text', 'text': '中', 'index': 0}][{'type': 'text', 'text': 'の', 'index': 0}][{'type': 'text', 'text': '短', 'index': 0}][{'type': 'text', 'text': 'い', 'index': 0}][{'type': 'text', 'text': '波', 'index': 0}][{'type': 'text', 'text': '長', 'index': 0}][{'type': 'text', 'text': 'の', 'index': 0}][{'type': 'text', 'text': '光', 'index': 0}][{'type': 'text', 'text': '(', 'index': 0}][{'type': 'text', 'text': '青', 'index': 0}]
...以下略
langchain-awsには、InvokeModel APIを使用する「ChatBedrock
」とConverse APIを使用する「ChatBedrockConverse
」の2つの実装があります。Novaモデルの場合はどうも 「ChatBedrockを使用しても内部でConverse APIを使用する」 という動作をしているようです。
Novaモデルを使用した場合の出力をClaudeの場合に合わせるには、StrOutputParserを使用します。
# Pyhton外部モジュールのインポート
from langchain_aws import ChatBedrock
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
# ChatBedrockを生成
chat = ChatBedrock(
model_id="amazon.nova-pro-v1:0",
model_kwargs={"max_tokens": 1000},
streaming=True,
)
# Nova特別対応:出力が生成したテキストになるようにStrOutputParserを追加
chat = chat | StrOutputParser()
# メッセージを定義
messages = [
SystemMessage(content="あなたのタスクはユーザーの質問に明確に答えることです。"),
HumanMessage(content="空が青いのはなぜですか?"),
]
# Stream形式でモデル呼び出し
for chunk in chat.stream(messages):
print(chunk, end="", flush=True)
print("")
空が青く見える理由は、レイリー散乱と呼ばれる現象によるものです。太陽光は、赤外線、可視光線、紫外線を含むさまざまな波長の光で構成されています。地球の大気に入ると、これらの光は空気中の窒素や酸素などの小さな分子によって散乱されます。
レイリー散乱では、光の波長が短いほど散乱が強くなります。可視光線の中で、青色の光は波長が比較的短いため、赤色やオレンジ色の光よりも強く散乱されます。このため、青色の光が大気中であらゆる方向に散乱され、私たちは空を青く見ることになるのです。
夕焼けや日の出の際に空が赤やオレンジに見えるのも、同じ原理に基づいています。これらの時間帯には、太陽が地平線近くにあるため、光はより長い距離を大気中を通過します。これにより、青色や緑色の光がより強く散乱され、残る光は主に赤やオレンジの波長になるため、空が赤やオレンジに見えることになります。
今回のコードでは問題ありませんが、chunk
の内容がテキストのみの置き換わっているため、chunk.content以外のchunk.response_metadataなどはなくなってしまいますので、用途に合わせて修正してください。
まとめ
今後もできるだけアップデートに追随していきたいと思いますので、引き続きよろしくお願いいたします!