[初心者]AWS Bedrock + Lambda + API GatewayでAPIでAIを触れるようにする
概要
APIURLとプロンプトでリクエストを投げるとAIが返答してくれるようなAPIを作る
プロンプトの投げ方
{
"body": "{"prompt":"TypeScriptの特徴を3つ教えて"}"
}
ステップ
- Lambdaを作成する(1)
- Lambda関数からBedrockのamazon.nova-2-multimodal-embeddings-v1:0モデルを呼び出す
- Lambdaにポリシーをアタッチする(1と同時並行)
- Lambda(Role) Attach > AI触れる権限(bedrock:InvokeModel), CloudWatchの権限
- API GatewayでAPIを作成する
- メソッドを作り、リクエストを投げれるようにする
早速やる
Lambda関数の作成
関数名: test-bedrock-AI
ランタイム: Node.js 22.x
アーキテクチャ: x86_64
(まだ作成ボタンは押さない)
IAMポリシーの作成
{
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": [
"arn:aws:bedrock:*::foundation-model/*",
"arn:aws:bedrock:*:*:inference-profile/*"
]
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
上記の内容でポリシーを作成する
ポリシー名: test-bedrock-AI-policy

このような許可で進みます
IAMロール作成とLambdaに付与する

基本設定

ポリシーをアタッチ
ロール名: test-bedrock-AI-role
ロールを作成

ロールを付与して関数を作成する
(ロールが検索でヒットしなかった場合リフレッシュする必要がある)
Lambda内Coding!! +a
import { BedrockRuntimeClient, InvokeModelCommand, ConversationRole, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
const client = new BedrockRuntimeClient({ region: "us-east-1" })
const modelId = "amazon.nova-pro-v1:0";
export const handler = async (event) => {
const body = JSON.parse(event.body);
const prompt = body.prompt
const inputText = body.prompt;
const message = {
content: [{ text: inputText }],
role: ConversationRole.USER
};
const request = {
modelId,
messages: [message],
InferenceConfig: {
maxTokens: 500,
temperature: 0.5,
}
}
try {
const response = await client.send(new ConverseCommand(request));
return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(response)
}
} catch (err) {
console.error(`ERROR: Can't invoke '${modelId}'. Reason: ${error.message}`);
return {
statusCode: 500,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(err)
}
}
};
デプロイする
テストを実行

エラーが発生!!

タイムアウトが起こっているので設定を変更する
設定 > 一般設定 > 編集

タイムアウトを30秒にする
そうすると...

できた!!
API Gatewayで公開!

REST APIで構築をする。
新しいAPI
API名: test-bedrock-AI-API
APIエンドポイントタイプ: リージョン
IPアドレスのタイプ: IPv4
"/" にメソッドを作成
メソッド: POST
統合タイプ: Lambda関数
Lambda プロキシ統合: True
Lambda関数: test-bedrock-AI(Lambda関数)のarn
メソッドを作成
APIを"production"というステージ名でデプロイ

コピーをしておく
Ubuntuで動作確認
Ubuntuを開く
curl -X POST \
-H "Content-Type: application/json" \
-d '{"body": "{\"prompt\":\"TypeScriptの特徴を3つ教えて\"}"}' \
https://qgc9rjv61e.execute-api.ap-northeast-1.amazonaws.com/production
上記のようにcurlコマンドを実行する
実行
{"message": "Internal server error"}
Internal Server Errorが起きますね。
ポリシーを更新する
"bedrock:ListFoundationModels": "*"
の権限を追加する
コード下部(errとerrorが統一されていなかった)
} catch (err //ここをerrorに直す) {
console.error(`ERROR: Can't invoke '${modelId}'. Reason: ${error.message}`);
return {
statusCode: 500,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(err(//ここをerrorに直す))
}
}
};
Lambda, API Gatewayを再デプロイ
$ curl -X POST \
-H "Content-Type: application/json" \
-d '{"body": "{\"prompt\":\"TypeScriptの特徴を3つ教えて\"}"}' \
https://qgc9rjv61e.execute-api.ap-northeast-1.amazonaws.com/production
{}
何も帰ってこなくなった、、、
現在
LambdaではBedrockから受け取った生のresponseを持っている。
returnする中でJSON.stringifyを使用しているが、複雑すぎて正しく変換できない可能性があるらしい。そのためにAPI Gatewayが期待するシンプルなJSONボディの文字列として認識できずに、{}が返されてしまうらしい。
だから
try {
const response = await client.send(new ConverseCommand(request));
const modelOutputText = response.output?.message?.content?.[0]?.text;
return {
statusCode: 200,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
result: modelOutputText || "応答なし"
})
}
このようにして回答部分を持ってきてレスポンスにする
Lambda, API Gatewayを再デプロイ
Body部分を変更する
API Gatewayに{"prompt":"..."}というJSONを直接送った場合、event.body は'{"prompt":"..."}'という文字列になるらしい。
だから{"body":}は消す必要がある

できたーーー!!!
つまったところまとめ
- AIは即レスは無理 -> タイムアウトを30秒以上に!
- JSON.stringify()への過信によるレスポンスの誤作動
- API Gatewayのリクエストボディ処理への理解
- event.bodyが必ずボディにつく
- 頭の中での定義された定数名と実際の定数名の齟齬(err/error)
まとめ/振り返り
BedrockのAIにリクエスト投げるの結構難しかった。
エラー解決するのは楽しかった。
API Gatewayの理解も深まってよかった。やはり、定数名などは小さいミスだけど重大なエラーを引き起こしかねないので、これから十分注意していきたいと思った。
参照
Invoke Amazon Nova on Amazon Bedrock using Bedrock's Converse API Supported foundation models in Amazon Bedrock
Discussion