はじめに
昨日、Power AppsでGPT-4oを用いた画像から中身を判別するアプリを作成しました。
驚きの手軽さで作れることができます。
今回は作成のもととなった記事に挑戦し、食材の写真から料理を提案するアプリをサクっと作っていきましょう!
流れ
昨日作成したPower Automate フロー
のプロンプト
を変えれば、ほとんどできてしまいます。
プロンプト
を変えます。
{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "あなたはプロの栄養士です。提供された画像から食材を分析し、作成できる料理を推定してください。"
},
{
"role": "user",
"content": [
{
"type": "image_url",
"image_url": {
"url": "@{triggerBody()?['text']}",
"detail": "high"
}
},
{
"type": "text",
"text": "画像にある食材から料理を提案してください。料理の名前、レシピ、カロリー、食材を推定し回答してください。調理にかかる時間も教えてください。回答は日本語でしてください。## JSON Schema {\"type\": \"object\",\"properties\": {\"cooking\": {\"type\":\"string\", \"description\": \"提案される料理名\"}},{ \"recipe\": {\"type\": \"string\", \"description\": \"料理のレシピ\"}},{\"calorie\": {\"type\": \"string\", \"description\": \"カロリー\"}},{\"ingredient\": {\"type\": \"string\", \"description\": \"食材\"}},{\"hour\": {\"type\": \"string\", \"description\": \"調理にかかる時間\"}},\"required\": [\"cooking\",\"recipe\",\"calorie\",\"ingredient\",\"hour\"],}"
}
]
}
],
"temperature": 1,
"response_format": {
"type": "json_object"
}
}
上記の中のコチラ↓
## JSON Schema {\"type\": \"object\",\"properties\": {\"cooking\": {\"type\":\"string\", \"description\": \"提案される料理名\"}},{ \"recipe\": {\"type\": \"string\", \"description\": \"料理のレシピ\"}},{\"calorie\": {\"type\": \"string\", \"description\": \"カロリー\"}},{\"ingredient\": {\"type\": \"string\", \"description\": \"食材\"}},{\"hour\": {\"type\": \"string\", \"description\": \"調理にかかる時間\"}},\"required\": [\"cooking\",\"recipe\",\"calorie\",\"ingredient\",\"hour\"],}"
の箇所です。
properties
に戻り値を追加します。
キー | データ型 | 内容 |
---|---|---|
cooking | string | 提案される料理名 |
recipe | string | 料理のレシピ |
calorie | string | カロリー |
ingredient | string | 食材 |
hour | string | 調理にかかる時間 |
JSON
のなかでJSONスキーマ
を定義するため、ダブルクオーテーション"
のまえに、 エスケープ文字\
が入ります。
ひじょーに書きづらいですね…。
こういうのもGPT-4oにお願いしちゃいましょう!
すげー!
GPT-4o
に渡すJSON
の定義は、作成アクション
で実施します。
作成アクションで記載する中でも、JSONの形式が異常であればエラーを表示するため、誤りを見つけやすいです。
中身の文字列が変わっただけで、やっていることは一緒ですね。
-
PowerApps (V2) トリガー
で、画像のbase64文字列
を、Power Apps
から受け取る -
作成アクション
で、GPT-4o
に渡す値を定義 -
Azure Key Vault
からAPIキー
を取得 -
GPT-4o
にHTTP要求
を送信 - 結果をPower Appsに返す
さらっと上手くいってしまう・・・。
Power Appsのデザインに時間を投入しましょう。
前回と戻り値が異なるため、JSON の解析
アクションでスキーマを再定義する必要があります。
今回の例では下記の通りとなります。
{
"type": "object",
"properties": {
"cooking": {
"type": "string"
},
"recipe": {
"type": "string"
},
"calorie": {
"type": "string"
},
"ingredient": {
"type": "string"
},
"Hour": {
"type": "string"
}
}
}
Power Appsに戻す値も、設定しましょう。
{
"type": "Response",
"kind": "PowerApp",
"inputs": {
"schema": {
"type": "object",
"properties": {
"cooking": {
"title": "cooking",
"type": "string",
"x-ms-content-hint": "TEXT",
"x-ms-dynamically-added": true
},
"recipe": {
"title": "recipe",
"type": "string",
"x-ms-content-hint": "TEXT",
"x-ms-dynamically-added": true
},
"calorie": {
"title": "calorie",
"type": "string",
"x-ms-content-hint": "TEXT",
"x-ms-dynamically-added": true
},
"ingredient": {
"title": "ingredient",
"type": "string",
"x-ms-content-hint": "TEXT",
"x-ms-dynamically-added": true
},
"hour": {
"title": "hour",
"type": "string",
"x-ms-content-hint": "TEXT",
"x-ms-dynamically-added": true
}
},
"additionalProperties": {}
},
"statusCode": 200,
"body": {
"cooking": "@body('JSON_の解析')?['cooking']",
"recipe": "@body('JSON_の解析')?['recipe']",
"calorie": "@body('JSON_の解析')?['calorie']",
"ingredient": "@body('JSON_の解析')?['ingredient']",
"hour": "@body('JSON_の解析')?['Hour']"
}
},
"runAfter": {
"JSON_の解析": [
"Succeeded"
]
}
}
Power Apps
完成系はTwitterで紹介させていただきました。
デザイン整えられた!
— 出戻りガツオ🐟 Microsoft MVP (@DemodoriGatsuo) May 18, 2024
GPT-4oを使って、食材の写真から、料理名・カロリー・食材・かかる時間を提案してもらうアプリ!
低コストでこれが実現できるのは震えます!!#PowerApps #OpenAI #ChatGPT #GPT4o pic.twitter.com/aVgy0qAr7t
レイアウトの着想は、Power Apps loverの師匠Reza Dorrani
氏のYouTubeで紹介されたアプリをオマージュしています。
コントロールのツリー図
Main
├─ grpComplete // 完了画面
│ ├─ btnComplete // ボタンで完了画面を閉じる
│ ├─ txtComplete
│ └─ backgroundComplete
├─ grpProgress // ロード画面
│ ├─ txtProgress
│ ├─ imgProgress
│ ├─ Progress
│ └─ backgroundProgress
└─ ScreenContainer
├─ HeaderContainer
│ ├─ imgTop
│ ├─ lblTitle
│ ├─ Avatar
│ └─ lblMyname
├─ BottomContainer
│ └─ SidebarContainer
│ └─ galMode // 写真のアップロードかカメラか選択
│ │ ├─ shpSelected
│ │ ├─ icoMenu
│ │ ├─ lblMode
│ │ └─ shpSelectedBackground
│ ├─ conBadge
│ │ └─ Badge
│ ├─ InfoContainer
│ │ ├─ InfoButton
│ │ └─ lblInfo
│ ├─ conPicture // galModeに応じて表示、非表示
│ │ ├─ ImageContainer
│ │ │ ├─ AddPicture
│ │ │ └─ Image
│ │ └─ ButtonPicture
│ └─ conCamera // galModeに応じて表示、非表示
│ ├─ CameraContainer
│ └─ ButtonCamera
└─ MainContainer
├─ conHeaderMain
│ ├─ LogoImage
│ ├─ lblHeaderMain
│ ├─ lblCooking // 料理名を表示するテキストラベル
│ └─ barHeaderMain
├─ conMainRecipe
│ └─ txtRecipe // レシピを表示するテキスト入力
├─ conCalorie
│ ├─ imgCalorie
│ ├─ keyCalorie
│ └─ txtCalorie // カロリーを表示するテキストラベル
├─ conIngredient
│ ├─ imgIngredient
│ ├─ keyIngredient
│ └─ txtIngredient // 食材を表示するテキストラベル
└─ conHour
├─ imgHour
├─ keyHour
└─ txtHour // 時間を表示するテキストラベル
モダンコントロールでSharePoint
を選択。
これをもとに、コントロールのカラーに統一感を持たせます。
色の濃淡はApp.Theme.Colors
で表現しましょう
モダン コントロールのほとんどが、このテーマに沿って色の表現するため、非常に便利です。
今回はヘッダーを自作します。アバター
が追加されましたね。
アイコン
は下記のサイトから使わせていただいています。
ドーナツ🍩のアイコンは、PowerPoint
にてストック画像の色を変えて、挿入しています。
画像の追加
とカメラ
からも画像認識をすることを見据えてコントロールを用意します。
メニューの切り替えは、ギャラリーで実施します。
写真を直接追加する方法
とカメラから写真を送る方法
で、コンテナー単位でコントロールを分けます。
ギャラリー
のSelectedの値
とVisible
を連動させ、切り替えを実現します。
(galMode.Selected.Mode="Attachment")
(galMode.Selected.Mode="Camera")
GPT-4o
から
料理名
レシピ
カロリー
食材
かかる時間
合計5つの値を受け取るため、受け皿を加えます。
Power Automateの戻り値を受け取り、それぞれのプロパティで値を設定。
└─ MainContainer
├─ conHeaderMain
│ ├─ LogoImage
│ ├─ lblHeaderMain
│ ├─ lblCooking // 料理名を表示するテキストラベル
│ └─ barHeaderMain
├─ conMainRecipe
│ └─ txtRecipe // レシピを表示するテキスト入力
├─ conCalorie
│ ├─ imgCalorie
│ ├─ keyCalorie
│ └─ txtCalorie // カロリーを表示するテキストラベル
├─ conIngredient
│ ├─ imgIngredient
│ ├─ keyIngredient
│ └─ txtIngredient // 食材を表示するテキストラベル
└─ conHour
├─ imgHour
├─ keyHour
└─ txtHour // 時間を表示するテキストラベル
あとはPower Automate実行中の待機画面です。
GPT-4oの強みで全然時間がかからないのですが、昨日と全く一緒では芸がないので、ロード画面
と完了画面
を入れます。
-
ロード画面
四角形に、テキストラベルとプログレスバーを入れる -
完了画面
四角形に、テキストラベルとボタンを入れる
Main
├─ grpComplete // 完了画面
│ ├─ btnComplete // ボタンで完了画面を閉じる
│ ├─ txtComplete
│ └─ backgroundComplete
├─ grpProgress // ロード画面
│ ├─ txtProgress
│ ├─ imgProgress
│ ├─ Progress
│ └─ backgroundProgress
それぞれ作成します。
画面の真ん中にコントロールが配置されるようにするには、X
、Y
のプロパティを下記のように設定します。
(Parent.Width - Self.Width) / 2
(Parent.Height - Self.Height) / 2
ギャラリーの中のコントロールの場合は、
- (Parent.TemplateWidth - Self.Width) / 2
- (Parent.TemplateHeight - Self.Height) / 2
上記で定義できます。
しょっちゅう使いまわすので、覚えておいて損はないです。
これで、GPT-4o
を呼び出すボタンコントロールに下記の式を設定すれば
/*
* ロード画面を表示する
*/
Set(varVisible1,true);
/*
* Power AutomateでGPT-4oを呼び出す
*/
UpdateContext({Response:'GPT-4o-Image-observe'.Run(Substitute(JSON(Image.Image,JSONFormat.IncludeBinaryData),"""",""))});
/*
* 完了画面を表示する
*/
Set(varVisible2,true);
ロード画面のグループ、完了画面のグループが順番に表示され、映えます!
Power Automateの昨日と異なるポイントが、ほとんどないためPower Appsの説明に重きを置きました。
ChatGPT用のカスタムコネクタを作ると、Power Automateを経由せずにできるので、さらに便利ですね。
カスタムコネクタの作り方は、Microsoftの吉野様のQiitaの記事が大変わかりやすいです。
おわりに
強力な機能が、こんなに簡単に使えて本当にいいのかと震えます。
今度は何を作ろう・・・。
ワクワクが止まりませんね!
皆様、良いPower lifeを!