Closed21

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

つくぼしつくぼし

一から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を環境変数で変更できるようにしてみる

つくぼしつくぼし

sandboxで使用する場合はローカルでの定義が必要なため、以下の環境変数ファイルを新規作成し配置する。
なおReact.jsで環境変数を使用する場合は、REACT_APP_で始まる変数名にしなければならない。

.env.local
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
つくぼしつくぼし

変数読み込みファイルを新規作成し、他ファイルから参照できるようにする

amplify/env.ts
export const model_id = process.env.REACT_APP_MODEL_ID as string;
export const model_region = process.env.REACT_APP_MODEL_REGION as string;
つくぼしつくぼし

以下の通りモデルIDおよびリージョンを変数に変更すると共に、AppSyncで使用する環境変数としてモデルIDを追加

amplify/backend.ts
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を環境変数から読み込むように変更

amplify/data/bedrock.js
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_IDREACT_APP_MODEL_REGIONの2つの変数を作成

つくぼしつくぼし

さらにコンソール上でビルドの設定を以下の通り更新し、.envファイルに設定した環境変数を追記する

amplify.yml
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リゾルバーを使う方が良さげ

このスクラップは2024/09/22にクローズされました