Share
Embed
AWS AmplifyとBedrockを活用した生成AIアプリの開発@第6回 FlutterGakkai
Tomoki Hirai
December 19, 2024
Transcript
26 slides
-
自己紹介 平井友樹 (Tomoki Hirai) @AllJokin KDDIアジャイル開発センター株式会社 / KDDI株式会社 エンジニアとしてアジャイルの内製開発支援を担当 React,
Flutter, AWS, GCPなど KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 1 -
google_generative_ai Googleの生成AIであるGeminiをFlutterから簡単に呼び出すためのライブラリ。 Using the Google AI SDK for Dart (Flutter)
to call the Google AI Gemini API directly from your app is recommended for prototyping only. → 商用では非推奨なので使えない…。API Keyを管理するためのバックエンドを構築がめんどくさい…。 import 'package:google_generative_ai/google_generative_ai.dart'; const apiKey = ...; void main() async { final model = GenerativeModel( model: 'gemini-1.5-flash-latest', apiKey: apiKey, ); final prompt = 'Write a story about a magic backpack.'; final content = [Content.text(prompt)]; final response = await model.generateContent(content); }; KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 4 -
AWS Amplify (Gen2) フルスタックのWebアプリケーションやモバイルアプリケーションを素早く構築、デプロイするためのツール Typescriptによるバックエンド定義 開発者ごとのクラウドサンドボックス環境 Gitブランチと連携した自動デプロイ AWS CDKによるバックエンドのカスタマイズ KDDI
Agile Development Center Corporation KDDIアジャイル開発センター株式会社 6 -
Typescriptによるバックエンド定義 Typescriptによるリソース定義によってGraphQLスキーマとDynamoDBのテーブルを自動生成 GraphQLスキーマ定義 DynamoDBテーブル id content 1 test 2 test
import { type ClientSchema, a, defineData } from '@aws-amplify/backend'; const schema = a.schema({ Todo: a.model({ content: a.string(), }).authorization(allow => [allow.owner()]), }); export type Schema = ClientSchema<typeof schema>; export const data = defineData({ schema, authorizationModes: { defaultAuthorizationMode: 'userPool', }, }); type Todo { id: ID! content: String! } KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 7 -
作成したサンプルアプリケーション 料理の写真をFlutterアプリで撮影し、生成AIで料理名とカロリーを推定する https://github.com/tm-hirai/flutter_amplify_bedrock https://qiita.com/allJokin/items/dd1a267bdce88a79d0cc AWS Cloud User { "food": カレー,
"calorie": 800 } AWS Amplify Amazon Bedrock AWS AppSync AWS Lambda KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 10 -
生成AIを呼び出すバックエンドの作成 フロントエンドから送信された料理画像を生成AIで処理し、カロリーを推定するバックエンドを作成 Amplifyのファイル構成に従い、以下のファイルを作成する 使用するサービスは以下の3つ。 AppSync: サーバーレスでマネージドなGraphQLサーバー Lambda: Node.js、Python、Rubyなどをサーバーレスに実行できる Faas (Function
as a Service) Bedrock: 生成AIサービス amplify/ data/ calorieCalculation.ts Lambda関数の定義 resource.ts スキーマの定義 backend.ts バックエンドの定義 KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 12 -
Lambda関数の実装 Lambda関数で実行されるTypescriptのコード。Base64形式の画像をBedrockに送信する。 export const handler: Schema["calorieCalculation"]["functionHandler"] = async (event,_ )
=> { const client = new BedrockRuntimeClient({ region: "us-east-1" }); // base64形式の画像 const base64String = event.arguments.base64String; const input = { modelId: process.env.MODEL_ID, contentType: "application/json", accept: "application/json", body: prompt, // 次スライドに記載 } as InvokeModelCommandInput; const command = new InvokeModelCommand(input); const response = await client.send(command); const data = JSON.parse(Buffer.from(response.body) .toString()); return data.content[0].text; }; KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 13 -
Bedrockに送信するプロンプト 日本語より英語の方がトークン数(課金単位)が少なくて低コストなので、英語で作成 システムプロンプト 入力画像 命令文 system: "You are a calorie
analysis expert capable of estimating the calories of any dish shown in an image." { type: "image", source: {type: "base64", media_type: "image/jpeg", data: base64String} } { type: "text", text: `Estimate calories of the food in this image. Use JSON format with "food" (dish name in Japanese) and "calorie" (in kcal) as keys. Do not output anything other than JSON. <example> { "food": "寿司", "calorie": 300 } </example>` } KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 14 -
スキーマ定義 Lambda関数の設定とGraphQLスキーマ定義する。フロントから呼び出す際の引数や返り値、認証方式などを定義する。 export const MODEL_ID = "anthropic.claude-3-sonnet-20240229-v1:0"; // Lambdaの定義 export
const calorieCalculationFunction = defineFunction({ entry: "./calorieCalculation.ts", environment: { MODEL_ID, }, runtime: 20, timeoutSeconds: 10, }); const schema = a.schema({ calorieCalculation: a .query().arguments({ base64String: a.string().required() }).returns(a.string()) .authorization((allow) => [allow.publicApiKey()]) .handler(a.handler.function(calorieCalculationFunction)), }); export type Schema = ClientSchema<typeof schema>; export const data = defineData({ schema, authorizationModes: { defaultAuthorizationMode: "apiKey", apiKeyAuthorizationMode: { expiresInDays: 30 } }, }); KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 15 -
バックエンド定義 defineBackend によってバックエンドが生成される。 import { defineBackend } from "@aws-amplify/backend"; import
{ data, MODEL_ID, calorieCalculationFunction } from "./data/resource"; import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam"; export const backend = defineBackend({ data, calorieCalculationFunction, }); // LambdaからBedrockを呼び出すための権限を付与 backend.calorieCalculationFunction.resources.lambda.addToRolePolicy( new PolicyStatement({ effect: Effect.ALLOW, actions: ["bedrock:InvokeModel"], resources: [`arn:aws:bedrock:us-east-1::foundation-model/${MODEL_ID}`], }) ); KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 16 -
デプロイ 以下のコマンドでサンドボックス環境にデプロイ可能。GraphQLスキーマとフロントの設定ファイルが生成される。 アプリ側で読み込むdartファイル GraphQLのスキーマ npx ampx sandbox --outputs-format dart --outputs-out-dir
lib --outputs-version 0 const amplifyConfig = '''{ "UserAgent": "@aws-amplify/client-config/1.0.4", "Version": "1.0", "api": { "plugins": { "awsAPIPlugin": { "data": { "endpointType": "GraphQL", "endpoint": "https://xxx.appsync-api. ap-northeast-1.amazonaws.com/graphql", "region": "ap-northeast-1", "authorizationType": "API_KEY", "apiKey": "xxx" } }}}}'''; type Query { calorieCalculation(base64String: String!): String @aws_iam @aws_api_key } KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 17 -
アプリの概要 1. カメラで写真を撮影 2. 撮影した画像を加工 3. 画像をアップロード KDDI Agile Development
Center Corporation KDDIアジャイル開発センター株式会社 19 -
1.カメラで写真を撮影 camera パッケージを使ってカメラ機能を実装 void main() async { WidgetsFlutterBinding.ensureInitialized(); // 利用可能なカメラの取得
final cameras = await availableCameras(); runApp(MyApp( cameras: cameras, )); } // コントローラー初期化 void initState() { super.initState(); controller = CameraController(widget.cameras[0], ResolutionPreset.max, enableAudio: false); controller.initialize().then((_) { if (!mounted) { return; } setState(() {}); }); } Widget build(BuildContext context) { // プレビューウィジット return CameraPreview(controller); } // 撮影 controller.takePicture().then((XFile file) async {}); KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 20 -
2.撮影した画像を加工 1. 端末によって縦横比が異なるので、正方形に切り取る 2. 解像度が高いとコストが大きいので、500x500にリサイズする ①画像ファイルを 正⽅形に整形 ②500x500に リサイズ 元画像
正⽅形画像 500x500 tokens = (width px * height px)/750 画像サイズ トークン数 コスト/1枚 コスト/1000枚 200x200 約54 約$0.00016 約$0.16 500x500 約333 約$0.001 約$1 1000x1000 約1334 約$0.004 約$4.00 KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 21 -
2.撮影した画像を加工 image パッケージを使って500x500の画像を作成する copyCrop : 画像を切り取る copyResize : 画像のサイズを変更する final
cmd = img.Command() ..image(image) ..copyCrop(x: 0, y: 0, width: image.width, height: image.width) ..copyResize(width: 500, height: 500); final processedImage = await cmd.getImageThread(); KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 22 -
3.画像をアップロードして推定結果を受け取る base64形式の画像を引数に設定してGraphQLのリクエストを実行する const graphQLDocument = ''' query calorieCalculation(\$base64String: String!) {
calorieCalculation(base64String: \$base64String) } '''; // imageのオブジェクトをjpg形式でbase64エンコード final base64String = base64Encode(img.encodeJpg(_image!)); final request = GraphQLRequest<String>( document: graphQLDocument, variables: <String, String>{ "base64String": base64String }, authorizationMode: APIAuthorizationType.apiKey); /// API実行 final response = await Amplify.API.query(request: request).response; Map<String, dynamic> jsonMap = json.decode(response.data!); KDDI Agile Development Center Corporation KDDIアジャイル開発センター株式会社 23 -
まとめ AWS Amplify x Amazon Bedrockで 簡単に生成AIアプリケーションを作成可能 ぜひ試してみてください! KDDI Agile
Development Center Corporation KDDIアジャイル開発センター株式会社 25