Amplify Gen 2 & Bedrockによるレシピ生成AIアプリ開発スレ

AWS公式のレシピ生成AIアプリ開発ハンズオンを日本語化し、ap-northeast-1で動くようにカスタマイズしてみる

一からReact + Viteプロジェクトを作ってローカルで動かすための準備
# プロジェクト名の設定
PROJECT_NAME=ai-recipe-generator
# React + Viteプロジェクトの作成
npm create vite@latest ${PROJECT_NAME} -- --template react-ts -y
cd ${PROJECT_NAME}
npm install

AWS CLIの認証後、サンドボックス環境を作成
# サンドボックス環境を作成
npx ampx sandbox
# "File written: amplify_outputs.json"が出力されるまで待つ
別ターミナルを開き、ローカルでReactが動く事を確認
# ローカル環境が動作する事を確認
npm run dev
ブラウザでhttps://localhost:5173にアクセスする

GitHubでリポジトリを作成後、以下のコマンドを実施しデプロイの準備
# Amplifyモジュールの導入
npm create amplify@latest -y
# ユーザー名の設定
USER_NAME=tsukuboshi
# GitHubへの接続
git init
git add .
git commit -m "first commit"
git remote add origin https://github.com/${USER_NAME}/${PROJECT_NAME}.git
git branch -M main
git push -u origin main
上記のコマンドを実施後、Amplifyコンソールでデプロイ

リポジトリはこちら

リージョンの違いに苦しんでたけどなんとか出せたぞ!
ちなみにモデルはhaikuを使用

日本語化も完了

今回のコードだとAWS AppSync JavaScript リゾルバーがBedrockを直接呼び出してるから、ハンズオンで示されている構成図中のLambdaはないんじゃないか?
Lambdaコンソール見てもそれらしき関数なかったし、ないのが正解な気がする

このハンズオン、この記事と作るアプリケーションはほぼ一緒だな
ただ一から作る形になっているので、前より何やっているかは分かりやすいか

Amplify environment variablesを用いて、モデルのリージョンおよびIDを環境変数で変更できるようにしてみる

Amplify environment variablesの設定は以下を参照

sandboxで使用する場合はローカルでの定義が必要なため、以下の環境変数ファイルを新規作成し配置する。
なおReact.jsで環境変数を使用する場合は、REACT_APP_で始まる変数名にしなければならない。
REACT_APP_MODEL_ID=anthropic.claude-3-haiku-20240307-v1:0
REACT_APP_MODEL_REGION=ap-northeast-1

環境変数読み込みの際にdotenvxモジュールが必要なので追加でインストール
npm install @dotenvx/dotenvx --save

以下のdotenvxコマンドで定義されている変数の中身を確認可能
npx dotenvx run -f .env.local -- node -e "console.log(process.env.REACT_APP_MODEL_ID)"
[dotenvx@1.14.1] injecting env (2) from .env.local
anthropic.claude-3-haiku-20240307-v1:0

変数読み込みファイルを新規作成し、他ファイルから参照できるようにする
export const model_id = process.env.REACT_APP_MODEL_ID as string;
export const model_region = process.env.REACT_APP_MODEL_REGION as string;

今回はバックエンドのGraqhQLにも環境変数を設定し渡す必要があるため、以下で既にデプロイされているAmplifyリソースのプロパティを修正

以下の通りモデルIDおよびリージョンを変数に変更すると共に、AppSyncで使用する環境変数としてモデルIDを追加
import { defineBackend } from "@aws-amplify/backend";
import { data } from "./data/resource";
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
import { auth } from "./auth/resource";
+ import { model_id, model_region } from "./env";
const backend = defineBackend({
auth,
data,
});
const bedrockDataSource = backend.data.resources.graphqlApi.addHttpDataSource(
"bedrockDS",
- "https://bedrock-runtime.us-east-1.amazonaws.com",
+ `https://bedrock-runtime.${model_region}.amazonaws.com`,
{
authorizationConfig: {
- signingRegion: "us-east-1",
+ signingRegion: model_region,
signingServiceName: "bedrock",
},
}
);
bedrockDataSource.grantPrincipal.addToPrincipalPolicy(
new PolicyStatement({
resources: [
- "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-sonnet-20240229-v1:0",
+ `arn:aws:bedrock:${model_region}::foundation-model/${model_id}`,
],
actions: ["bedrock:InvokeModel"],
})
);
+ // Add environment variables to the GraphQL API
+ backend.data.resources.cfnResources.cfnGraphqlApi.environmentVariables = {
+ MODEL_ID: model_id,
+ };

カスタムリゾルバー内の関数でモデルIDを環境変数から読み込むように変更
export function request(ctx) {
const { ingredients = [] } = ctx.args;
// Construct the prompt with the provided ingredients
const prompt = `Suggest a recipe idea using these ingredients: ${ingredients.join(", ")}.`;
+ // Extract the model ID from the environment variables
+ const model_id = ctx.env.MODEL_ID;
// Return the request configuration
return {
- resourcePath: `/model/anthropic.claude-3-sonnet-20240229-v1:0/invoke`,
+ resourcePath: `/model/${model_id}/invoke`,
method: "POST",
params: {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1000,
messages: [
{
role: "user",
content: [
{
type: "text",
text: `\n\nHuman: ${prompt}\n\nAssistant:`,
},
],
},
],
}),
},
};
}
export function response(ctx) {
// Parse the response body
const parsedBody = JSON.parse(ctx.result.body);
// Extract the text content from the response
const res = {
body: parsedBody.content[0].text,
};
// Return the response
return res;
}

hostingされているアプリのデプロイ前に、コンソール上でAmplify environment variablesにREACT_APP_MODEL_ID
とREACT_APP_MODEL_REGION
の2つの変数を作成

さらにコンソール上でビルドの設定を以下の通り更新し、.env
ファイルに設定した環境変数を追記する
version: 1
backend:
phases:
build:
commands:
+ - echo "REACT_APP_MODEL_ID=$REACT_APP_MODEL_ID" >> .env
+ - echo "REACT_APP_MODEL_REGION=$REACT_APP_MODEL_REGION" >> .env
- npm ci --cache .npm --prefer-offline
- npx ampx pipeline-deploy --branch $AWS_BRANCH --app-id $AWS_APP_ID
frontend:
phases:
build:
commands:
- npm run build
artifacts:
baseDirectory: dist
files:
- '**/*'
cache:
paths:
- .npm/**/*

やってみて分かったけど、カスタムリゾルバーにすると環境変数の他にBedrockのHTTPエンドポイントにもリージョン値が含まれるので、インフラ更新が必ず伴うから気軽に変更できず相性悪そう
Converse APIを使って最小限の変更でリージョンやIDを切り替えたいなら、環境変数の値を変更するだけで済むLambdaリゾルバーを使う方が良さげ